From 85efc33183d9a4b4d5fbfa4a2af6b344a115971f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 27 Oct 2022 11:24:05 -0400 Subject: [PATCH 01/66] finished gui --- cybersyn/scripts/global.lua | 2 + cybersyn/scripts/gui.lua | 91 +++++++++++++++++++++++++++++-------- cybersyn/scripts/layout.lua | 4 +- cybersyn/scripts/main.lua | 36 ++++++++------- 4 files changed, 94 insertions(+), 39 deletions(-) diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 449030a..569b43b 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -2,6 +2,7 @@ ---@class MapData ---@field public total_ticks uint ---@field public layout_top_id uint +---@field public to_comb {[uint]: LuaEntity} ---@field public to_output {[uint]: LuaEntity} ---@field public to_stop {[uint]: LuaEntity} ---@field public stations {[uint]: Station} @@ -56,6 +57,7 @@ mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value global.total_ticks = 0 +global.to_comb = {} global.to_output = {} global.to_stop = {} global.stations = {} diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 9c3c493..42aff8c 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -1,5 +1,6 @@ --By Mami -local gui = require("__flib__.gui") +local flib_gui = require("__flib__.gui") +local flib_event = require("__flib__.event") local RED = "utility/status_not_working" local GREEN = "utility/status_working" @@ -25,11 +26,24 @@ STATUS_NAMES[defines.entity_status.disabled_by_script] = "entity-status.disabled STATUS_NAMES[defines.entity_status.marked_for_deconstruction] = "entity-status.marked-for-deconstruction" STATUS_NAMES_DEFAULT = "entity-status.disabled" ----@param entity LuaEntity -function gui_opened(entity, player) +---@param comb LuaEntity +---@param player LuaPlayer +function gui_opened(comb, player) local rootgui = player.gui.screen - local window = gui.build(rootgui, { - {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, tags={unit_number=entity.unit_number}, actions={ + local selected_index = 0 + local control = comb.get_or_create_control_behavior().parameters + if control.operation == OPERATION_PRIMARY_IO then + selected_index = 1 + elseif control.operation == OPERATION_SECONDARY_IO then + selected_index = 2 + elseif control.operation == OPERATION_DEPOT then + selected_index = 3 + elseif control.operation == OPERATION_WAGON_MANIFEST then + selected_index = 4 + end + + local window = flib_gui.build(rootgui, { + {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, actions={ on_close = {"test"} }, children={ --title bar @@ -37,15 +51,15 @@ function gui_opened(entity, player) {type="label", style="frame_title", caption={"cybersyn-gui.combinator-title"}, elem_mods={ignored_by_interaction=true}}, {type="empty-widget", style="flib_titlebar_drag_handle", elem_mods={ignored_by_interaction=true}}, {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}, sprite="utility/close_white", hovered_sprite="utility/close_black", name=COMBINATOR_NAME, actions={ - on_click = {"test"} + on_click = {"close", comb.unit_number} }} }}, {type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=8}, children={ {type="flow", direction="vertical", style_mods={horizontal_align="left"}, children={ --status {type="flow", style = "status_flow", direction = "horizontal", style_mods={vertical_align="center", horizontally_stretchable=true}, children={ - {type="sprite", sprite=STATUS_SPRITES[entity.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}}, - {type="label", caption={STATUS_NAMES[entity.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}} + {type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}}, + {type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}} }}, --preview {type="frame", style="deep_frame_in_shallow_frame", style_mods={minimal_width=0, horizontally_stretchable=true, padding=0}, children={ @@ -53,8 +67,8 @@ function gui_opened(entity, player) }}, {type="label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=8}}, {type="drop-down", ref={"operation"}, actions={ - on_selection_state_changed = {"test"} - }, items={ + on_selection_state_changed = {"drop-down", comb.unit_number} + }, selected_index=selected_index, items={ {"cybersyn-gui.comb1"}, {"cybersyn-gui.comb2"}, {"cybersyn-gui.depot"}, @@ -65,34 +79,71 @@ function gui_opened(entity, player) }} }) - window.preview.entity = entity + window.preview.entity = comb window.titlebar.drag_target = window.main_window window.main_window.force_auto_center() player.opened = window.main_window end -local function o(event) +local function on_gui_opened(event) local entity = event.entity - if not entity or not entity.valid then return end + if not entity or not entity.valid or entity.name ~= COMBINATOR_NAME then return end + local player = game.get_player(event.player_index) + if not player then return end + + gui_opened(entity, player) +end + +local function on_gui_closed(event) + if not event.element or event.element.name ~= COMBINATOR_NAME then return end local player = game.get_player(event.player_index) if not player then return end local rootgui = player.gui.screen if rootgui[COMBINATOR_NAME] then - --rootgui[COMBINATOR_NAME].destroy() - else - gui_opened(entity, player) + rootgui[COMBINATOR_NAME].destroy() + --TODO: play close sound to player end end function register_gui_actions() - gui.hook_events(function(event) - local msg = gui.read_action(event) + flib_gui.hook_events(function(event) + local msg = flib_gui.read_action(event) if msg then + local player = game.get_player(event.player_index) + if not player then return end + local rootgui = player.gui.screen -- read the action to determine what to do - local hi = 2 + if msg[1] == "close" then + if rootgui[COMBINATOR_NAME] then + rootgui[COMBINATOR_NAME].destroy() + --TODO: play close sound to player + end + elseif msg[1] == "drop-down" then + local element = event.element + if not element then return end + local comb = global.to_comb[msg[2]] + if not comb or not comb.valid then return end + + local a = comb.get_or_create_control_behavior() + local control = a.parameters + if element.selected_index == 1 then + control.operation = OPERATION_PRIMARY_IO + elseif element.selected_index == 2 then + control.operation = OPERATION_SECONDARY_IO + elseif element.selected_index == 3 then + control.operation = OPERATION_DEPOT + elseif element.selected_index == 4 then + control.operation = OPERATION_WAGON_MANIFEST + else + return + end + a.parameters = control + on_combinator_updated(global, comb) + end end end) - script.on_event(defines.events.on_gui_opened, o) + flib_event.register(defines.events.on_gui_opened, on_gui_opened) + flib_event.register(defines.events.on_gui_closed, on_gui_closed) end diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 5dafdfa..6831692 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -107,7 +107,7 @@ local function reset_station_layout(map_data, station, forbidden_entity) direction_filter = {defines.direction.east, defines.direction.west} is_ver = true elseif station_direction == defines.direction.east then - search_area = {left_top = {y = middle_y - reach, x = middle_x}, right_bottom = {y = middle_y + reach, x = middle_x - 6}} + search_area = {left_top = {y = middle_y - reach, x = middle_x - 6}, right_bottom = {y = middle_y + reach, x = middle_x}} area_delta = {x = -7, y = 0} direction_filter = {defines.direction.north, defines.direction.south} is_ver = false @@ -117,7 +117,7 @@ local function reset_station_layout(map_data, station, forbidden_entity) direction_filter = {defines.direction.east, defines.direction.west} is_ver = true elseif station_direction == defines.direction.west then - search_area = {left_top = {y = middle_y - reach, x = middle_x + 6}, right_bottom = {y = middle_y + reach, x = middle_x}} + search_area = {left_top = {y = middle_y - reach, x = middle_x}, right_bottom = {y = middle_y + reach, x = middle_x + 6}} area_delta = {x = 7, y = 0} direction_filter = {defines.direction.north, defines.direction.south} is_ver = false diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 2d69b9b..4baf7f6 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -1,4 +1,5 @@ --By Mami +local flib_event = require("__flib__.event") ---@param map_data MapData ---@param train Train @@ -151,6 +152,7 @@ local function on_combinator_built(map_data, comb) wire = defines.wire_type.red, }) + map_data.to_comb[comb.unit_number] = comb map_data.to_output[comb.unit_number] = out map_data.to_stop[comb.unit_number] = stop @@ -229,12 +231,13 @@ local function on_combinator_broken(map_data, comb) if out and out.valid then out.destroy() end + map_data.to_comb[comb.unit_number] = nil map_data.to_output[comb.unit_number] = nil map_data.to_stop[comb.unit_number] = nil end ---@param map_data MapData ---@param comb LuaEntity -local function on_combinator_updated(map_data, comb) +function on_combinator_updated(map_data, comb) --NOTE: this is the lazy way to implement updates and puts strong restrictions on data validity on on_combinator_broken on_combinator_broken(map_data, comb) on_combinator_built(map_data, comb) @@ -588,40 +591,39 @@ local filter_broken = { } local function register_events() --NOTE: I have no idea if this correctly registers all events once in all situations - script.on_event(defines.events.on_built_entity, on_built, filter_built) - script.on_event(defines.events.on_robot_built_entity, on_built, filter_built) - script.on_event({defines.events.script_raised_built, defines.events.script_raised_revive, defines.events.on_entity_cloned}, on_built) + flib_event.register(defines.events.on_built_entity, on_built, filter_built) + flib_event.register(defines.events.on_robot_built_entity, on_built, filter_built) + flib_event.register({defines.events.script_raised_built, defines.events.script_raised_revive, defines.events.on_entity_cloned}, on_built) - script.on_event(defines.events.on_pre_player_mined_item, on_broken, filter_broken) - script.on_event(defines.events.on_robot_pre_mined, on_broken, filter_broken) - script.on_event(defines.events.on_entity_died, on_broken, filter_broken) - script.on_event(defines.events.script_raised_destroy, on_broken) + flib_event.register(defines.events.on_pre_player_mined_item, on_broken, filter_broken) + flib_event.register(defines.events.on_robot_pre_mined, on_broken, filter_broken) + flib_event.register(defines.events.on_entity_died, on_broken, filter_broken) + flib_event.register(defines.events.script_raised_destroy, on_broken) - script.on_event({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed) + flib_event.register({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed) local nth_tick = math.ceil(60/mod_settings.tps); - script.on_nth_tick(nil) - script.on_nth_tick(nth_tick, on_tick) + flib_event.on_nth_tick(nth_tick, on_tick) - script.on_event(defines.events.on_train_created, on_train_built) - script.on_event(defines.events.on_train_changed_state, on_train_changed) + flib_event.register(defines.events.on_train_created, on_train_built) + flib_event.register(defines.events.on_train_changed_state, on_train_changed) - script.on_event(defines.events.on_entity_renamed, on_rename) + flib_event.register(defines.events.on_entity_renamed, on_rename) register_gui_actions() end -script.on_load(function() +flib_event.on_load(function() register_events() end) -script.on_init(function() +flib_event.on_init(function() --TODO: we are not checking changed cargo capacities --find_and_add_all_stations(global) register_events() end) -script.on_configuration_changed(function(data) +flib_event.on_configuration_changed(function(data) --TODO: we are not checking changed cargo capacities --find_and_add_all_stations(global) register_events() From 4887e3925d4a1cc79ebdef9b3b7cd356402d760a Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 27 Oct 2022 15:11:11 -0400 Subject: [PATCH 02/66] added penalty for no power --- cybersyn/scripts/controller.lua | 6 +++--- cybersyn/scripts/gui.lua | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cybersyn/scripts/controller.lua b/cybersyn/scripts/controller.lua index 592874c..e277e6d 100644 --- a/cybersyn/scripts/controller.lua +++ b/cybersyn/scripts/controller.lua @@ -64,9 +64,9 @@ end ---@param station Station local function get_signals(station) - if station.entity_comb1.valid then - local signals = station.entity_comb1.get_merged_signals(defines.circuit_connector_id.combinator_input) - return signals + local comb = station.entity_comb1 + if comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then + return comb.get_merged_signals(defines.circuit_connector_id.combinator_input) else return nil end diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 42aff8c..cdea2fa 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -43,9 +43,7 @@ function gui_opened(comb, player) end local window = flib_gui.build(rootgui, { - {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, actions={ - on_close = {"test"} - }, children={ + {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={ --title bar {type="flow", ref={"titlebar"}, children={ {type="label", style="frame_title", caption={"cybersyn-gui.combinator-title"}, elem_mods={ignored_by_interaction=true}}, From d2bb9e252350118101f14fc96a546cb4af27a1d5 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 28 Oct 2022 14:40:49 -0400 Subject: [PATCH 03/66] api update --- .../factorio/runtime-api-LuaAISettings.lua | 2 +- ...time-api-LuaAccumulatorControlBehavior.lua | 2 +- .../runtime-api-LuaAchievementPrototype.lua | 2 +- .../runtime-api-LuaAmmoCategoryPrototype.lua | 2 +- ...LuaArithmeticCombinatorControlBehavior.lua | 2 +- ...ntime-api-LuaAutoplaceControlPrototype.lua | 2 +- .vscode/factorio/runtime-api-LuaBootstrap.lua | 2 +- .vscode/factorio/runtime-api-LuaBurner.lua | 2 +- .../runtime-api-LuaBurnerPrototype.lua | 5 +- .../factorio/runtime-api-LuaChunkIterator.lua | 2 +- .../runtime-api-LuaCircuitNetwork.lua | 2 +- ...ntime-api-LuaCombinatorControlBehavior.lua | 2 +- .../runtime-api-LuaCommandProcessor.lua | 2 +- ...i-LuaConstantCombinatorControlBehavior.lua | 2 +- ...untime-api-LuaContainerControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-LuaControl.lua | 12 +- .../runtime-api-LuaControlBehavior.lua | 2 +- .../runtime-api-LuaCustomChartTag.lua | 2 +- .../runtime-api-LuaCustomInputPrototype.lua | 2 +- .../factorio/runtime-api-LuaCustomTable.lua | 2 +- .../runtime-api-LuaDamagePrototype.lua | 2 +- ...pi-LuaDeciderCombinatorControlBehavior.lua | 2 +- .../runtime-api-LuaDecorativePrototype.lua | 2 +- ...e-api-LuaElectricEnergySourcePrototype.lua | 5 +- .vscode/factorio/runtime-api-LuaEntity.lua | 24 ++-- .../runtime-api-LuaEntityPrototype.lua | 55 +++++++- .vscode/factorio/runtime-api-LuaEquipment.lua | 2 +- ...time-api-LuaEquipmentCategoryPrototype.lua | 2 +- .../factorio/runtime-api-LuaEquipmentGrid.lua | 2 +- .../runtime-api-LuaEquipmentGridPrototype.lua | 2 +- .../runtime-api-LuaEquipmentPrototype.lua | 10 +- .../runtime-api-LuaFlowStatistics.lua | 2 +- .vscode/factorio/runtime-api-LuaFluidBox.lua | 6 +- .../runtime-api-LuaFluidBoxPrototype.lua | 2 +- ...time-api-LuaFluidEnergySourcePrototype.lua | 5 +- .../runtime-api-LuaFluidPrototype.lua | 2 +- .../factorio/runtime-api-LuaFontPrototype.lua | 2 +- .vscode/factorio/runtime-api-LuaForce.lua | 8 +- .../runtime-api-LuaFuelCategoryPrototype.lua | 2 +- .../factorio/runtime-api-LuaGameScript.lua | 22 ++-- ...ime-api-LuaGenericOnOffControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-LuaGroup.lua | 14 +-- .vscode/factorio/runtime-api-LuaGui.lua | 2 +- .../factorio/runtime-api-LuaGuiElement.lua | 52 ++++++-- .../runtime-api-LuaHeatBufferPrototype.lua | 2 +- ...ntime-api-LuaHeatEnergySourcePrototype.lua | 5 +- ...runtime-api-LuaInserterControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-LuaInventory.lua | 2 +- .../factorio/runtime-api-LuaItemPrototype.lua | 4 +- .vscode/factorio/runtime-api-LuaItemStack.lua | 2 +- .../runtime-api-LuaLampControlBehavior.lua | 2 +- .../runtime-api-LuaLazyLoadedValue.lua | 2 +- .../factorio/runtime-api-LuaLogisticCell.lua | 2 +- ...pi-LuaLogisticContainerControlBehavior.lua | 2 +- .../runtime-api-LuaLogisticNetwork.lua | 2 +- .../factorio/runtime-api-LuaLogisticPoint.lua | 2 +- ...time-api-LuaMiningDrillControlBehavior.lua | 2 +- .../runtime-api-LuaModSettingPrototype.lua | 2 +- ...runtime-api-LuaModuleCategoryPrototype.lua | 2 +- .../runtime-api-LuaNamedNoiseExpression.lua | 2 +- .../runtime-api-LuaNoiseLayerPrototype.lua | 2 +- .../runtime-api-LuaParticlePrototype.lua | 2 +- .../runtime-api-LuaPermissionGroup.lua | 2 +- .../runtime-api-LuaPermissionGroups.lua | 2 +- .vscode/factorio/runtime-api-LuaPlayer.lua | 10 +- .vscode/factorio/runtime-api-LuaProfiler.lua | 2 +- ...-LuaProgrammableSpeakerControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-LuaRCON.lua | 2 +- ...-api-LuaRailChainSignalControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-LuaRailPath.lua | 2 +- ...ntime-api-LuaRailSignalControlBehavior.lua | 2 +- .../runtime-api-LuaRandomGenerator.lua | 2 +- .vscode/factorio/runtime-api-LuaRecipe.lua | 2 +- ...runtime-api-LuaRecipeCategoryPrototype.lua | 2 +- .../runtime-api-LuaRecipePrototype.lua | 2 +- .vscode/factorio/runtime-api-LuaRemote.lua | 14 +-- .vscode/factorio/runtime-api-LuaRendering.lua | 2 +- ...ntime-api-LuaResourceCategoryPrototype.lua | 2 +- ...runtime-api-LuaRoboportControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-LuaSettings.lua | 2 +- .../runtime-api-LuaShortcutPrototype.lua | 2 +- ...time-api-LuaStorageTankControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-LuaStyle.lua | 2 +- .vscode/factorio/runtime-api-LuaSurface.lua | 49 +++++--- .../factorio/runtime-api-LuaTechnology.lua | 2 +- .../runtime-api-LuaTechnologyPrototype.lua | 2 +- .vscode/factorio/runtime-api-LuaTile.lua | 2 +- .../factorio/runtime-api-LuaTilePrototype.lua | 2 +- .vscode/factorio/runtime-api-LuaTrain.lua | 2 +- ...untime-api-LuaTrainStopControlBehavior.lua | 2 +- ...me-api-LuaTransportBeltControlBehavior.lua | 2 +- .../factorio/runtime-api-LuaTransportLine.lua | 2 +- .../runtime-api-LuaTrivialSmokePrototype.lua | 2 +- .vscode/factorio/runtime-api-LuaUnitGroup.lua | 2 +- .../runtime-api-LuaVirtualSignalPrototype.lua | 4 +- ...ntime-api-LuaVoidEnergySourcePrototype.lua | 5 +- .../runtime-api-LuaWallControlBehavior.lua | 2 +- .vscode/factorio/runtime-api-builtin.lua | 2 +- .vscode/factorio/runtime-api-concepts.lua | 119 ++++++++++++++---- .vscode/factorio/runtime-api-custom.lua | 2 +- .vscode/factorio/runtime-api-defines.lua | 2 +- .vscode/factorio/runtime-api-events.lua | 8 +- .../factorio/runtime-api-global_functions.lua | 4 +- .vscode/factorio/runtime-api-table_types.lua | 2 +- cybersyn/control.lua | 1 + cybersyn/graphics/icons/area-of-effect.png | Bin 0 -> 1364 bytes cybersyn/graphics/icons/lost-train.png | Bin 2532 -> 1184 bytes cybersyn/prototypes/entity.lua | 5 +- cybersyn/prototypes/item.lua | 2 +- cybersyn/scripts/controller.lua | 7 +- cybersyn/scripts/global.lua | 2 +- cybersyn/scripts/layout.lua | 4 +- cybersyn/scripts/main.lua | 29 +++-- 113 files changed, 432 insertions(+), 222 deletions(-) create mode 100644 cybersyn/graphics/icons/area-of-effect.png diff --git a/.vscode/factorio/runtime-api-LuaAISettings.lua b/.vscode/factorio/runtime-api-LuaAISettings.lua index 158e690..224babe 100644 --- a/.vscode/factorio/runtime-api-LuaAISettings.lua +++ b/.vscode/factorio/runtime-api-LuaAISettings.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaAISettings -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaAccumulatorControlBehavior.lua b/.vscode/factorio/runtime-api-LuaAccumulatorControlBehavior.lua index de0db1c..3f82cf4 100644 --- a/.vscode/factorio/runtime-api-LuaAccumulatorControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaAccumulatorControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaAccumulatorControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaAchievementPrototype.lua b/.vscode/factorio/runtime-api-LuaAchievementPrototype.lua index 4874bc5..90fa333 100644 --- a/.vscode/factorio/runtime-api-LuaAchievementPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaAchievementPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaAchievementPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaAmmoCategoryPrototype.lua b/.vscode/factorio/runtime-api-LuaAmmoCategoryPrototype.lua index de1c73b..812d6ce 100644 --- a/.vscode/factorio/runtime-api-LuaAmmoCategoryPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaAmmoCategoryPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaAmmoCategoryPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaArithmeticCombinatorControlBehavior.lua b/.vscode/factorio/runtime-api-LuaArithmeticCombinatorControlBehavior.lua index 45f6211..8087806 100644 --- a/.vscode/factorio/runtime-api-LuaArithmeticCombinatorControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaArithmeticCombinatorControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaArithmeticCombinatorControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaAutoplaceControlPrototype.lua b/.vscode/factorio/runtime-api-LuaAutoplaceControlPrototype.lua index b034b51..97a65fe 100644 --- a/.vscode/factorio/runtime-api-LuaAutoplaceControlPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaAutoplaceControlPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaAutoplaceControlPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaBootstrap.lua b/.vscode/factorio/runtime-api-LuaBootstrap.lua index c8e512d..e279aa0 100644 --- a/.vscode/factorio/runtime-api-LuaBootstrap.lua +++ b/.vscode/factorio/runtime-api-LuaBootstrap.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaBootstrap -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaBurner.lua b/.vscode/factorio/runtime-api-LuaBurner.lua index 215d594..32992ff 100644 --- a/.vscode/factorio/runtime-api-LuaBurner.lua +++ b/.vscode/factorio/runtime-api-LuaBurner.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaBurner -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaBurnerPrototype.lua b/.vscode/factorio/runtime-api-LuaBurnerPrototype.lua index 4533341..b5e2a36 100644 --- a/.vscode/factorio/runtime-api-LuaBurnerPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaBurnerPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaBurnerPrototype -- This file is automatically generated. Edits will be overwritten. @@ -18,7 +18,8 @@ --- ---[View documentation](https://lua-api.factorio.com/latest/LuaBurnerPrototype.html#LuaBurnerPrototype.effectivity) ---@field effectivity double ----[R] +---[R] +---The emissions of this energy source in `pollution/Joule`. Multiplying it by energy consumption in `Watt` gives `pollution/second`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaBurnerPrototype.html#LuaBurnerPrototype.emissions) ---@field emissions double diff --git a/.vscode/factorio/runtime-api-LuaChunkIterator.lua b/.vscode/factorio/runtime-api-LuaChunkIterator.lua index d36436b..ada1853 100644 --- a/.vscode/factorio/runtime-api-LuaChunkIterator.lua +++ b/.vscode/factorio/runtime-api-LuaChunkIterator.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaChunkIterator -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaCircuitNetwork.lua b/.vscode/factorio/runtime-api-LuaCircuitNetwork.lua index 65316d0..76216ad 100644 --- a/.vscode/factorio/runtime-api-LuaCircuitNetwork.lua +++ b/.vscode/factorio/runtime-api-LuaCircuitNetwork.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaCircuitNetwork -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaCombinatorControlBehavior.lua b/.vscode/factorio/runtime-api-LuaCombinatorControlBehavior.lua index 3fad135..d6e569f 100644 --- a/.vscode/factorio/runtime-api-LuaCombinatorControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaCombinatorControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaCombinatorControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaCommandProcessor.lua b/.vscode/factorio/runtime-api-LuaCommandProcessor.lua index 36fbf9c..c9039d6 100644 --- a/.vscode/factorio/runtime-api-LuaCommandProcessor.lua +++ b/.vscode/factorio/runtime-api-LuaCommandProcessor.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaCommandProcessor -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaConstantCombinatorControlBehavior.lua b/.vscode/factorio/runtime-api-LuaConstantCombinatorControlBehavior.lua index 1c8b4e8..3d3802c 100644 --- a/.vscode/factorio/runtime-api-LuaConstantCombinatorControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaConstantCombinatorControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaConstantCombinatorControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaContainerControlBehavior.lua b/.vscode/factorio/runtime-api-LuaContainerControlBehavior.lua index 36a7b48..7383a3d 100644 --- a/.vscode/factorio/runtime-api-LuaContainerControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaContainerControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaContainerControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaControl.lua b/.vscode/factorio/runtime-api-LuaControl.lua index beb9bcf..91291b3 100644 --- a/.vscode/factorio/runtime-api-LuaControl.lua +++ b/.vscode/factorio/runtime-api-LuaControl.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaControl -- This file is automatically generated. Edits will be overwritten. @@ -206,14 +206,14 @@ --- ---This is the GUI that will asked to close (by firing the [on_gui_closed](https://lua-api.factorio.com/latest/events.html#on_gui_closed) event) when the `Esc` or `E` keys are pressed. If this attribute is not `nil`, and a new GUI is written to it, the existing one will be asked to close. --- ----**Note:** Write supports any of the types. Read will return the `entity`, `equipment`, `equipment-grid`, `player`, `element`, `inventory` or `nil`. +---**Note:** Write supports any of the types. Read will return the `entity`, `equipment`, `equipment-grid`, `player`, `element`, `inventory`, `technology`, or `nil`. --- ---**Events:** --- * May raise [on_gui_opened](https://lua-api.factorio.com/latest/events.html#on_gui_opened) instantly. ---Raised when writing a valid GUI target to this attribute. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaControl.html#LuaControl.opened) ----@field opened? LuaEntity|LuaItemStack|LuaEquipment|LuaEquipmentGrid|LuaPlayer|LuaGuiElement|LuaInventory|defines.gui_type +---@field opened? LuaEntity|LuaItemStack|LuaEquipment|LuaEquipmentGrid|LuaPlayer|LuaGuiElement|LuaInventory|LuaTechnology|defines.gui_type ---[R] --- ---[View documentation](https://lua-api.factorio.com/latest/LuaControl.html#LuaControl.opened_gui_type) @@ -244,7 +244,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/LuaControl.html#LuaControl.resource_reach_distance) ---@field resource_reach_distance double ---[RW] ----Current riding state of this car or the vehicle this player is riding in. +---Current riding state of this car, or of the car this player is riding in. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaControl.html#LuaControl.riding_state) ---@field riding_state RidingState @@ -426,13 +426,13 @@ get_item_count=function(item)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaControl.html#LuaControl.get_main_inventory) ---@return LuaInventory?@The inventory or `nil` if this entity is not a character or player. get_main_inventory=function()end, ----Gets the parameters of a personal logistic request and auto-trash slot. Only used on `spider-vehicle`. +---Gets the parameters of a personal logistic request and auto-trash slot. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaControl.html#LuaControl.get_personal_logistic_slot) ---@param slot_index uint@The slot to get. ---@return LogisticParameters@The logistic parameters. If personal logistics are not researched yet, their `name` will be `nil`. get_personal_logistic_slot=function(slot_index)end, ----Gets the parameters of a vehicle logistic request and auto-trash slot. +---Gets the parameters of a vehicle logistic request and auto-trash slot. Only used on `spider-vehicle`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaControl.html#LuaControl.get_vehicle_logistic_slot) ---@param slot_index uint@The slot to get. diff --git a/.vscode/factorio/runtime-api-LuaControlBehavior.lua b/.vscode/factorio/runtime-api-LuaControlBehavior.lua index 0ceaad3..4fc360f 100644 --- a/.vscode/factorio/runtime-api-LuaControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaCustomChartTag.lua b/.vscode/factorio/runtime-api-LuaCustomChartTag.lua index 665b0be..f9dc650 100644 --- a/.vscode/factorio/runtime-api-LuaCustomChartTag.lua +++ b/.vscode/factorio/runtime-api-LuaCustomChartTag.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaCustomChartTag -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaCustomInputPrototype.lua b/.vscode/factorio/runtime-api-LuaCustomInputPrototype.lua index f5219b3..baccea3 100644 --- a/.vscode/factorio/runtime-api-LuaCustomInputPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaCustomInputPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaCustomInputPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaCustomTable.lua b/.vscode/factorio/runtime-api-LuaCustomTable.lua index c59d5f0..1fd3b7e 100644 --- a/.vscode/factorio/runtime-api-LuaCustomTable.lua +++ b/.vscode/factorio/runtime-api-LuaCustomTable.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaCustomTable -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaDamagePrototype.lua b/.vscode/factorio/runtime-api-LuaDamagePrototype.lua index 9b998e6..d5732d9 100644 --- a/.vscode/factorio/runtime-api-LuaDamagePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaDamagePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaDamagePrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaDeciderCombinatorControlBehavior.lua b/.vscode/factorio/runtime-api-LuaDeciderCombinatorControlBehavior.lua index 5595943..f604812 100644 --- a/.vscode/factorio/runtime-api-LuaDeciderCombinatorControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaDeciderCombinatorControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaDeciderCombinatorControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaDecorativePrototype.lua b/.vscode/factorio/runtime-api-LuaDecorativePrototype.lua index cbb7a40..f653295 100644 --- a/.vscode/factorio/runtime-api-LuaDecorativePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaDecorativePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaDecorativePrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaElectricEnergySourcePrototype.lua b/.vscode/factorio/runtime-api-LuaElectricEnergySourcePrototype.lua index 0728ff7..a029a3c 100644 --- a/.vscode/factorio/runtime-api-LuaElectricEnergySourcePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaElectricEnergySourcePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaElectricEnergySourcePrototype -- This file is automatically generated. Edits will be overwritten. @@ -18,7 +18,8 @@ --- ---[View documentation](https://lua-api.factorio.com/latest/LuaElectricEnergySourcePrototype.html#LuaElectricEnergySourcePrototype.drain) ---@field drain double ----[R] +---[R] +---The emissions of this energy source in `pollution/Joule`. Multiplying it by energy consumption in `Watt` gives `pollution/second`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaElectricEnergySourcePrototype.html#LuaElectricEnergySourcePrototype.emissions) ---@field emissions double diff --git a/.vscode/factorio/runtime-api-LuaEntity.lua b/.vscode/factorio/runtime-api-LuaEntity.lua index 21bc446..601cb15 100644 --- a/.vscode/factorio/runtime-api-LuaEntity.lua +++ b/.vscode/factorio/runtime-api-LuaEntity.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaEntity -- This file is automatically generated. Edits will be overwritten. @@ -282,6 +282,8 @@ ---Whether the driver of this car or spidertron is the gunner. If `false`, the passenger is the gunner. `nil` if this is neither a car or a spidertron. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.driver_is_gunner) +--- +---_Can only be used if this is Car or SpiderVehicle_ ---@field driver_is_gunner? boolean ---[RW] ---Position where the entity puts its stuff. @@ -884,7 +886,7 @@ --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.selected_gun_index) --- ----_Can only be used if this is Character or Car_ +---_Can only be used if this is Character, Car or SpiderVehicle_ ---@field selected_gun_index? uint ---[R] ---[LuaEntityPrototype::selection_box](https://lua-api.factorio.com/latest/LuaEntityPrototype.html#LuaEntityPrototype.selection_box) around entity's given position and respecting the current entity orientation. @@ -892,7 +894,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.selection_box) ---@field selection_box BoundingBox ---[RW] ----The shooting target for this turret, if any. +---The shooting target for this turret, if any. Can't be set to `nil` via script. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.shooting_target) ---@field shooting_target? LuaEntity @@ -1513,7 +1515,7 @@ get_heat_setting=function()end, ---_Can only be used if this is Rail_ ---@return LuaEntity[] get_inbound_signals=function()end, ----Gets the filter for this infinity container at the given index or `nil` if the filter index doesn't exist or is empty. +---Gets the filter for this infinity container at the given index, or `nil` if the filter index doesn't exist or is empty. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.get_infinity_container_filter) --- @@ -1521,7 +1523,7 @@ get_inbound_signals=function()end, ---@param index uint@The index to get. ---@return InfinityInventoryFilter? get_infinity_container_filter=function(index)end, ----Gets the filter for this infinity pipe or `nil` if the filter is empty. +---Gets the filter for this infinity pipe, or `nil` if the filter is empty. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.get_infinity_pipe_filter) --- @@ -1598,7 +1600,7 @@ get_parent_signals=function()end, --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.get_passenger) --- ----_Can only be used if this is Vehicle_ +---_Can only be used if this is Car or SpiderVehicle_ ---@return LuaEntity|LuaPlayer?@`nil` if the vehicle contains no passenger. To check if there's a driver see [LuaEntity::get_driver](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.get_driver). get_passenger=function()end, ---The radius of this entity. @@ -2065,7 +2067,7 @@ set_driver=function(driver)end, --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.set_filter) ---@param slot_index uint@Index of the slot to set the filter for. ----@param item string@Prototype name of the item to filter. +---@param item string|nil@Prototype name of the item to filter, or `nil` to clear the filter. set_filter=function(slot_index,item)end, ---Sets the heat setting for this heat interface. --- @@ -2080,14 +2082,14 @@ set_heat_setting=function(filter)end, --- ---_Can only be used if this is InfinityContainer_ ---@param index uint@The index to set. ----@param filter InfinityInventoryFilter@The new filter or `nil` to clear the filter. +---@param filter InfinityInventoryFilter|nil@The new filter, or `nil` to clear the filter. set_infinity_container_filter=function(index,filter)end, ---Sets the filter for this infinity pipe. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.set_infinity_pipe_filter) --- ---_Can only be used if this is InfinityPipe_ ----@param filter InfinityPipeFilter@The new filter or `nil` to clear the filter. +---@param filter InfinityPipeFilter|nil@The new filter, or `nil` to clear the filter. set_infinity_pipe_filter=function(filter)end, ---Sets the passenger of this car or spidertron. --- @@ -2098,7 +2100,7 @@ set_infinity_pipe_filter=function(filter)end, --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.set_passenger) --- ----_Can only be used if this is Vehicle_ +---_Can only be used if this is Car or SpiderVehicle_ ---@param passenger LuaEntity|PlayerIdentification set_passenger=function(passenger)end, ---Sets the current recipe in this assembly machine. @@ -2153,7 +2155,7 @@ spawn_decorations=function()end, --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.start_fading_out) start_fading_out=function()end, ----Stops the given SpiderVehicle. +---Sets the [speed](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.speed) of the given SpiderVehicle to zero. Notably does not clear its [autopilot_destination](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.autopilot_destination), which it will continue moving towards if set. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.stop_spider) --- diff --git a/.vscode/factorio/runtime-api-LuaEntityPrototype.lua b/.vscode/factorio/runtime-api-LuaEntityPrototype.lua index c47f776..7760292 100644 --- a/.vscode/factorio/runtime-api-LuaEntityPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaEntityPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaEntityPrototype -- This file is automatically generated. Edits will be overwritten. @@ -129,6 +129,13 @@ ---_Can only be used if this is Lamp_ ---@field always_on? boolean ---[R] +---Name of the ammo category of this land mine. +--- +---[View documentation](https://lua-api.factorio.com/latest/LuaEntityPrototype.html#LuaEntityPrototype.ammo_category) +--- +---_Can only be used if this is LandMine_ +---@field ammo_category? string +---[R] ---The animation speed coefficient of this belt connectable prototype. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntityPrototype.html#LuaEntityPrototype.animation_speed_coefficient) @@ -742,7 +749,51 @@ --- ---_Can only be used if this is ProgrammableSpeaker_ ---@field instruments? ProgrammableSpeakerInstrument[] ----[R] +---[R] +---Everything in the following list is considered a building. +--- +---- AccumulatorPrototype +---- ArtilleryTurretPrototype +---- BeaconPrototype +---- BoilerPrototype +---- BurnerGeneratorPrototype +---- CombinatorPrototype → ArithmeticCombinator, DeciderCombinator +---- ConstantCombinatorPrototype +---- ContainerPrototype → LogisticContainer, InfinityContainer +---- CraftingMachinePrototype → AssemblingMachine, RocketSilo, Furnace +---- ElectricEnergyInterfacePrototype +---- ElectricPolePrototype +---- EnemySpawnerPrototype +---- GatePrototype +---- GeneratorPrototype +---- HeatInterfacePrototype +---- HeatPipePrototype +---- InserterPrototype +---- LabPrototype +---- LampPrototype +---- LinkedContainerPrototype +---- MarketPrototype +---- MiningDrillPrototype +---- OffshorePumpPrototype +---- PipePrototype → InfinityPipe +---- PipeToGroundPrototype +---- PlayerPortPrototype +---- PowerSwitchPrototype +---- ProgrammableSpeakerPrototype +---- PumpPrototype +---- RadarPrototype +---- RailPrototype → CurvedRail, StraightRail +---- RailSignalBasePrototype → RailChainSignal, RailSignal +---- ReactorPrototype +---- RoboportPrototype +---- SimpleEntityPrototype +---- SimpleEntityWithOwnerPrototype → SimpleEntityWithForce +---- SolarPanelPrototype +---- StorageTankPrototype +---- TrainStopPrototype +---- TransportBeltConnectablePrototype → LinkedBelt, Loader1x1, Loader1x2, Splitter, TransportBelt, UndergroundBelt +---- TurretPrototype → AmmoTurret, ElectricTurret, FluidTurret +---- WallPrototype --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEntityPrototype.html#LuaEntityPrototype.is_building) ---@field is_building boolean diff --git a/.vscode/factorio/runtime-api-LuaEquipment.lua b/.vscode/factorio/runtime-api-LuaEquipment.lua index 43069fc..000daf7 100644 --- a/.vscode/factorio/runtime-api-LuaEquipment.lua +++ b/.vscode/factorio/runtime-api-LuaEquipment.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaEquipment -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaEquipmentCategoryPrototype.lua b/.vscode/factorio/runtime-api-LuaEquipmentCategoryPrototype.lua index 1cb40cb..6217a36 100644 --- a/.vscode/factorio/runtime-api-LuaEquipmentCategoryPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaEquipmentCategoryPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaEquipmentCategoryPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaEquipmentGrid.lua b/.vscode/factorio/runtime-api-LuaEquipmentGrid.lua index a5cc637..06df396 100644 --- a/.vscode/factorio/runtime-api-LuaEquipmentGrid.lua +++ b/.vscode/factorio/runtime-api-LuaEquipmentGrid.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaEquipmentGrid -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaEquipmentGridPrototype.lua b/.vscode/factorio/runtime-api-LuaEquipmentGridPrototype.lua index fd5a1f9..ad6c918 100644 --- a/.vscode/factorio/runtime-api-LuaEquipmentGridPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaEquipmentGridPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaEquipmentGridPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaEquipmentPrototype.lua b/.vscode/factorio/runtime-api-LuaEquipmentPrototype.lua index d8b45ba..d82cb27 100644 --- a/.vscode/factorio/runtime-api-LuaEquipmentPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaEquipmentPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaEquipmentPrototype -- This file is automatically generated. Edits will be overwritten. @@ -75,13 +75,15 @@ ---**Note:** Both the `charging_station_shift` and `stationing_offset` vectors are tables with `x` and `y` keys instead of an array. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEquipmentPrototype.html#LuaEquipmentPrototype.logistic_parameters) ----@field logistic_parameters LuaEquipmentPrototype.logistic_parameters +--- +---_Can only be used if this is RoboportEquipment_ +---@field logistic_parameters? LuaEquipmentPrototype.logistic_parameters ---[R] --- ---[View documentation](https://lua-api.factorio.com/latest/LuaEquipmentPrototype.html#LuaEquipmentPrototype.movement_bonus) --- ----_Can only be used if this is MovementBonusEquipmentPrototype_ ----@field movement_bonus float +---_Can only be used if this is MovementBonusEquipment_ +---@field movement_bonus? float ---[R] ---Name of this prototype. --- diff --git a/.vscode/factorio/runtime-api-LuaFlowStatistics.lua b/.vscode/factorio/runtime-api-LuaFlowStatistics.lua index bc80d5a..85384b5 100644 --- a/.vscode/factorio/runtime-api-LuaFlowStatistics.lua +++ b/.vscode/factorio/runtime-api-LuaFlowStatistics.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaFlowStatistics -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaFluidBox.lua b/.vscode/factorio/runtime-api-LuaFluidBox.lua index 319905f..64e64f9 100644 --- a/.vscode/factorio/runtime-api-LuaFluidBox.lua +++ b/.vscode/factorio/runtime-api-LuaFluidBox.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaFluidBox -- This file is automatically generated. Edits will be overwritten. @@ -41,7 +41,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/LuaFluidBox.html#LuaFluidBox.length) ---@operator len: uint ---[R] ----Access, set or clear a fluid box. The index must always be in bounds (see [LuaFluidBox::index](https://lua-api.factorio.com/latest/LuaFluidBox.html#LuaFluidBox.index)). New fluidboxes may not be added or removed using this operator. +---Access, set or clear a fluid box. The index must always be in bounds (see [LuaFluidBox::length_operator](https://lua-api.factorio.com/latest/LuaFluidBox.html#LuaFluidBox.length_operator)). New fluidboxes may not be added or removed using this operator. --- ---Is `nil` if the given fluid box does not contain any fluid. Writing `nil` removes all fluid from the fluid box. --- @@ -110,7 +110,7 @@ help=function()end, --- ---[View documentation](https://lua-api.factorio.com/latest/LuaFluidBox.html#LuaFluidBox.set_filter) ---@param index uint@The index of the filter to set. ----@param filter FluidBoxFilterSpec?@The filter to set. Setting `nil` clears the filter. +---@param filter FluidBoxFilterSpec|nil@The filter to set. Setting `nil` clears the filter. ---@return boolean@Whether the filter was set successfully. set_filter=function(index,filter)end, } diff --git a/.vscode/factorio/runtime-api-LuaFluidBoxPrototype.lua b/.vscode/factorio/runtime-api-LuaFluidBoxPrototype.lua index 3ca4b7b..d20585b 100644 --- a/.vscode/factorio/runtime-api-LuaFluidBoxPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaFluidBoxPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaFluidBoxPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaFluidEnergySourcePrototype.lua b/.vscode/factorio/runtime-api-LuaFluidEnergySourcePrototype.lua index f3a2360..96f33c4 100644 --- a/.vscode/factorio/runtime-api-LuaFluidEnergySourcePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaFluidEnergySourcePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaFluidEnergySourcePrototype -- This file is automatically generated. Edits will be overwritten. @@ -22,7 +22,8 @@ --- ---[View documentation](https://lua-api.factorio.com/latest/LuaFluidEnergySourcePrototype.html#LuaFluidEnergySourcePrototype.effectivity) ---@field effectivity double ----[R] +---[R] +---The emissions of this energy source in `pollution/Joule`. Multiplying it by energy consumption in `Watt` gives `pollution/second`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaFluidEnergySourcePrototype.html#LuaFluidEnergySourcePrototype.emissions) ---@field emissions double diff --git a/.vscode/factorio/runtime-api-LuaFluidPrototype.lua b/.vscode/factorio/runtime-api-LuaFluidPrototype.lua index 2168209..323e73d 100644 --- a/.vscode/factorio/runtime-api-LuaFluidPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaFluidPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaFluidPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaFontPrototype.lua b/.vscode/factorio/runtime-api-LuaFontPrototype.lua index fe44f7f..20ea356 100644 --- a/.vscode/factorio/runtime-api-LuaFontPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaFontPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaFontPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaForce.lua b/.vscode/factorio/runtime-api-LuaForce.lua index 1da4f3d..b2f383e 100644 --- a/.vscode/factorio/runtime-api-LuaForce.lua +++ b/.vscode/factorio/runtime-api-LuaForce.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaForce -- This file is automatically generated. Edits will be overwritten. @@ -502,8 +502,12 @@ get_saved_technology_progress=function(technology)end, get_spawn_position=function(surface)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaForce.html#LuaForce.get_train_stops) ---@class LuaForce.get_train_stops_param +---The name(s) of the train stops. Not providing names will match any stop. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaForce.html#LuaForce.get_train_stops) ---@field name? string|string[] +---The surface to search. Not providing a surface will match stops on any surface. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaForce.html#LuaForce.get_train_stops) ---@field surface? SurfaceIdentification @@ -515,7 +519,7 @@ get_spawn_position=function(surface)end, ---@return LuaEntity[] get_train_stops=function(param)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaForce.html#LuaForce.get_trains) ----@param surface SurfaceIdentification?@If given only trains on the surface are returned. +---@param surface SurfaceIdentification?@The surface to search. Not providing a surface will match trains on any surface. ---@return LuaTrain[] get_trains=function(surface)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaForce.html#LuaForce.get_turret_attack_modifier) diff --git a/.vscode/factorio/runtime-api-LuaFuelCategoryPrototype.lua b/.vscode/factorio/runtime-api-LuaFuelCategoryPrototype.lua index 1de1b8e..eba1945 100644 --- a/.vscode/factorio/runtime-api-LuaFuelCategoryPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaFuelCategoryPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaFuelCategoryPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaGameScript.lua b/.vscode/factorio/runtime-api-LuaGameScript.lua index efadaa3..77d71bb 100644 --- a/.vscode/factorio/runtime-api-LuaGameScript.lua +++ b/.vscode/factorio/runtime-api-LuaGameScript.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaGameScript -- This file is automatically generated. Edits will be overwritten. @@ -500,7 +500,7 @@ force_crc=function()end, ---**Note:** This is very expensive to determine. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.get_active_entities_count) ----@param surface SurfaceIdentification?@If give, only the entities active on this surface are counted. +---@param surface SurfaceIdentification?@If given, only the entities active on this surface are counted. ---@return uint get_active_entities_count=function(surface)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.get_entity_by_tag) @@ -656,10 +656,16 @@ get_script_inventories=function(mod)end, get_surface=function(surface)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.get_train_stops) ---@class LuaGameScript.get_train_stops_param +---The name(s) of the train stops. Not providing names will match any stop. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.get_train_stops) ---@field name? string|string[] +---The surface to search. Not providing a surface will match stops on any surface. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.get_train_stops) ---@field surface? SurfaceIdentification +---The force to search. Not providing a force will match stops in any force. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.get_train_stops) ---@field force? ForceIdentification @@ -841,18 +847,18 @@ server_save=function(name)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.set_game_state) ---@class LuaGameScript.set_game_state_param ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.set_game_state) ----@field game_finished boolean +---@field game_finished? boolean ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.set_game_state) ----@field player_won boolean +---@field player_won? boolean ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.set_game_state) ----@field next_level string +---@field next_level? string ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.set_game_state) ----@field can_continue boolean +---@field can_continue? boolean ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.set_game_state) ----@field victorious_force ForceIdentification +---@field victorious_force? ForceIdentification ----Set scenario state. +---Set scenario state. Any parameters not provided do not change the current state. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGameScript.html#LuaGameScript.set_game_state) ---@param param LuaGameScript.set_game_state_param diff --git a/.vscode/factorio/runtime-api-LuaGenericOnOffControlBehavior.lua b/.vscode/factorio/runtime-api-LuaGenericOnOffControlBehavior.lua index 2ae43b6..129d26b 100644 --- a/.vscode/factorio/runtime-api-LuaGenericOnOffControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaGenericOnOffControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaGenericOnOffControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaGroup.lua b/.vscode/factorio/runtime-api-LuaGroup.lua index c7800bd..4be90e8 100644 --- a/.vscode/factorio/runtime-api-LuaGroup.lua +++ b/.vscode/factorio/runtime-api-LuaGroup.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaGroup -- This file is automatically generated. Edits will be overwritten. @@ -35,19 +35,15 @@ ---[View documentation](https://lua-api.factorio.com/latest/LuaGroup.html#LuaGroup.order) ---@field order string ---[R] ----The additional order value used in recipe ordering. ---- ----**Note:** Can only be used on groups, not on subgroups. +---The additional order value used in recipe ordering. Can only be used on groups, not on subgroups. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGroup.html#LuaGroup.order_in_recipe) ----@field order_in_recipe string +---@field order_in_recipe? string ---[R] ----Subgroups of this group. ---- ----**Note:** Can only be used on groups, not on subgroups. +---Subgroups of this group. Can only be used on groups, not on subgroups. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGroup.html#LuaGroup.subgroups) ----@field subgroups LuaGroup[] +---@field subgroups? LuaGroup[] ---[R] --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGroup.html#LuaGroup.type) diff --git a/.vscode/factorio/runtime-api-LuaGui.lua b/.vscode/factorio/runtime-api-LuaGui.lua index 064aa97..4335ce0 100644 --- a/.vscode/factorio/runtime-api-LuaGui.lua +++ b/.vscode/factorio/runtime-api-LuaGui.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaGui -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaGuiElement.lua b/.vscode/factorio/runtime-api-LuaGuiElement.lua index 6f4c090..5ffd124 100644 --- a/.vscode/factorio/runtime-api-LuaGuiElement.lua +++ b/.vscode/factorio/runtime-api-LuaGuiElement.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaGuiElement -- This file is automatically generated. Edits will be overwritten. @@ -127,9 +127,11 @@ ---_Can only be used if this is textfield or text-box_ ---@field clear_and_focus_on_right_click boolean ---[RW] ----The image to display on this sprite-button when it is clicked. +---The sprite to display on this sprite-button when it is clicked. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.clicked_sprite) +--- +---_Can only be used if this is sprite-button_ ---@field clicked_sprite SpritePath ---[R] ---The number of columns in this table. @@ -236,11 +238,15 @@ ---The entity associated with this entity-preview, camera, minimap, if any. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.entity) +--- +---_Can only be used if this is entity-preview, camera or minimap_ ---@field entity? LuaEntity ---[RW] ---The force this minimap is using, if any. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.force) +--- +---_Can only be used if this is minimap_ ---@field force? string ---[R] ---The GUI this element is a child of. @@ -255,7 +261,7 @@ ---_Can only be used if this is scroll-pane_ ---@field horizontal_scroll_policy string ---[RW] ----The image to display on this sprite-button when it is hovered. +---The sprite to display on this sprite-button when it is hovered. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.hovered_sprite) --- @@ -282,6 +288,8 @@ ---The items in this dropdown or listbox. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.items) +--- +---_Can only be used if this is drop-down or list-box_ ---@field items LocalisedString[] ---[RW] ---The text shown for the left switch label. @@ -327,6 +335,8 @@ ---The mouse button filters for this button or sprite-button. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.mouse_button_filter) +--- +---_Can only be used if this is button or sprite-button_ ---@field mouse_button_filter MouseButtonFlags ---[RW] ---The name of this element. `""` if no name was set. @@ -342,6 +352,8 @@ ---The number to be shown in the bottom right corner of this sprite-button. Set this to `nil` to show nothing. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.number) +--- +---_Can only be used if this is sprite-button_ ---@field number double ---[RW] ---Whether this textfield is limited to only numberic characters. @@ -369,6 +381,8 @@ ---The position this camera or minimap is focused on, if any. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.position) +--- +---_Can only be used if this is camera or minimap_ ---@field position MapPosition ---[RW] ---Whether this text-box is read-only. Defaults to `false`. @@ -378,9 +392,11 @@ ---_Can only be used if this is text-box_ ---@field read_only boolean ---[RW] ----Whether the image widget should resize according to the sprite in it. Defaults to `true`. +---Whether the sprite widget should resize according to the sprite in it. Defaults to `true`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.resize_to_sprite) +--- +---_Can only be used if this is sprite_ ---@field resize_to_sprite boolean ---[RW] ---The text shown for the right switch label. @@ -407,6 +423,8 @@ ---The selected index for this dropdown or listbox. Returns `0` if none is selected. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.selected_index) +--- +---_Can only be used if this is drop-down or list-box_ ---@field selected_index uint ---[RW] ---The selected tab index for this tabbed pane, if any. @@ -419,6 +437,8 @@ ---Related to the number to be shown in the bottom right corner of this sprite-button. When set to `true`, numbers that are non-zero and smaller than one are shown as a percentage rather than the value. For example, `0.5` will be shown as `50%` instead. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.show_percent_for_small_numbers) +--- +---_Can only be used if this is sprite-button_ ---@field show_percent_for_small_numbers boolean ---[RW] ---The value of this slider element. @@ -428,16 +448,18 @@ ---_Can only be used if this is slider_ ---@field slider_value double ---[RW] ----The image to display on this sprite-button or sprite in the default state. +---The sprite to display on this sprite-button or sprite in the default state. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.sprite) +--- +---_Can only be used if this is sprite-button or sprite_ ---@field sprite SpritePath ---[RW] ---Is this checkbox or radiobutton checked? --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.state) --- ----_Can only be used if this is CheckBox or RadioButton_ +---_Can only be used if this is checkbox or radiobutton_ ---@field state boolean ---[RW] ---The style of this element. When read, this evaluates to a [LuaStyle](https://lua-api.factorio.com/latest/LuaStyle.html). For writing, it only accepts a string that specifies the textual identifier (prototype name) of the desired style. @@ -448,6 +470,8 @@ ---The surface index this camera or minimap is using. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.surface_index) +--- +---_Can only be used if this is camera or minimap_ ---@field surface_index uint ---[RW] ---The switch state (left, none, right) for this switch. @@ -462,6 +486,8 @@ ---The tabs and contents being shown in this tabbed-pane. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.tabs) +--- +---_Can only be used if this is tabbed-pane_ ---@field tabs TabAndContent[] ---[RW] ---The tags associated with this LuaGuiElement. @@ -526,6 +552,8 @@ ---The zoom this camera or minimap is using. This value must be positive. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.zoom) +--- +---_Can only be used if this is camera or minimap_ ---@field zoom double ---[R] ---The indexing operator. Gets children by name. @@ -563,7 +591,7 @@ local LuaGuiElement={ --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.add) ---@field ignored_by_interaction? boolean ----Style of the child element. +---The name of the style prototype to apply to the new element. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.add) ---@field style? string @@ -905,6 +933,8 @@ add=function(param)end, ---Inserts a string at the end or at the given index of this dropdown or listbox. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.add_item) +--- +---_Can only be used if this is drop-down or list-box_ ---@param string LocalisedString@The text to insert. ---@param index uint?@The index at which to insert the item. add_item=function(string,index)end, @@ -934,6 +964,8 @@ clear=function()end, ---Removes the items in this dropdown or listbox. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.clear_items) +--- +---_Can only be used if this is drop-down or list-box_ clear_items=function()end, ---Remove this element, along with its children. Any [LuaGuiElement](https://lua-api.factorio.com/latest/LuaGuiElement.html) objects referring to the destroyed elements become invalid after this operation. --- @@ -969,6 +1001,8 @@ get_index_in_parent=function()end, ---Gets the item at the given index from this dropdown or listbox. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.get_item) +--- +---_Can only be used if this is drop-down or list-box_ ---@param index uint@The index to get ---@return LocalisedString get_item=function(index)end, @@ -1012,6 +1046,8 @@ help=function()end, ---Removes the item at the given index from this dropdown or listbox. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.remove_item) +--- +---_Can only be used if this is drop-down or list-box_ ---@param index uint@The index remove_item=function(index)end, ---Removes the given tab and its associated content from this tabbed pane. @@ -1094,6 +1130,8 @@ select_all=function()end, ---Sets the given string at the given index in this dropdown or listbox. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaGuiElement.html#LuaGuiElement.set_item) +--- +---_Can only be used if this is drop-down or list-box_ ---@param index uint@The index whose text to replace. ---@param string LocalisedString@The text to set at the given index. set_item=function(index,string)end, diff --git a/.vscode/factorio/runtime-api-LuaHeatBufferPrototype.lua b/.vscode/factorio/runtime-api-LuaHeatBufferPrototype.lua index fc149da..ec8a954 100644 --- a/.vscode/factorio/runtime-api-LuaHeatBufferPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaHeatBufferPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaHeatBufferPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaHeatEnergySourcePrototype.lua b/.vscode/factorio/runtime-api-LuaHeatEnergySourcePrototype.lua index 22a4851..65a02e2 100644 --- a/.vscode/factorio/runtime-api-LuaHeatEnergySourcePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaHeatEnergySourcePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaHeatEnergySourcePrototype -- This file is automatically generated. Edits will be overwritten. @@ -18,7 +18,8 @@ --- ---[View documentation](https://lua-api.factorio.com/latest/LuaHeatEnergySourcePrototype.html#LuaHeatEnergySourcePrototype.default_temperature) ---@field default_temperature double ----[R] +---[R] +---The emissions of this energy source in `pollution/Joule`. Multiplying it by energy consumption in `Watt` gives `pollution/second`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaHeatEnergySourcePrototype.html#LuaHeatEnergySourcePrototype.emissions) ---@field emissions double diff --git a/.vscode/factorio/runtime-api-LuaInserterControlBehavior.lua b/.vscode/factorio/runtime-api-LuaInserterControlBehavior.lua index 957e15f..27f994c 100644 --- a/.vscode/factorio/runtime-api-LuaInserterControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaInserterControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaInserterControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaInventory.lua b/.vscode/factorio/runtime-api-LuaInventory.lua index cf4ddc3..365ae70 100644 --- a/.vscode/factorio/runtime-api-LuaInventory.lua +++ b/.vscode/factorio/runtime-api-LuaInventory.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaInventory -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaItemPrototype.lua b/.vscode/factorio/runtime-api-LuaItemPrototype.lua index 8f6ea14..409f552 100644 --- a/.vscode/factorio/runtime-api-LuaItemPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaItemPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaItemPrototype -- This file is automatically generated. Edits will be overwritten. @@ -543,7 +543,7 @@ local LuaItemPrototype={ ---[View documentation](https://lua-api.factorio.com/latest/LuaItemPrototype.html#LuaItemPrototype.get_ammo_type) --- ---_Can only be used if this is AmmoItem_ ----@param ammo_source_type string?@"default", "player", "turret", or "vehicle" +---@param ammo_source_type string?@One of `"default"`, `"player"`, `"turret"`, or `"vehicle"`. Defaults to `"default"`. ---@return AmmoType? get_ammo_type=function(ammo_source_type)end, ---Does this prototype have a flag enabled? diff --git a/.vscode/factorio/runtime-api-LuaItemStack.lua b/.vscode/factorio/runtime-api-LuaItemStack.lua index 6e337b5..e25608e 100644 --- a/.vscode/factorio/runtime-api-LuaItemStack.lua +++ b/.vscode/factorio/runtime-api-LuaItemStack.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaItemStack -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaLampControlBehavior.lua b/.vscode/factorio/runtime-api-LuaLampControlBehavior.lua index 1f789ca..4f70705 100644 --- a/.vscode/factorio/runtime-api-LuaLampControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaLampControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaLampControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaLazyLoadedValue.lua b/.vscode/factorio/runtime-api-LuaLazyLoadedValue.lua index efbf2c7..ec11d80 100644 --- a/.vscode/factorio/runtime-api-LuaLazyLoadedValue.lua +++ b/.vscode/factorio/runtime-api-LuaLazyLoadedValue.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaLazyLoadedValue -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaLogisticCell.lua b/.vscode/factorio/runtime-api-LuaLogisticCell.lua index 719fa6b..965356c 100644 --- a/.vscode/factorio/runtime-api-LuaLogisticCell.lua +++ b/.vscode/factorio/runtime-api-LuaLogisticCell.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaLogisticCell -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaLogisticContainerControlBehavior.lua b/.vscode/factorio/runtime-api-LuaLogisticContainerControlBehavior.lua index 4b956a5..e27e3a3 100644 --- a/.vscode/factorio/runtime-api-LuaLogisticContainerControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaLogisticContainerControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaLogisticContainerControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaLogisticNetwork.lua b/.vscode/factorio/runtime-api-LuaLogisticNetwork.lua index dce0f8d..db9e572 100644 --- a/.vscode/factorio/runtime-api-LuaLogisticNetwork.lua +++ b/.vscode/factorio/runtime-api-LuaLogisticNetwork.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaLogisticNetwork -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaLogisticPoint.lua b/.vscode/factorio/runtime-api-LuaLogisticPoint.lua index 736ca77..9fd0c05 100644 --- a/.vscode/factorio/runtime-api-LuaLogisticPoint.lua +++ b/.vscode/factorio/runtime-api-LuaLogisticPoint.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaLogisticPoint -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaMiningDrillControlBehavior.lua b/.vscode/factorio/runtime-api-LuaMiningDrillControlBehavior.lua index 27c3412..a485d03 100644 --- a/.vscode/factorio/runtime-api-LuaMiningDrillControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaMiningDrillControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaMiningDrillControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaModSettingPrototype.lua b/.vscode/factorio/runtime-api-LuaModSettingPrototype.lua index 4c4978c..373ec83 100644 --- a/.vscode/factorio/runtime-api-LuaModSettingPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaModSettingPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaModSettingPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaModuleCategoryPrototype.lua b/.vscode/factorio/runtime-api-LuaModuleCategoryPrototype.lua index 130f506..647925b 100644 --- a/.vscode/factorio/runtime-api-LuaModuleCategoryPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaModuleCategoryPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaModuleCategoryPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaNamedNoiseExpression.lua b/.vscode/factorio/runtime-api-LuaNamedNoiseExpression.lua index f6c245e..16fd4b0 100644 --- a/.vscode/factorio/runtime-api-LuaNamedNoiseExpression.lua +++ b/.vscode/factorio/runtime-api-LuaNamedNoiseExpression.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaNamedNoiseExpression -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaNoiseLayerPrototype.lua b/.vscode/factorio/runtime-api-LuaNoiseLayerPrototype.lua index af9bc11..1b7ff53 100644 --- a/.vscode/factorio/runtime-api-LuaNoiseLayerPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaNoiseLayerPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaNoiseLayerPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaParticlePrototype.lua b/.vscode/factorio/runtime-api-LuaParticlePrototype.lua index b3f3b99..541cb68 100644 --- a/.vscode/factorio/runtime-api-LuaParticlePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaParticlePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaParticlePrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaPermissionGroup.lua b/.vscode/factorio/runtime-api-LuaPermissionGroup.lua index 018792a..ed3b159 100644 --- a/.vscode/factorio/runtime-api-LuaPermissionGroup.lua +++ b/.vscode/factorio/runtime-api-LuaPermissionGroup.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaPermissionGroup -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaPermissionGroups.lua b/.vscode/factorio/runtime-api-LuaPermissionGroups.lua index c840a91..9c296b1 100644 --- a/.vscode/factorio/runtime-api-LuaPermissionGroups.lua +++ b/.vscode/factorio/runtime-api-LuaPermissionGroups.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaPermissionGroups -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaPlayer.lua b/.vscode/factorio/runtime-api-LuaPlayer.lua index 1458612..0e135f7 100644 --- a/.vscode/factorio/runtime-api-LuaPlayer.lua +++ b/.vscode/factorio/runtime-api-LuaPlayer.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaPlayer -- This file is automatically generated. Edits will be overwritten. @@ -314,15 +314,15 @@ build_from_cursor=function(param)end, can_build_from_cursor=function(param)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaPlayer.html#LuaPlayer.can_place_entity) ---@class LuaPlayer.can_place_entity_param ----Name of the entity to check +---Name of the entity to check. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaPlayer.html#LuaPlayer.can_place_entity) ---@field name string ----Where the entity would be placed +---Where the entity would be placed. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaPlayer.html#LuaPlayer.can_place_entity) ---@field position MapPosition ----Direction the entity would be placed +---Direction the entity would be placed. Defaults to `north`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaPlayer.html#LuaPlayer.can_place_entity) ---@field direction? defines.direction @@ -744,7 +744,7 @@ set_goal_description=function(text,only_update)end, --- ---_Can only be used if this is InfinityContainer_ ---@param index uint@The index to set. ----@param filter InfinityInventoryFilter@The new filter or `nil` to clear the filter. +---@param filter InfinityInventoryFilter|nil@The new filter or `nil` to clear the filter. set_infinity_inventory_filter=function(index,filter)end, ---Sets the quick bar filter for the given slot. --- diff --git a/.vscode/factorio/runtime-api-LuaProfiler.lua b/.vscode/factorio/runtime-api-LuaProfiler.lua index 4945cfb..5f5843e 100644 --- a/.vscode/factorio/runtime-api-LuaProfiler.lua +++ b/.vscode/factorio/runtime-api-LuaProfiler.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaProfiler -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaProgrammableSpeakerControlBehavior.lua b/.vscode/factorio/runtime-api-LuaProgrammableSpeakerControlBehavior.lua index 00562ad..3781580 100644 --- a/.vscode/factorio/runtime-api-LuaProgrammableSpeakerControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaProgrammableSpeakerControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaProgrammableSpeakerControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRCON.lua b/.vscode/factorio/runtime-api-LuaRCON.lua index 65238f1..88275f4 100644 --- a/.vscode/factorio/runtime-api-LuaRCON.lua +++ b/.vscode/factorio/runtime-api-LuaRCON.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRCON -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRailChainSignalControlBehavior.lua b/.vscode/factorio/runtime-api-LuaRailChainSignalControlBehavior.lua index efe2d1c..49c1e73 100644 --- a/.vscode/factorio/runtime-api-LuaRailChainSignalControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaRailChainSignalControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRailChainSignalControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRailPath.lua b/.vscode/factorio/runtime-api-LuaRailPath.lua index 6271f2c..cb8198a 100644 --- a/.vscode/factorio/runtime-api-LuaRailPath.lua +++ b/.vscode/factorio/runtime-api-LuaRailPath.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRailPath -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRailSignalControlBehavior.lua b/.vscode/factorio/runtime-api-LuaRailSignalControlBehavior.lua index 8f9663c..5f0db48 100644 --- a/.vscode/factorio/runtime-api-LuaRailSignalControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaRailSignalControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRailSignalControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRandomGenerator.lua b/.vscode/factorio/runtime-api-LuaRandomGenerator.lua index 86a8425..fff18ad 100644 --- a/.vscode/factorio/runtime-api-LuaRandomGenerator.lua +++ b/.vscode/factorio/runtime-api-LuaRandomGenerator.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRandomGenerator -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRecipe.lua b/.vscode/factorio/runtime-api-LuaRecipe.lua index fd8435e..d0e7380 100644 --- a/.vscode/factorio/runtime-api-LuaRecipe.lua +++ b/.vscode/factorio/runtime-api-LuaRecipe.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRecipe -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRecipeCategoryPrototype.lua b/.vscode/factorio/runtime-api-LuaRecipeCategoryPrototype.lua index 8abf989..f5ee746 100644 --- a/.vscode/factorio/runtime-api-LuaRecipeCategoryPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaRecipeCategoryPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRecipeCategoryPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRecipePrototype.lua b/.vscode/factorio/runtime-api-LuaRecipePrototype.lua index 4ce6e79..d816896 100644 --- a/.vscode/factorio/runtime-api-LuaRecipePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaRecipePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRecipePrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRemote.lua b/.vscode/factorio/runtime-api-LuaRemote.lua index e29ff44..48a90c0 100644 --- a/.vscode/factorio/runtime-api-LuaRemote.lua +++ b/.vscode/factorio/runtime-api-LuaRemote.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRemote -- This file is automatically generated. Edits will be overwritten. @@ -26,7 +26,7 @@ ---``` ---@class LuaRemote:LuaObject ---[R] ----List of all registered interfaces. For each interface name, `remote.interfaces[name]` is a dictionary mapping the interface's registered functions to the value `true`. +---List of all registered interfaces. For each interface name, `remote.interfaces[name]` is a dictionary mapping the interface's registered functions to `true`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaRemote.html#LuaRemote.interfaces) --- @@ -36,7 +36,7 @@ ---game.player.print(tostring(remote.interfaces["human interactor"]["hello"])) -- prints true ---game.player.print(tostring(remote.interfaces["human interactor"]["nonexistent"])) -- prints nil ---``` ----@field interfaces {[string]: {[string]: boolean}} +---@field interfaces {[string]: {[string]: true}} ---[R] ---This object's name. --- @@ -45,20 +45,18 @@ remote={ ---Add a remote interface. --- ----**Note:** It is an error if the given interface `name` is already registered. ---- ---[View documentation](https://lua-api.factorio.com/latest/LuaRemote.html#LuaRemote.add_interface) ----@param name string@Name of the interface. +---@param name string@Name of the interface. If the name matches any existing interface, an error is thrown. ---@param functions {[string]: fun()}@List of functions that are members of the new interface. add_interface=function(name,functions)end, ---Call a function of an interface. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaRemote.html#LuaRemote.call) ---@param interface string@Interface to look up `function` in. ----@param function_ string@Function name that belongs to `interface`. +---@param function_ string@Function name that belongs to the `interface`. ---@vararg Any --- ----**vararg**: Arguments to pass to the called function. +---**vararg**: Arguments to pass to the called function. Note that any arguments passed through the interface are a copy of the original, not a reference. Metatables are not retained, while references to LuaObjects stay intact. ---@return Any? call=function(interface,function_,...)end, ---Removes an interface with the given name. diff --git a/.vscode/factorio/runtime-api-LuaRendering.lua b/.vscode/factorio/runtime-api-LuaRendering.lua index 6d18e2e..e3b0a3a 100644 --- a/.vscode/factorio/runtime-api-LuaRendering.lua +++ b/.vscode/factorio/runtime-api-LuaRendering.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRendering -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaResourceCategoryPrototype.lua b/.vscode/factorio/runtime-api-LuaResourceCategoryPrototype.lua index 3fdc6e3..0301113 100644 --- a/.vscode/factorio/runtime-api-LuaResourceCategoryPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaResourceCategoryPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaResourceCategoryPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaRoboportControlBehavior.lua b/.vscode/factorio/runtime-api-LuaRoboportControlBehavior.lua index 6c4e01f..056f586 100644 --- a/.vscode/factorio/runtime-api-LuaRoboportControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaRoboportControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaRoboportControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaSettings.lua b/.vscode/factorio/runtime-api-LuaSettings.lua index e6f8ed7..f0776eb 100644 --- a/.vscode/factorio/runtime-api-LuaSettings.lua +++ b/.vscode/factorio/runtime-api-LuaSettings.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaSettings -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaShortcutPrototype.lua b/.vscode/factorio/runtime-api-LuaShortcutPrototype.lua index 42d864c..feaa779 100644 --- a/.vscode/factorio/runtime-api-LuaShortcutPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaShortcutPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaShortcutPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaStorageTankControlBehavior.lua b/.vscode/factorio/runtime-api-LuaStorageTankControlBehavior.lua index d6cc3d5..e7bc55c 100644 --- a/.vscode/factorio/runtime-api-LuaStorageTankControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaStorageTankControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaStorageTankControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaStyle.lua b/.vscode/factorio/runtime-api-LuaStyle.lua index 6725367..2ac0080 100644 --- a/.vscode/factorio/runtime-api-LuaStyle.lua +++ b/.vscode/factorio/runtime-api-LuaStyle.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaStyle -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaSurface.lua b/.vscode/factorio/runtime-api-LuaSurface.lua index 3eb0e45..38aeb20 100644 --- a/.vscode/factorio/runtime-api-LuaSurface.lua +++ b/.vscode/factorio/runtime-api-LuaSurface.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaSurface -- This file is automatically generated. Edits will be overwritten. @@ -175,19 +175,19 @@ build_enemy_base=function(position,unit_count,force)end, calculate_tile_properties=function(property_names,positions)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_fast_replace) ---@class LuaSurface.can_fast_replace_param ----Name of the entity to check +---Name of the entity to check. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_fast_replace) ---@field name string ----Where the entity would be placed +---Where the entity would be placed. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_fast_replace) ---@field position MapPosition ----Direction the entity would be placed +---Direction the entity would be placed. Defaults to `north`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_fast_replace) ---@field direction? defines.direction ----The force that would place the entity. If not specified, the enemy force is assumed. +---The force that would place the entity. Defaults to the `"neutral"` force. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_fast_replace) ---@field force? ForceIdentification @@ -209,19 +209,19 @@ can_fast_replace=function(param)end, --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_place_entity) ---@field position MapPosition ----Direction of the placed entity. +---Direction of the placed entity. Defaults to `north`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_place_entity) ---@field direction? defines.direction ----The force that would place the entity. If not specified, the enemy force is assumed. +---The force that would place the entity. Defaults to the `"neutral"` force. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_place_entity) ---@field force? ForceIdentification ----Which type of check should be carried out. +---Which type of check should be carried out. Defaults to `ghost_revive`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_place_entity) ---@field build_check_type? defines.build_check_type ----If `true`, entities that can be marked for deconstruction are ignored. Only used if `build_check_type` is either `manual_ghost`, `script_ghost` or `blueprint_ghost`. +---If `true`, entities that can be marked for deconstruction are ignored. Only used if `build_check_type` is either `manual_ghost`, `script_ghost` or `blueprint_ghost`. Defaults to `false`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.can_place_entity) ---@field forced? boolean @@ -630,11 +630,17 @@ create_decoratives=function(param)end, ---Applies to **"beam"**: (optional) ---Absolute target position that can be used instead of target entity (entity has precedence if both entity and position are defined). --- +---Applies to **"stream"**: (optional) +---Absolute target position that can be used instead of target entity (entity has precedence if both entity and position are defined). +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.create_entity) ---@field target_position? MapPosition ---Applies to **"beam"**: (optional) ---Absolute source position that can be used instead of source entity (entity has precedence if both entity and position are defined). --- +---Applies to **"stream"**: (optional) +---Absolute source position that can be used instead of source entity (entity has precedence if both entity and position are defined). +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.create_entity) ---@field source_position? MapPosition ---Applies to **"beam"**: (optional) @@ -650,6 +656,9 @@ create_decoratives=function(param)end, ---Applies to **"beam"**: (optional) ---Source position will be offset by this value when rendering the beam. --- +---Applies to **"stream"**: (optional) +---Source position will be offset by this value when rendering the stream. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.create_entity) ---@field source_offset? Vector ---Applies to **"container"**: (optional) @@ -761,12 +770,16 @@ create_decoratives=function(param)end, ---@field frame_speed float ---Applies to **"projectile"**: (required) --- ----[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.create_entity) ----@field speed double ----Applies to **"projectile"**: (required) +---Applies to **"artillery-projectile"**: (required) --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.create_entity) ----@field max_range double +---@field speed double +---Applies to **"projectile"**: (optional) +--- +---Applies to **"artillery-projectile"**: (optional) +--- +---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.create_entity) +---@field max_range? double ---Applies to **"resource"**: (required) --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.create_entity) @@ -1435,9 +1448,9 @@ get_script_positions=function(name)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.get_starting_area_radius) ---@return double get_starting_area_radius=function()end, ----Get the tile at a given position. +---Get the tile at a given position. An alternative call signature for this method is passing it a single [TilePosition](https://lua-api.factorio.com/latest/Concepts.html#TilePosition). --- ----**Note:** The input position params can also be a single tile position. +---**Note:** Non-integer values will result in them being rounded down. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.get_tile) ---@param x int @@ -1451,8 +1464,12 @@ get_tile=function(x,y)end, get_total_pollution=function()end, ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.get_train_stops) ---@class LuaSurface.get_train_stops_param +---The name(s) of the train stops. Not providing names will match any stop. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.get_train_stops) ---@field name? string|string[] +---The force to search. Not providing a force will match stops in any force. +--- ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.get_train_stops) ---@field force? ForceIdentification @@ -1464,7 +1481,7 @@ get_total_pollution=function()end, ---@return LuaEntity[] get_train_stops=function(param)end, ---[View documentation](https://lua-api.factorio.com/latest/LuaSurface.html#LuaSurface.get_trains) ----@param force ForceIdentification?@If given only trains matching this force are returned. +---@param force ForceIdentification?@The force to search. Not providing a force will match trains in any force. ---@return LuaTrain[] get_trains=function(force)end, ---All methods and properties that this object supports. diff --git a/.vscode/factorio/runtime-api-LuaTechnology.lua b/.vscode/factorio/runtime-api-LuaTechnology.lua index 7f55426..4d07562 100644 --- a/.vscode/factorio/runtime-api-LuaTechnology.lua +++ b/.vscode/factorio/runtime-api-LuaTechnology.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTechnology -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTechnologyPrototype.lua b/.vscode/factorio/runtime-api-LuaTechnologyPrototype.lua index 864919a..fea2918 100644 --- a/.vscode/factorio/runtime-api-LuaTechnologyPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaTechnologyPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTechnologyPrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTile.lua b/.vscode/factorio/runtime-api-LuaTile.lua index 74938ff..e62c300 100644 --- a/.vscode/factorio/runtime-api-LuaTile.lua +++ b/.vscode/factorio/runtime-api-LuaTile.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTile -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTilePrototype.lua b/.vscode/factorio/runtime-api-LuaTilePrototype.lua index 5af5116..276b0b7 100644 --- a/.vscode/factorio/runtime-api-LuaTilePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaTilePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTilePrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTrain.lua b/.vscode/factorio/runtime-api-LuaTrain.lua index 7f596be..777e639 100644 --- a/.vscode/factorio/runtime-api-LuaTrain.lua +++ b/.vscode/factorio/runtime-api-LuaTrain.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTrain -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTrainStopControlBehavior.lua b/.vscode/factorio/runtime-api-LuaTrainStopControlBehavior.lua index 17c178f..690438c 100644 --- a/.vscode/factorio/runtime-api-LuaTrainStopControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaTrainStopControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTrainStopControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTransportBeltControlBehavior.lua b/.vscode/factorio/runtime-api-LuaTransportBeltControlBehavior.lua index c9ec661..2e9932d 100644 --- a/.vscode/factorio/runtime-api-LuaTransportBeltControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaTransportBeltControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTransportBeltControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTransportLine.lua b/.vscode/factorio/runtime-api-LuaTransportLine.lua index a0896f0..47a342d 100644 --- a/.vscode/factorio/runtime-api-LuaTransportLine.lua +++ b/.vscode/factorio/runtime-api-LuaTransportLine.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTransportLine -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaTrivialSmokePrototype.lua b/.vscode/factorio/runtime-api-LuaTrivialSmokePrototype.lua index e5b8bea..9e49f79 100644 --- a/.vscode/factorio/runtime-api-LuaTrivialSmokePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaTrivialSmokePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaTrivialSmokePrototype -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaUnitGroup.lua b/.vscode/factorio/runtime-api-LuaUnitGroup.lua index a695161..9f08d91 100644 --- a/.vscode/factorio/runtime-api-LuaUnitGroup.lua +++ b/.vscode/factorio/runtime-api-LuaUnitGroup.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaUnitGroup -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-LuaVirtualSignalPrototype.lua b/.vscode/factorio/runtime-api-LuaVirtualSignalPrototype.lua index 5ec9f2f..307cdab 100644 --- a/.vscode/factorio/runtime-api-LuaVirtualSignalPrototype.lua +++ b/.vscode/factorio/runtime-api-LuaVirtualSignalPrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaVirtualSignalPrototype -- This file is automatically generated. Edits will be overwritten. @@ -34,7 +34,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/LuaVirtualSignalPrototype.html#LuaVirtualSignalPrototype.order) ---@field order string ---[R] ----If this is a special signal +---Whether this is a special signal. The `everything`, `anything`, `each`, and `unknown` signals are considered special. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaVirtualSignalPrototype.html#LuaVirtualSignalPrototype.special) ---@field special boolean diff --git a/.vscode/factorio/runtime-api-LuaVoidEnergySourcePrototype.lua b/.vscode/factorio/runtime-api-LuaVoidEnergySourcePrototype.lua index 3b3a2f9..59feb92 100644 --- a/.vscode/factorio/runtime-api-LuaVoidEnergySourcePrototype.lua +++ b/.vscode/factorio/runtime-api-LuaVoidEnergySourcePrototype.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaVoidEnergySourcePrototype -- This file is automatically generated. Edits will be overwritten. @@ -10,7 +10,8 @@ --- ---[View documentation](https://lua-api.factorio.com/latest/LuaVoidEnergySourcePrototype.html) ---@class LuaVoidEnergySourcePrototype:LuaObject ----[R] +---[R] +---The emissions of this energy source in `pollution/Joule`. Multiplying it by energy consumption in `Watt` gives `pollution/second`. --- ---[View documentation](https://lua-api.factorio.com/latest/LuaVoidEnergySourcePrototype.html#LuaVoidEnergySourcePrototype.emissions) ---@field emissions double diff --git a/.vscode/factorio/runtime-api-LuaWallControlBehavior.lua b/.vscode/factorio/runtime-api-LuaWallControlBehavior.lua index 840c3df..122b046 100644 --- a/.vscode/factorio/runtime-api-LuaWallControlBehavior.lua +++ b/.vscode/factorio/runtime-api-LuaWallControlBehavior.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section LuaWallControlBehavior -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-builtin.lua b/.vscode/factorio/runtime-api-builtin.lua index f3e280a..3476613 100644 --- a/.vscode/factorio/runtime-api-builtin.lua +++ b/.vscode/factorio/runtime-api-builtin.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section builtin -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-concepts.lua b/.vscode/factorio/runtime-api-concepts.lua index b533422..5bf19b6 100644 --- a/.vscode/factorio/runtime-api-concepts.lua +++ b/.vscode/factorio/runtime-api-concepts.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section concepts -- This file is automatically generated. Edits will be overwritten. @@ -515,20 +515,41 @@ ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) ---@class CapsuleAction ----One of `"throw"`, `"equipment-remote"`, `"use-on-self"`. +---One of `"throw"`, `"equipment-remote"`, `"use-on-self"`, `"artillery-remote"`, `"destroy-cliffs"`. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) ---@field type string ----Only present when `type` is `"throw"` or `"use-on-self"`. +---Applies to **"artillery-remote"**: (required) +---Name of the [flare prototype](https://lua-api.factorio.com/latest/LuaEntityPrototype.html). --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) ----@field attack_parameters? AttackParameters ----Only present when `type` is `"equipment-remote"`. It is the equipment prototype name. ---- ----[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) ----@field equipment? string ----[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) ---@field flare? string +---Applies to **"throw"**: (required) +--- +---Applies to **"use-on-self"**: (required) +--- +---Applies to **"destroy-cliffs"**: (required) +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) +---@field attack_parameters AttackParameters +---Applies to **"throw"**: (required) +---Whether using the capsule consumes an item from the stack. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) +---@field uses_stack boolean +---Applies to **"equipment-remote"**: (required) +---Name of the [LuaEquipmentPrototype](https://lua-api.factorio.com/latest/LuaEquipmentPrototype.html). +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) +---@field equipment string +---Applies to **"destroy-cliffs"**: (required) +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) +---@field radius float +---Applies to **"destroy-cliffs"**: (required) +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CapsuleAction) +---@field timeout uint ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#ChartTagSpec) ---@class ChartTagSpec @@ -662,7 +683,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CliffPlacementSettings) ---@field richness MapGenSize ----A set of flags. Set flags are in the dictionary as `true`, while unset flags aren't present at all. +---A set of flags. Active flags are in the dictionary as `true`, while inactive flags aren't present at all. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#CollisionMask) ---@alias CollisionMask {[CollisionMaskLayer]: true} @@ -1163,7 +1184,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#EntityPrototypeFilter) ---@field crafting_category string ----A set of flags. Set flags are in the dictionary as `true`, while unset flags aren't present at all. +---A set of flags. Active flags are in the dictionary as `true`, while inactive flags aren't present at all. --- ---By default, none of these flags are set. --- @@ -1794,7 +1815,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#ItemPrototypeFilter) ---@field value uint ----A set of flags. Set flags are in the dictionary as `true`, while unset flags aren't present at all. +---A set of flags. Active flags are in the dictionary as `true`, while inactive flags aren't present at all. --- ---By default, none of these flags are set. --- @@ -2886,7 +2907,7 @@ ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#ModuleEffects) ---@field pollution? ModuleEffectValue ----A set of flags. Set flags are in the dictionary as `true`, while unset flags aren't present at all. +---A set of flags. Active flags are in the dictionary as `true`, while inactive flags aren't present at all. --- ---To write to this, use an array[[string](https://lua-api.factorio.com/latest/Builtin-Types.html#string)] of the mouse buttons that should be possible to use with on button. The flag `"left-and-right"` can also be set, which will set `"left"` and `"right"` to `true`. --- @@ -3144,7 +3165,7 @@ ---The amount of pollution eaten by a chunk's tiles as a percentage of 1. Defaults to `1`. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#PollutionMapSettings) ----@field aeging double +---@field ageing double ---Any amount of pollution larger than this value is visualized as this value instead. Defaults to `150`. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#PollutionMapSettings) @@ -3405,50 +3426,98 @@ ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectedPrototypeData) ---@field name string ----A set of flags. Set flags are in the dictionary as `true`, while unset flags aren't present at all. +---A set of flags on a selection tool that define how entities and tiles are selected. Active flags are in the dictionary as `true`, while inactive flags aren't present at all. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@class SelectionModeFlags ----Entities that can be selected for blueprint. +---Selects entities and tiles as if selecting them for a blueprint. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["blueprint"] true|nil ----Entities that can be marked for deconstruction. +---Selects entities and tiles as if selecting them for deconstruction. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["deconstruct"] true|nil ----Entities that can be marked for deconstruction cancelling. +---Selects entities and tiles as if selecting them for deconstruction cancellation. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["cancel-deconstruct"] true|nil ----@field ["item"] true|nil +---Selects items on the ground. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) +---@field ["items"] true|nil +---Selects trees. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["trees"] true|nil ----Buildable entities. +---Selects entities which are considered a [building](https://lua-api.factorio.com/latest/LuaEntityPrototype.html#LuaEntityPrototype.is_building), plus landmines. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["buildable-type"] true|nil ----Only select an area. +---Selects no entities or tiles, but is useful to select an area. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["nothing"] true|nil ----Entities that can be placed using an item. +---Selects entities and tiles that can be [built by an item](https://lua-api.factorio.com/latest/LuaItemPrototype.html#LuaItemPrototype.place_result). --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["items-to-place"] true|nil +---Selects all entities. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["any-entity"] true|nil +---Selects all tiles. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["any-tile"] true|nil ----Entities with the same force as the selector. +---Selects entities with the same force as the selecting player. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["same-force"] true|nil +---Selects entities with a different force as the selecting player. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["not-same-force"] true|nil +---Selects entities from a [friendly](https://lua-api.factorio.com/latest/LuaForce.html#LuaForce.is_friend) force. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["friend"] true|nil +---Selects entities from an [enemy](https://lua-api.factorio.com/latest/LuaForce.html#LuaForce.is_enemy) force. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["enemy"] true|nil +---Selects entities as if selecting them for upgrading. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["upgrade"] true|nil +---Selects entities as if selecting them for upgrade cancellation. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["cancel-upgrade"] true|nil +---Selects entities as if selecting them for downgrading. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) +---@field ["downgrade"] true|nil +---Selects entities that are [entities with health](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.is_entity_with_health). +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["entity-with-health"] true|nil +---Deprecated. Replaced by `is-military-target`. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["entity-with-force"] true|nil +---Selects entities that are [military targets](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.is_military_target). +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) +---@field ["is-military-target"] true|nil +---Selects entities that are [entities with owner](https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.is_entity_with_owner). +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) ---@field ["entity-with-owner"] true|nil +---Selects entities that are not `rolling-stocks`. +--- +---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#SelectionModeFlags) +---@field ["avoid-rolling-stock"] true|nil ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#Signal) ---@class Signal @@ -4004,11 +4073,11 @@ --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#WaitCondition) ---@field compare_type string ----Number of ticks to wait or of inactivity. Only present when `type` is `"time"` or `"inactivity"`. +---Number of ticks to wait when `type` is `"time"`, or number of ticks of inactivity when `type` is `"inactivity"`. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#WaitCondition) ---@field ticks? uint ----Only present when `type` is `"item_count"`, `"circuit"` or `"fluid_count"`. +---Only present when `type` is `"item_count"`, `"circuit"` or `"fluid_count"`, and a circuit condition is configured. --- ---[View documentation](https://lua-api.factorio.com/latest/Concepts.html#WaitCondition) ---@field condition? CircuitCondition diff --git a/.vscode/factorio/runtime-api-custom.lua b/.vscode/factorio/runtime-api-custom.lua index 40fd0b9..6f45bae 100644 --- a/.vscode/factorio/runtime-api-custom.lua +++ b/.vscode/factorio/runtime-api-custom.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section custom -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-defines.lua b/.vscode/factorio/runtime-api-defines.lua index b32a6fd..a01278a 100644 --- a/.vscode/factorio/runtime-api-defines.lua +++ b/.vscode/factorio/runtime-api-defines.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section defines -- This file is automatically generated. Edits will be overwritten. diff --git a/.vscode/factorio/runtime-api-events.lua b/.vscode/factorio/runtime-api-events.lua index 1ff3c62..b30991f 100644 --- a/.vscode/factorio/runtime-api-events.lua +++ b/.vscode/factorio/runtime-api-events.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section events -- This file is automatically generated. Edits will be overwritten. @@ -2059,7 +2059,9 @@ ---@field tick uint ---@alias on_player_driving_changed_state EventData.on_player_driving_changed_state ----Called when the player's driving state has changed, this means a player has either entered or left a vehicle. +---Called when the player's driving state has changed, meaning a player has either entered or left a vehicle. +--- +---**Note:** This event is not raised when the player is ejected from a vehicle due to it being destroyed. --- ---[View documentation](https://lua-api.factorio.com/latest/events.html#on_player_driving_changed_state) ---@class EventData.on_player_driving_changed_state : EventData @@ -3083,7 +3085,7 @@ ---@field tick uint ---@alias on_pre_player_mined_item EventData.on_pre_player_mined_item ----Called when the player finishes mining an entity, before the entity is removed from map. Can be filtered using [LuaPrePlayerMinedEntityEventFilter](https://lua-api.factorio.com/latest/Concepts.html#LuaPrePlayerMinedEntityEventFilter). +---Called when the player completes a mining action, but before the entity is potentially removed from the map. This is called even if the entity does not end up being removed. Can be filtered using [LuaPrePlayerMinedEntityEventFilter](https://lua-api.factorio.com/latest/Concepts.html#LuaPrePlayerMinedEntityEventFilter). --- ---[View documentation](https://lua-api.factorio.com/latest/events.html#on_pre_player_mined_item) ---@class EventData.on_pre_player_mined_item : EventData diff --git a/.vscode/factorio/runtime-api-global_functions.lua b/.vscode/factorio/runtime-api-global_functions.lua index 3e56ded..00d5115 100644 --- a/.vscode/factorio/runtime-api-global_functions.lua +++ b/.vscode/factorio/runtime-api-global_functions.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section global_functions -- This file is automatically generated. Edits will be overwritten. @@ -24,7 +24,7 @@ log=function(string)end ---end ---``` --- ----Note that `table_size()` does not work correctly for [LuaCustomTable](https://lua-api.factorio.com/latest/LuaCustomTable.html), their size has to be determined with [LuaCustomTable::length](https://lua-api.factorio.com/latest/LuaCustomTable.html#LuaCustomTable.length) instead. +---Note that `table_size()` does not work correctly for [LuaCustomTable](https://lua-api.factorio.com/latest/LuaCustomTable.html), their size has to be determined with [LuaCustomTable::length_operator](https://lua-api.factorio.com/latest/LuaCustomTable.html#LuaCustomTable.length_operator) instead. ---@param table table ---@return uint table_size=function(table)end diff --git a/.vscode/factorio/runtime-api-table_types.lua b/.vscode/factorio/runtime-api-table_types.lua index b0ca203..2ccef3e 100644 --- a/.vscode/factorio/runtime-api-table_types.lua +++ b/.vscode/factorio/runtime-api-table_types.lua @@ -1,7 +1,7 @@ ---@meta ---@diagnostic disable ---$Factorio 1.1.69 +--$Factorio 1.1.70 --$Overlay 5 --$Section table_types -- This file is automatically generated. Edits will be overwritten. diff --git a/cybersyn/control.lua b/cybersyn/control.lua index ed1cfee..2e2c4d4 100644 --- a/cybersyn/control.lua +++ b/cybersyn/control.lua @@ -5,4 +5,5 @@ require("scripts.global") require("scripts.controller") require("scripts.layout") require("scripts.gui") +require("scripts.alerts") require("scripts.main") diff --git a/cybersyn/graphics/icons/area-of-effect.png b/cybersyn/graphics/icons/area-of-effect.png new file mode 100644 index 0000000000000000000000000000000000000000..4815fb790c493dd8e079571f2f252f3958d26b4f GIT binary patch literal 1364 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV0u&)5>XPASgue|l%JNFld4cs zS&*ubT9KK?z)*4P?exH+I|@8)_bpxf8&p~zSR^DYGk@{%cXQZqgx^?y;eSH;b?!FLfYUd7 zYy0&#y#C94TRz}#MtNC|eWvv-{XD)@w=|Pmenr+oc5k=**86-@<71-LZ@KN7w<2x7 zH#_z$`@r72gS#`{-%k1Dp2$;M9-o|KFhOO3?xc@APV>JOPcPAD*%Rge>u2)qr+UX< zzA#(+W69-hllNWAU-@N*;78G!^2SCxoocS-Z!vMRPY-u0nV|j7^X{@uQ4$e~%h#M& z>UtQqC3kAiq?(WHD@=X1TE$;Hc_g4LV1ZjJ+t*O}5{07&=JJ|;KDa5zv&UGzNXcR0 z!Dj~+dNR#se!`NruV|sD`S}WtX#zV1-HZ-O>TcpcYAd}j%FaS6S+7`WzTVmi;^)7% znyBskc|?O@;YQP|OZ9hd{&fC2i?+fF?`JKXV)6dsp)dDsODPmv&*yyj-lddW>jsyO zwH|FOEHx^DT~B&%b}UF{n%{ILk||t3q4X4QWRXB;L-XB+8$XU^^rk+&-1C@KA!L!n zg@nZnVH{FQ-=Yr*Y5BP}uW~c>owe1aQ!%UO;v<{oU7f7K-D%TwqCF=$T@snRbV`xd z%*_uZf~O~Mn^SadsX_)P>prey;u2cnD;H+vya~kvM=N#>FA2%q={?zE5Z6d?C z?r56s);nIy`<7mDzAm@>>y=`C=Iq=P9M0}CW|vMZj=5rXwg1YzlYi$)Xv#e(yyQmurJa$mXo6I0);-6+0$YFERH_dLdgen2_cU$j_ zFDefB?!WV+c2VA<-{Md1xO{hbXS`kfN>t?WwK@*P;!eSHCN#ZmcWf1|`|$cn_SVn! zFCV;pfeiN-sW8{ur2Mh|D*Sfe#G4nCw`4IeFt%noI|q0=JHt{tFpbZtooMTE*g@uK zeDu;FttK&%mlGDgN^%T261XaYg?s7R3#*z>E{(siMA5UbP$Y(%uTCdSHfBy~X@Mxq zL4E;ub#+&9&5!k-3tfBIkN>SGe_!~W?}M3^kKryg23OOsAvS6$#jBhCi!!!c>^J1; zI&(qj3VsV*~r%&CZ3 z&MjnL6kGJ8xA0c6ze|9@;&WpT%eA-nT#+vg7rr!D%E`8vQ`pd7|#4MS9 zZENg)y-n>c<8Nx&Z@*^eC*QzRy0wN`Muusz z>#4Z%%5wko{QvVZzkX-lmQ{4<(G8|oz*2}U$=lrpNCUx7=iT){inG8YvKUxkT?b)C zCym(^Ktc8rPhVH|XIwHu>YBHc|E~iIC3(6yh5$>b3yh2m3@pqBU*6Z)-cVXId5PTY nD!&E>MkW>x0R;yN7%>|{typT-*61VxZDa6s^>bP0l+XkKbn8-! literal 0 HcmV?d00001 diff --git a/cybersyn/graphics/icons/lost-train.png b/cybersyn/graphics/icons/lost-train.png index 3f4233236012a0c3d07b99909145f407a013676f..efa415f9399ea4af89c44b88ced1cf6b810b2feb 100644 GIT binary patch delta 1165 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 diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index e9d08e4..1a48818 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -1,6 +1,6 @@ --By Mami combinator_entity = flib.copy_prototype(data.raw["arithmetic-combinator"]["arithmetic-combinator"], COMBINATOR_NAME) -combinator_entity.icon = "__cybersyn__/graphics/icons/combinator.png" +combinator_entity.icon = "__cybersyn__/graphics/icons/area-of-effect.png" combinator_entity.radius_visualisation_specification = { sprite = { filename = "__cybersyn__/graphics/icons/combinator.png", @@ -10,6 +10,9 @@ combinator_entity.radius_visualisation_specification = { }, distance = 1, } +combinator_entity.active_energy_usage = "10KW" +combinator_entity.allow_copy_paste = false + combinator_out_entity = flib.copy_prototype(data.raw["constant-combinator"]["constant-combinator"], COMBINATOR_OUT_NAME) combinator_out_entity.icon = nil diff --git a/cybersyn/prototypes/item.lua b/cybersyn/prototypes/item.lua index 428db83..a2c20b0 100644 --- a/cybersyn/prototypes/item.lua +++ b/cybersyn/prototypes/item.lua @@ -3,4 +3,4 @@ combinator_item = flib.copy_prototype(data.raw["item"]["arithmetic-combinator"], combinator_item.icon = "__cybersyn__/graphics/icons/combinator.png" combinator_item.icon_size = 64 combinator_item.icon_mipmaps = 4 -combinator_item.order = combinator_item.order.."-c" +combinator_item.order = data.raw["item"]["decider-combinator"].order.."-b" diff --git a/cybersyn/scripts/controller.lua b/cybersyn/scripts/controller.lua index e277e6d..653e61e 100644 --- a/cybersyn/scripts/controller.lua +++ b/cybersyn/scripts/controller.lua @@ -236,7 +236,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, train, p local item_type = v.signal.type if item_name and item_type and item_type ~= "virtual" then local effective_item_count = item_count + (p_station.deliveries[item_name] or 0) - local r_threshold, p_threshold = get_thresholds(map_data, r_station, v.signal) + local r_threshold, p_threshold = get_thresholds(map_data, p_station, v.signal) if effective_item_count >= p_threshold then local r = requests[item_name] if r then @@ -332,6 +332,7 @@ end function tick(map_data, mod_settings) local total_ticks = map_data.total_ticks local stations = map_data.stations + ---@type Economy local economy = { r_stations_all = {}, p_stations_all = {}, @@ -442,7 +443,7 @@ function tick(map_data, mod_settings) if best_train then send_train_between(map_data, r_station_id, p_stations[best], best_train, item_name, economy) elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, p_stations[best].entity_stop) + send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) end until #r_stations == 0 else @@ -475,7 +476,7 @@ function tick(map_data, mod_settings) if best_train then send_train_between(map_data, r_stations[best], p_station_id, best_train, item_name, economy) elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[best].entity_stop, p_stations[p_station_id].entity_stop) + send_missing_train_alert_for_stops(stations[r_stations[best]].entity_stop, stations[p_station_id].entity_stop) end until #p_stations == 0 end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 569b43b..22b77f0 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -68,5 +68,5 @@ global.layouts = {} global.layout_train_count = {} global.layout_top_id = 1 global.train_classes = { - [TRAIN_CLASS_ALL] = {}, + --[TRAIN_CLASS_ALL.name] = {}, } diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 6831692..0111f79 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -15,7 +15,7 @@ function remove_train(map_data, train, train_id) for station_id, station in pairs(map_data.stations) do station.accepted_layouts[layout_id] = nil end - map_data.train_classes[TRAIN_CLASS_ALL][layout_id] = nil + --map_data.train_classes[TRAIN_CLASS_ALL][layout_id] = nil else map_data.layout_train_count[layout_id] = count - 1 end @@ -64,7 +64,7 @@ function update_train_layout(map_data, train) station.accepted_layouts[layout_id] = true end end - map_data.train_classes[TRAIN_CLASS_ALL][layout_id] = true + --map_data.train_classes[TRAIN_CLASS_ALL][layout_id] = true else map_data.layout_train_count[layout_id] = map_data.layout_train_count[layout_id] + 1 end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 4baf7f6..5590e82 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -214,7 +214,7 @@ local function on_combinator_broken(map_data, comb) station.entity_comb1 = comb1 else on_station_broken(map_data, stop.unit_number, station) - map_data.depots[stop.unit_number] = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, nil) + map_data.depots[stop.unit_number] = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) end elseif station.entity_comb2 == comb then station.entity_comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) @@ -290,7 +290,7 @@ local function on_stop_broken(map_data, stop) } local entities = stop.surface.find_entities(search_area) for _, entity in pairs(entities) do - if map_data.to_stop[entity.unit_number] == stop then + if entity.valid and map_data.to_stop[entity.unit_number] == stop then map_data.to_stop[entity.unit_number] = nil end end @@ -307,7 +307,7 @@ local function on_station_rename(map_data, stop) --search for trains coming to the renamed station local station_id = stop.unit_number local station = map_data.stations[station_id] - if station.deliveries_total > 0 then + if station and station.deliveries_total > 0 then for train_id, train in pairs(map_data.trains) do local is_p = train.p_station_id == station_id local is_r = train.r_station_id == station_id @@ -397,8 +397,9 @@ end ---@param stop LuaEntity ---@param train Train local function on_train_arrives_buffer(map_data, stop, train) - local station_id = stop.unit_number if train.manifest then + ---@type uint + local station_id = stop.unit_number if train.status == STATUS_D_TO_P then if train.p_station_id == station_id then train.status = STATUS_P @@ -408,7 +409,7 @@ local function on_train_arrives_buffer(map_data, stop, train) for i, item in ipairs(train.manifest) do signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item.count} end - set_combinator_output(map_data, station.comb1, signals) + set_combinator_output(map_data, station.entity_comb1, signals) if station.wagon_combs then for i, entity in ipairs(station.wagon_combs) do @@ -424,7 +425,7 @@ local function on_train_arrives_buffer(map_data, stop, train) for i, item in ipairs(train.manifest) do signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = -1} end - set_combinator_output(map_data, station.comb1, signals) + set_combinator_output(map_data, station.entity_comb1, signals) end else on_failed_delivery(map_data, train) @@ -547,7 +548,7 @@ local function on_train_changed(event) local train = global.trains[train_e.id] if train_e.state == defines.train_state.wait_station then local stop = train_e.station - if stop and stop.name == "train-stop" then + if stop and stop.valid and stop.name == "train-stop" then if global.stations[stop.unit_number] then on_train_arrives_buffer(global, stop, train) elseif global.depots[stop.unit_number] then @@ -573,6 +574,15 @@ local function on_surface_removed(event) end end +local function on_paste(event) + local entity = event.destination + if not entity or not entity.valid then return end + + if entity.name == COMBINATOR_NAME then + on_combinator_updated(global, entity) + end +end + local filter_built = { {filter = "type", type = "train-stop"}, @@ -589,6 +599,9 @@ local filter_broken = { {filter = "type", type = "straight-rail"}, {filter = "rolling-stock"}, } +local filter_comb = { + {filter = "type", type = "arithmetic-combinator"}, +} local function register_events() --NOTE: I have no idea if this correctly registers all events once in all situations flib_event.register(defines.events.on_built_entity, on_built, filter_built) @@ -602,6 +615,8 @@ local function register_events() flib_event.register({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed) + flib_event.register(defines.events.on_entity_settings_pasted, on_paste) + local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nth_tick, on_tick) From 0e487575757813e0d3279d5413bfef8d8d399e6c Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 28 Oct 2022 14:42:01 -0400 Subject: [PATCH 04/66] fixed typo --- cybersyn/prototypes/entity.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index 1a48818..f9c71c6 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -1,9 +1,9 @@ --By Mami combinator_entity = flib.copy_prototype(data.raw["arithmetic-combinator"]["arithmetic-combinator"], COMBINATOR_NAME) -combinator_entity.icon = "__cybersyn__/graphics/icons/area-of-effect.png" +combinator_entity.icon = "__cybersyn__/graphics/icons/combinator.png" combinator_entity.radius_visualisation_specification = { sprite = { - filename = "__cybersyn__/graphics/icons/combinator.png", + filename = "__cybersyn__/graphics/icons/area-of-effect.png", tint = {r = 1, g = 1, b = .25, a = 1}, height = 64, width = 64, From 202ae86c95c4f4660abdcddc80b5b2598f2fbca4 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 30 Oct 2022 00:30:26 -0400 Subject: [PATCH 05/66] fixed art --- cybersyn/TODO | 4 +++ cybersyn/graphics/icons/area-of-effect.png | Bin 1364 -> 1363 bytes cybersyn/prototypes/entity.lua | 5 ++-- cybersyn/scripts/gui.lua | 31 +++++++++++++++++---- 4 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 cybersyn/TODO diff --git a/cybersyn/TODO b/cybersyn/TODO new file mode 100644 index 0000000..bebcb0a --- /dev/null +++ b/cybersyn/TODO @@ -0,0 +1,4 @@ +finish wagon manifest +add rail networks +figure out how to make the area-of-effect graphic the correct size +close gui when the combinator is destroyed diff --git a/cybersyn/graphics/icons/area-of-effect.png b/cybersyn/graphics/icons/area-of-effect.png index 4815fb790c493dd8e079571f2f252f3958d26b4f..38e64b34addf1e47ed00e6166eff38b3b8b16d2b 100644 GIT binary patch delta 671 zcmV;Q0$}~r3eyUZ0StUwVw3z$F|E|j?>X?TJcGzZ6|%L-%{x@ zBjPJEKW1cdh4`%BXlaao&aQ;$AXnUzx=m%ZFt9?-Q;mpXxD{<%C8H!?`~hkWQ?1Td zbTToN9Tcchp+SAs2OUT;aA!l!e6TEA664H=Oax&T8f*?6nr9@iIY3Te<|`Lkar2Ca zUCy^$@mya>W0IRsi~@7{!wgX6n*otIGK)^>VY_rM%Jpq6F!I9c^*olY$ z00D$)LqkwWLqi~Na&Km7Y-Iodc$|HaJxIeq9K~N#rBW&m?I6-2Lv^xHREnciu?QAQ zTcK44lS{v#NlTLA;wZQl9Q;_UI=DFN>fkB}f*&A`PELw0QsV!TLW>v=j{EWM-sA2a zAhc^tH3njUs+oEwnGk>Tg;mk_3O~XKB8Ircj7BQEn1k#1x`&6acTt|@e(ujPtP~9f zctqkk*3eDj4dVEw+2FiS9AXtoAwDOb)aio6k6f1>e&bxWS>Tx=J(rm$4iSsxK9>8K zRdkhjhB%_A8s!UlhZW9SoYh*Bb??bv7$|BhS+3IzBZ(!XkcNK<8BJ7CgNZ1u8Yu=c zw4ZSCkJS>W9U}PJ(xbA849&ot>j6O9aU9u%FO`%i*-p}Zp3PA7{@UJ<& zb@y@l0OY8v1TzN{F%9}U#GjMe F1bT74JyHMw delta 672 zcmV;R0$=^p3e*aamvk=#yjoBC`D5GVF30KUHm&%i)3%eo)^Dlw zm=W<6nIAJUxk7x_Z?rTxH+is4qYZIz6YeDMdUHB7ZS zU(w0LP87;C8#XTFoT0S@$V=i+dpDwMC7ti&DG$y(E#3%rvu{G7DFS)bw&({Pi zAgD{`gb5a3PZw3`zP4m>7OxIN!`{+lZ99NOh^*F8LZy^HcJ_j7-aUO8_t zz#|aPF-fnfRP|T&D{XKXP4i_>FVXW-reS>6z3#afnzbcCggJ ztfVW%)5H;3Q7PY>by()S#aXG;SnHnrg~7bKlIA+iFydH30!e=ekWfPz6_|+9s*++L zMf))q|FG>(l1n028H^kYs6v5g`@#RQmeo7~ ze9yp@+V)o)!0adK^|lr{0{XUri|e)~?E#lNz|fN>>Y^?AXnG3;;QfrgDF+PP0)aKB zx7I#RAAk&XwR{`{92^3pMao`xd3R4|Z~vZY_4fm|YI4Hj*aGU4p9C`p5+^C!WB;y` G*#vqo&pFBf diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index f9c71c6..099306a 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -4,11 +4,12 @@ combinator_entity.icon = "__cybersyn__/graphics/icons/combinator.png" combinator_entity.radius_visualisation_specification = { sprite = { filename = "__cybersyn__/graphics/icons/area-of-effect.png", - tint = {r = 1, g = 1, b = .25, a = 1}, + tint = {r = 1, g = 1, b = 0, a = .5}, height = 64, width = 64, }, - distance = 1, + --offset = {0, .5}, + distance = 1.5, } combinator_entity.active_energy_usage = "10KW" combinator_entity.allow_copy_paste = false diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index cdea2fa..309adbf 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -52,10 +52,10 @@ function gui_opened(comb, player) on_click = {"close", comb.unit_number} }} }}, - {type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=8}, children={ + {type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=12}, children={ {type="flow", direction="vertical", style_mods={horizontal_align="left"}, children={ --status - {type="flow", style = "status_flow", direction = "horizontal", style_mods={vertical_align="center", horizontally_stretchable=true}, children={ + {type="flow", style = "status_flow", direction = "horizontal", style_mods={vertical_align="center", horizontally_stretchable=true, bottom_padding=4}, children={ {type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}}, {type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}} }}, @@ -63,15 +63,22 @@ function gui_opened(comb, player) {type="frame", style="deep_frame_in_shallow_frame", style_mods={minimal_width=0, horizontally_stretchable=true, padding=0}, children={ {type="entity-preview", style="wide_entity_button", ref={"preview"}}, }}, - {type="label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=8}}, - {type="drop-down", ref={"operation"}, actions={ - on_selection_state_changed = {"drop-down", comb.unit_number} + --drop down + {type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=8}}, + {type="drop-down", style_mods={top_padding=3}, ref={"operation"}, actions={ + on_selection_state_changed={"drop-down", comb.unit_number} }, selected_index=selected_index, items={ {"cybersyn-gui.comb1"}, {"cybersyn-gui.comb2"}, {"cybersyn-gui.depot"}, {"cybersyn-gui.wagon-manifest"}, }}, + ---choose-elem-button + {type="line", style_mods={top_padding=10}}, + {type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=7}}, + {type="choose-elem-button", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={ + on_elem_changed={"choose-elem-button", comb.unit_number} + }}, }} }} }} @@ -139,6 +146,20 @@ function register_gui_actions() end a.parameters = control on_combinator_updated(global, comb) + elseif msg[1] == "choose-elem-button" then + local element = event.element + if not element then return end + local comb = global.to_comb[msg[2]] + if not comb or not comb.valid then return end + + local signal = element.elem_value + + local a = comb.get_or_create_control_behavior() + local control = a.parameters + + control.first_signal = signal + + a.parameters = control end end end) From bb596ff73369fc4d3d0a95960e41a21a25c2146f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 30 Oct 2022 12:36:58 -0400 Subject: [PATCH 06/66] beginning to add depot logic --- cybersyn/TODO | 3 + cybersyn/control.lua | 2 +- cybersyn/locale/en/base.cfg | 3 +- .../{controller.lua => central-planning.lua} | 111 +++++++++++++----- cybersyn/scripts/constants.lua | 2 + cybersyn/scripts/global.lua | 14 ++- cybersyn/scripts/gui.lua | 4 +- cybersyn/scripts/main.lua | 2 +- 8 files changed, 101 insertions(+), 40 deletions(-) rename cybersyn/scripts/{controller.lua => central-planning.lua} (79%) diff --git a/cybersyn/TODO b/cybersyn/TODO index bebcb0a..1db0f42 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -2,3 +2,6 @@ finish wagon manifest add rail networks figure out how to make the area-of-effect graphic the correct size close gui when the combinator is destroyed +Improve localization +support space elevator +add an "all" train class diff --git a/cybersyn/control.lua b/cybersyn/control.lua index 2e2c4d4..de63f60 100644 --- a/cybersyn/control.lua +++ b/cybersyn/control.lua @@ -2,7 +2,7 @@ require("scripts.constants") require("scripts.global") -require("scripts.controller") +require("scripts.central-planning") require("scripts.layout") require("scripts.gui") require("scripts.alerts") diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index e432a4d..793811b 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -40,7 +40,8 @@ lost-train=A train has become lost nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty [cybersyn-gui] -operation=Choose combinator type +operation=Combinator type +network=Network comb1=Primary controller comb2=Secondary station control depot=Depot diff --git a/cybersyn/scripts/controller.lua b/cybersyn/scripts/central-planning.lua similarity index 79% rename from cybersyn/scripts/controller.lua rename to cybersyn/scripts/central-planning.lua index 653e61e..628b146 100644 --- a/cybersyn/scripts/controller.lua +++ b/cybersyn/scripts/central-planning.lua @@ -2,6 +2,8 @@ local get_distance = require("__flib__.misc").get_distance local math = math local INF = math.huge +local btest = bit32.btest +local band = bit32.band local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ---@param stop LuaEntity @@ -72,6 +74,33 @@ local function get_signals(station) end end +---@param depot Depot +local function set_depot_signals(depot) + local comb = depot.entity_comb + if depot.network_name and comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then + depot.priority = 0 + depot.network_flag = 1 + local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) + if signals then + for k, v in pairs(signals) do + local item_name = v.signal.name + local item_count = v.count + if item_name then + if item_name == SIGNAL_PRIORITY then + depot.priority = item_count + end + if item_name == depot.network_name then + depot.network_flag = item_count + end + end + end + end + else + depot.priority = 0 + depot.network_flag = 0 + end +end + ---@param map_data MapData ---@param comb LuaEntity ---@param signals ConstantCombinatorParameters[]? @@ -144,7 +173,7 @@ end ---@param station Station ---@param layout_id uint local function station_accepts_layout(station, layout_id) - return true + return station.accepted_layouts[layout_id] end @@ -157,9 +186,12 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type) --NOTE: this code is the critical section for run-time optimization local r_station = map_data.stations[r_station_id] local p_station = map_data.stations[p_station_id] + ---@type string + local network_name = p_station.network_name local p_to_r_dist = get_stop_dist(p_station.entity_stop, r_station.entity_stop) - if p_to_r_dist == INF then + local netand = band(p_station.network_flag, r_station.network_flag) + if p_to_r_dist == INF or netand == 0 then return nil, INF end @@ -168,20 +200,22 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type) local valid_train_exists = false local is_fluid = item_type == "fluid" - for train_id, _ in pairs(map_data.trains_available) do + for depot_id, train_id in pairs(map_data.trains_available[network_name]) do + local depot = map_data.depots[depot_id] local train = map_data.trains[train_id] --check cargo capabilities --check layout validity for both stations if - ((is_fluid and train.fluid_capacity > 0) or (not is_fluid and train.item_slot_capacity > 0)) - and station_accepts_layout(r_station, train.layout_id) - and station_accepts_layout(p_station, train.layout_id) - and train.entity.station + depot.network_name == network_name and + btest(netand, depot.network_flag) + ((is_fluid and train.fluid_capacity > 0) or (not is_fluid and train.item_slot_capacity > 0)) and + station_accepts_layout(r_station, train.layout_id) and + station_accepts_layout(p_station, train.layout_id) then valid_train_exists = true --check if exists valid path --check if path is shortest so we prioritize locality - local d_to_p_dist = get_stop_dist(train.entity.station, p_station.entity_stop) + local d_to_p_dist = get_stop_dist(depot.entity_stop, p_station.entity_stop) - DEPOT_PRIORITY_MULT*depot.priority local dist = d_to_p_dist if dist < best_dist then @@ -206,6 +240,7 @@ end ---@param primary_item_name string ---@param economy Economy local function send_train_between(map_data, r_station_id, p_station_id, train, primary_item_name, economy) + --trains and stations expected to be of the same network local r_station = map_data.stations[r_station_id] local p_station = map_data.stations[p_station_id] @@ -301,8 +336,9 @@ local function send_train_between(map_data, r_station_id, p_station_id, train, p r_station.deliveries[item.name] = (r_station.deliveries[item.name] or 0) + item.count p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count - local r_stations = economy.r_stations_all[item.name] - local p_stations = economy.p_stations_all[item.name] + local item_network_name = (r_station.network_name and item.name + ":" + r_station.network_name) or item.name + local r_stations = economy.r_stations_all[item_network_name] + local p_stations = economy.p_stations_all[item_network_name] for i, id in ipairs(r_stations) do if id == r_station_id then table.remove(r_stations, i) @@ -336,26 +372,30 @@ function tick(map_data, mod_settings) local economy = { r_stations_all = {}, p_stations_all = {}, - all_items = {}, total_ticks = total_ticks, } local r_stations_all = economy.r_stations_all local p_stations_all = economy.p_stations_all - local all_items = economy.all_items + local all_names = {} + + for depot_id, _ in pairs(map_data.trains_available) do + set_depot_signals(map_data.depots[depot_id]) + end for station_id, station in pairs(stations) do - if station.deliveries_total < station.entity_stop.trains_limit then + if station.network_name and station.deliveries_total < station.entity_stop.trains_limit then station.r_threshold = mod_settings.r_threshold station.p_threshold = mod_settings.p_threshold station.priority = 0 station.locked_slots = 0 + station.network_flag = 1 local signals = get_signals(station) if signals then for k, v in pairs(signals) do local item_name = v.signal.name local item_count = v.count local item_type = v.signal.type - if item_name and item_type then + if item_name then if item_type == "virtual" then if item_name == SIGNAL_PRIORITY then station.priority = item_count @@ -368,6 +408,9 @@ function tick(map_data, mod_settings) end signals[k] = nil end + if item_name == station.network_name then + station.network_flag = item_count + end else signals[k] = nil end @@ -380,19 +423,21 @@ function tick(map_data, mod_settings) if item_name then if -effective_item_count >= r_threshold then - if r_stations_all[item_name] == nil then - r_stations_all[item_name] = {} - p_stations_all[item_name] = {} - all_items[#all_items + 1] = item_name - all_items[#all_items + 1] = v.signal.type + local item_network_name = item_name + ":" + station.network_name + if r_stations_all[item_network_name] == nil then + r_stations_all[item_network_name] = {} + p_stations_all[item_network_name] = {} + all_names[#all_names + 1] = item_network_name + all_names[#all_names + 1] = v.signal end table.insert(r_stations_all[item_name], station_id) elseif effective_item_count >= p_threshold then - if r_stations_all[item_name] == nil then - r_stations_all[item_name] = {} - p_stations_all[item_name] = {} - all_items[#all_items + 1] = item_name - all_items[#all_items + 1] = v.signal.type + local item_network_name = item_name + ":" + station.network_name + if r_stations_all[item_network_name] == nil then + r_stations_all[item_network_name] = {} + p_stations_all[item_network_name] = {} + all_names[#all_names + 1] = item_network_name + all_names[#all_names + 1] = v.signal end table.insert(p_stations_all[item_name], station_id) end @@ -402,17 +447,19 @@ function tick(map_data, mod_settings) end end - local failed_because_missing_trains = {} --we do not dispatch more than one train per station per tick --psuedo-randomize what item (and what station) to check first so if trains available is low they choose orders psuedo-randomly - local start_i = 2*(total_ticks%(#all_items/2)) + 1 - for item_i = 0, #all_items - 1, 2 do - local item_name = all_items[(start_i + item_i - 1)%#all_items + 1] - local item_type = all_items[(start_i + item_i)%#all_items + 1] - local r_stations = r_stations_all[item_name] - local p_stations = p_stations_all[item_name] + --NOTE: It may be better for performance to update stations one tick at a time rather than all at once, however this does mean more redundant data will be generated and discarded each tick. Once we have a performance test-bed it will probably be worth checking. + local start_i = 2*(total_ticks%(#all_names/2)) + 1 + for item_i = 0, #all_names - 1, 2 do + local item_network_name = all_names[(start_i + item_i - 1)%#all_names + 1] + local signal = all_names[(start_i + item_i)%#all_names + 1] + local item_name = signal.name + local item_type = signal.type + local r_stations = r_stations_all[item_network_name] + local p_stations = p_stations_all[item_network_name] - --NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm (and run it twice to compare the locality solution to the round-robin solution) but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic + --NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic if #r_stations > 0 and #p_stations > 0 then if #r_stations <= #p_stations then --probably backpressure, prioritize locality diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 6b0e4a9..5bd4ca6 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -19,6 +19,8 @@ OPERATION_WAGON_MANIFEST = "-" DELTA = 1/2048 +DEPOT_PRIORITY_MULT = 2048 + STATUS_D = 0 STATUS_D_TO_P = 1 STATUS_P = 2 diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 22b77f0..e4f70d6 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -6,9 +6,9 @@ ---@field public to_output {[uint]: LuaEntity} ---@field public to_stop {[uint]: LuaEntity} ---@field public stations {[uint]: Station} ----@field public depots {[uint]: LuaEntity} +---@field public depots {[uint]: Depot} ---@field public trains {[uint]: Train} ----@field public trains_available {[uint]: boolean} +---@field public trains_available {[string]: {[uint]: uint}} ---@field public layouts {[uint]: string} ---@field public layout_train_count {[uint]: int} ---@field public train_classes {[string]: TrainClass} @@ -25,10 +25,19 @@ ---@field public entity_comb2 LuaEntity? ---@field public wagon_combs {[int]: LuaEntity}?--NOTE: allowed to be invalid entities or combinators with the wrong operation, these must be checked and lazy deleted when found ---@field public deliveries {[string]: int} +---@field public network_name string? +---@field public network_flag int ---@field public train_class SignalID? ---@field public accepted_layouts TrainClass ---@field public layout_pattern string? +---@class Depot +---@field public priority int +---@field public entity_stop LuaEntity +---@field public entity_comb LuaEntity +---@field public network_name string? +---@field public network_flag int + ---@class Train ---@field public entity LuaTrain ---@field public layout_id uint @@ -47,7 +56,6 @@ ---@class Economy ---@field public r_stations_all {[string]: uint[]} ---@field public p_stations_all {[string]: uint[]} ----@field public all_items string[] ---@field public total_ticks uint --TODO: only init once diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 309adbf..c2f950a 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -75,8 +75,8 @@ function gui_opened(comb, player) }}, ---choose-elem-button {type="line", style_mods={top_padding=10}}, - {type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=7}}, - {type="choose-elem-button", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={ + {type="label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, + {type="choose-elem-button", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={ on_elem_changed={"choose-elem-button", comb.unit_number} }}, }} diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 5590e82..b00198b 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -354,7 +354,7 @@ local function on_train_arrives_depot(map_data, train_entity) train.manifest = nil train.depot_name = train_entity.station.backer_name train.status = STATUS_D - map_data.trains_available[train_entity.id] = true + map_data.trains_available[][train_entity.id] = true else if train.manifest then on_failed_delivery(map_data, train) From 50bfc20b084c120278f3e941e7564e3004720119 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 30 Oct 2022 19:21:11 -0400 Subject: [PATCH 07/66] implemented networks --- cybersyn/scripts/central-planning.lua | 81 +++++++++++----------- cybersyn/scripts/constants.lua | 10 +-- cybersyn/scripts/global.lua | 15 ++-- cybersyn/scripts/gui.lua | 26 +++++-- cybersyn/scripts/main.lua | 98 +++++++++++++++++++++------ 5 files changed, 153 insertions(+), 77 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 628b146..34cecb8 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -247,41 +247,38 @@ local function send_train_between(map_data, r_station_id, p_station_id, train, p local requests = {} local manifest = {} - local r_signals = get_signals(r_station) + local r_signals = r_station.tick_signals if r_signals then for k, v in pairs(r_signals) do + ---@type string local item_name = v.signal.name local item_count = v.count - local item_type = v.signal.type - if item_name and item_type and item_type ~= "virtual" then - local effective_item_count = item_count + (r_station.deliveries[item_name] or 0) - local r_threshold, p_threshold = get_thresholds(map_data, r_station, v.signal) - if -effective_item_count >= r_threshold then - requests[item_name] = -effective_item_count - end + --local item_type = v.signal.type + local effective_item_count = item_count + (r_station.deliveries[item_name] or 0) + local r_threshold, p_threshold = get_thresholds(map_data, r_station, v.signal) + if -effective_item_count >= r_threshold then + requests[item_name] = -effective_item_count end end end - local p_signals = get_signals(p_station) + local p_signals = p_station.tick_signals if p_signals then for k, v in pairs(p_signals) do local item_name = v.signal.name local item_count = v.count local item_type = v.signal.type - if item_name and item_type and item_type ~= "virtual" then - local effective_item_count = item_count + (p_station.deliveries[item_name] or 0) - local r_threshold, p_threshold = get_thresholds(map_data, p_station, v.signal) - if effective_item_count >= p_threshold then - local r = requests[item_name] - if r then - local item = {name = item_name, count = math.min(r, effective_item_count), type = item_type} - if item_name == primary_item_name then - manifest[#manifest + 1] = manifest[1] - manifest[1] = item - else - manifest[#manifest + 1] = item - end + local effective_item_count = item_count + (p_station.deliveries[item_name] or 0) + local r_threshold, p_threshold = get_thresholds(map_data, p_station, v.signal) + if effective_item_count >= p_threshold then + local r = requests[item_name] + if r then + local item = {name = item_name, type = item_type, count = math.min(r, effective_item_count)} + if item_name == primary_item_name then + manifest[#manifest + 1] = manifest[1] + manifest[1] = item + else + manifest[#manifest + 1] = item end end end @@ -390,6 +387,7 @@ function tick(map_data, mod_settings) station.locked_slots = 0 station.network_flag = 1 local signals = get_signals(station) + station.tick_signals = signals if signals then for k, v in pairs(signals) do local item_name = v.signal.name @@ -421,26 +419,24 @@ function tick(map_data, mod_settings) local effective_item_count = item_count + (station.deliveries[item_name] or 0) local r_threshold, p_threshold = get_thresholds(map_data, station, v.signal) - if item_name then - if -effective_item_count >= r_threshold then - local item_network_name = item_name + ":" + station.network_name - if r_stations_all[item_network_name] == nil then - r_stations_all[item_network_name] = {} - p_stations_all[item_network_name] = {} - all_names[#all_names + 1] = item_network_name - all_names[#all_names + 1] = v.signal - end - table.insert(r_stations_all[item_name], station_id) - elseif effective_item_count >= p_threshold then - local item_network_name = item_name + ":" + station.network_name - if r_stations_all[item_network_name] == nil then - r_stations_all[item_network_name] = {} - p_stations_all[item_network_name] = {} - all_names[#all_names + 1] = item_network_name - all_names[#all_names + 1] = v.signal - end - table.insert(p_stations_all[item_name], station_id) + if -effective_item_count >= r_threshold then + local item_network_name = item_name + ":" + station.network_name + if r_stations_all[item_network_name] == nil then + r_stations_all[item_network_name] = {} + p_stations_all[item_network_name] = {} + all_names[#all_names + 1] = item_network_name + all_names[#all_names + 1] = v.signal end + table.insert(r_stations_all[item_name], station_id) + elseif effective_item_count >= p_threshold then + local item_network_name = item_name + ":" + station.network_name + if r_stations_all[item_network_name] == nil then + r_stations_all[item_network_name] = {} + p_stations_all[item_network_name] = {} + all_names[#all_names + 1] = item_network_name + all_names[#all_names + 1] = v.signal + end + table.insert(p_stations_all[item_name], station_id) end end end @@ -529,4 +525,7 @@ function tick(map_data, mod_settings) end end end + for station_id, station in pairs(stations) do + station.tick_signals = nil + end end diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 5bd4ca6..5012d5c 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -12,11 +12,14 @@ LOCKED_SLOTS = "cybersyn-locked-slots" COMBINATOR_NAME = "cybersyn-combinator" COMBINATOR_OUT_NAME = "cybersyn-combinator-output" -OPERATION_PRIMARY_IO = "*" -OPERATION_SECONDARY_IO = "/" +OPERATION_DEFAULT = "*" +OPERATION_PRIMARY_IO = "/" +OPERATION_SECONDARY_IO = "%" OPERATION_DEPOT = "+" OPERATION_WAGON_MANIFEST = "-" +NETWORK_SIGNAL_DEFAULT = {name="signal-A", type="virtual"} + DELTA = 1/2048 DEPOT_PRIORITY_MULT = 2048 @@ -38,6 +41,3 @@ STATION_LAYOUT_NOT_FLUID = "[NC]" STATION_LAYOUT_NOT_CARGO = "[NF]" LONGEST_INSERTER_REACH = 2 - -TRAIN_CLASS_ALL = {name = "all", type = "virtual"} -TRAIN_CLASS_AUTO = {name = "auto", type = "virtual"} diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index e4f70d6..cc32185 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -15,28 +15,29 @@ ---@class Station ---@field public deliveries_total int ----@field public priority int ---@field public last_delivery_tick int ----@field public r_threshold int >= 0 ----@field public p_threshold int >= 0 ----@field public locked_slots int >= 0 +---@field public priority int --transient +---@field public r_threshold int >= 0 --transient +---@field public p_threshold int >= 0 --transient +---@field public locked_slots int >= 0 --transient ---@field public entity_stop LuaEntity ---@field public entity_comb1 LuaEntity ---@field public entity_comb2 LuaEntity? ---@field public wagon_combs {[int]: LuaEntity}?--NOTE: allowed to be invalid entities or combinators with the wrong operation, these must be checked and lazy deleted when found ---@field public deliveries {[string]: int} ---@field public network_name string? ----@field public network_flag int +---@field public network_flag int --transient ---@field public train_class SignalID? ---@field public accepted_layouts TrainClass ---@field public layout_pattern string? +---@field public tick_signals Signal[]? --transient ---@class Depot ----@field public priority int +---@field public priority int --transient ---@field public entity_stop LuaEntity ---@field public entity_comb LuaEntity ---@field public network_name string? ----@field public network_flag int +---@field public network_flag int --transient ---@class Train ---@field public entity LuaTrain diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index c2f950a..6401114 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -75,8 +75,8 @@ function gui_opened(comb, player) }}, ---choose-elem-button {type="line", style_mods={top_padding=10}}, - {type="label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, - {type="choose-elem-button", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={ + {type="label", name="network_label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, + {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={ on_elem_changed={"choose-elem-button", comb.unit_number} }}, }} @@ -87,6 +87,7 @@ function gui_opened(comb, player) window.preview.entity = comb window.titlebar.drag_target = window.main_window window.main_window.force_auto_center() + window.network.visible = selected_index == 1 or selected_index == 3 player.opened = window.main_window end @@ -131,19 +132,29 @@ function register_gui_actions() local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end + local parent = element.parent local a = comb.get_or_create_control_behavior() local control = a.parameters if element.selected_index == 1 then control.operation = OPERATION_PRIMARY_IO + parent["network"].visible = true + parent["network_label"].visible = true elseif element.selected_index == 2 then control.operation = OPERATION_SECONDARY_IO + parent["network"].visible = false + parent["network_label"].visible = false elseif element.selected_index == 3 then control.operation = OPERATION_DEPOT + parent["network"].visible = true + parent["network_label"].visible = true elseif element.selected_index == 4 then control.operation = OPERATION_WAGON_MANIFEST + parent["network"].visible = false + parent["network_label"].visible = false else return end + a.parameters = control on_combinator_updated(global, comb) elseif msg[1] == "choose-elem-button" then @@ -152,14 +163,19 @@ function register_gui_actions() local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end - local signal = element.elem_value - local a = comb.get_or_create_control_behavior() local control = a.parameters - control.first_signal = signal + local signal = element.elem_value + if signal and (signal.name == "signal-everything" or signal.name == "signal-anything" or signal.name == "signal-each") then + control.first_signal = nil + element.elem_value = nil + else + control.first_signal = signal + end a.parameters = control + on_combinator_network_updated(global, comb, signal and signal.name or nil) end end end) diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index b00198b..d41ee40 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -26,11 +26,27 @@ local function on_failed_delivery(map_data, train) train.manifest = nil end + +---@param map_data MapData +---@param stop LuaEntity +---@param comb LuaEntity +local function on_depot_built(map_data, stop, comb, network_name) + local depot = { + entity_stop = stop, + entity_comb = comb, + network_name = network_name, + priority = 0, + network_flag = 0, + } + map_data.depots[stop.unit_number] = depot +end + ---@param map_data MapData ---@param stop LuaEntity ---@param comb1 LuaEntity ---@param comb2 LuaEntity -local function on_station_built(map_data, stop, comb1, comb2) +---@param network_name string +local function on_station_built(map_data, stop, comb1, comb2, network_name) local station = { entity_stop = stop, entity_comb1 = comb1, @@ -42,6 +58,8 @@ local function on_station_built(map_data, stop, comb1, comb2) r_threshold = 0, p_threshold = 0, locked_slots = 0, + network_name = network_name, + network_flag = 0, deliveries = {}, train_class = TRAIN_CLASS_AUTO, accepted_layouts = {}, @@ -156,7 +174,13 @@ local function on_combinator_built(map_data, comb) map_data.to_output[comb.unit_number] = out map_data.to_stop[comb.unit_number] = stop - local control = comb.get_or_create_control_behavior().parameters + local a = comb.get_or_create_control_behavior() + local control = a.parameters + if control.operation == OPERATION_DEFAULT then + control.operation = OPERATION_PRIMARY_IO + control.first_signal = NETWORK_SIGNAL_DEFAULT + a.parameters = control + end if control.operation == OPERATION_WAGON_MANIFEST then if rail then update_station_from_rail(map_data, rail, nil) @@ -164,11 +188,12 @@ local function on_combinator_built(map_data, comb) elseif control.operation == OPERATION_DEPOT then if stop then local station = map_data.stations[stop.unit_number] - local depot_comb = map_data.depots[stop.unit_number] - if depot_comb or station then + ---@type Depot + local depot = map_data.depots[stop.unit_number] + if depot or station then --NOTE: repeated combinators are ignored else - map_data.depots[stop.unit_number] = comb + on_depot_built(map_data, stop, comb, control.first_signal) end end elseif control.operation == OPERATION_SECONDARY_IO then @@ -181,11 +206,11 @@ local function on_combinator_built(map_data, comb) elseif stop then control.operation = OPERATION_PRIMARY_IO local station = map_data.stations[stop.unit_number] - local depot_comb = map_data.depots[stop.unit_number] + local depot = map_data.depots[stop.unit_number] if station then --NOTE: repeated combinators are ignored else - if depot_comb then + if depot then --NOTE: this will disrupt deliveries in progress that where dispatched from this station in a minor way map_data.depots[stop.unit_number] = nil end @@ -194,7 +219,26 @@ local function on_combinator_built(map_data, comb) local comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) - on_station_built(map_data, stop, comb, comb2) + on_station_built(map_data, stop, comb, comb2, control.first_signal) + end + end +end +---@param map_data MapData +---@param comb LuaEntity +function on_combinator_network_updated(map_data, comb, network_name) + local stop = map_data.to_stop[comb.unit_number] + + if stop and stop.valid then + local station = map_data.stations[stop.unit_number] + if station then + if station.entity_comb1 == comb then + station.network_name = network_name + end + else + local depot = map_data.depots[stop.unit_number] + if depot.entity_comb == comb then + depot.network_name = network_name + end end end end @@ -212,18 +256,31 @@ local function on_combinator_broken(map_data, comb) local comb1 = search_for_station_combinator(map_data, stop, OPERATION_PRIMARY_IO, comb) if comb1 then station.entity_comb1 = comb1 + local control = comb1.get_or_create_control_behavior().parameters + station.network_name = control.first_signal else on_station_broken(map_data, stop.unit_number, station) - map_data.depots[stop.unit_number] = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) + local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) + if depot_comb then + local control = depot_comb.get_or_create_control_behavior().parameters + on_depot_built(map_data, stop, depot_comb, control.first_signal) + end end elseif station.entity_comb2 == comb then station.entity_comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) end else - local depot_comb = map_data.depots[stop.unit_number] - if depot_comb == comb then + local depot = map_data.depots[stop.unit_number] + if depot.entity_comb == comb then --NOTE: this will disrupt deliveries in progress that where dispatched from this station in a minor way - map_data.depots[stop.unit_number] = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) + local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) + if depot_comb then + local control = depot_comb.get_or_create_control_behavior().parameters + depot.entity_comb = depot_comb + depot.network_name = control.first_signal + else + map_data.depots[stop.unit_number] = nil + end end end end @@ -272,9 +329,11 @@ local function on_stop_built(map_data, stop) end end if comb1 then - on_station_built(map_data, stop, comb1, comb2) + local control = comb1.get_or_create_control_behavior().parameters + on_station_built(map_data, stop, comb1, comb2, control.first_signal) elseif depot_comb then - map_data.depots[stop.unit_number] = depot_comb + local control = depot_comb.get_or_create_control_behavior().parameters + on_depot_built(map_data, stop, depot_comb, control.first_signal) end end ---@param map_data MapData @@ -342,8 +401,9 @@ end ---@param map_data MapData +---@param stop LuaEntity ---@param train_entity LuaTrain -local function on_train_arrives_depot(map_data, train_entity) +local function on_train_arrives_depot(map_data, stop, train_entity) local contents = train_entity.get_contents() local train = map_data.trains[train_entity.id] if train then @@ -354,7 +414,7 @@ local function on_train_arrives_depot(map_data, train_entity) train.manifest = nil train.depot_name = train_entity.station.backer_name train.status = STATUS_D - map_data.trains_available[][train_entity.id] = true + map_data.trains_available[stop.unit_number] = train_entity.id else if train.manifest then on_failed_delivery(map_data, train) @@ -362,7 +422,7 @@ local function on_train_arrives_depot(map_data, train_entity) end train.depot_name = train_entity.station.backer_name train.status = STATUS_D - map_data.trains_available[train_entity.id] = true + map_data.trains_available[stop.unit_number] = train_entity.id end if next(contents) ~= nil then --train still has cargo @@ -386,7 +446,7 @@ local function on_train_arrives_depot(map_data, train_entity) } update_train_layout(map_data, train) map_data.trains[train_entity.id] = train - map_data.trains_available[train_entity.id] = true + map_data.trains_available[stop.unit_number] = train_entity.id local schedule = create_depot_schedule(train.depot_name) train_entity.schedule = schedule else @@ -552,7 +612,7 @@ local function on_train_changed(event) if global.stations[stop.unit_number] then on_train_arrives_buffer(global, stop, train) elseif global.depots[stop.unit_number] then - on_train_arrives_depot(global, train_e) + on_train_arrives_depot(global, stop, train_e) end end elseif event.old_state == defines.train_state.wait_station then From e9ad5b47a2532c43f1b0360904797d091ae7d1c3 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 30 Oct 2022 20:48:30 -0400 Subject: [PATCH 08/66] implemented partially wagon_combs --- cybersyn/TODO | 6 +- cybersyn/scripts/layout.lua | 184 ++++++++++++++++++++++++++++++++++++ cybersyn/scripts/main.lua | 11 ++- 3 files changed, 193 insertions(+), 8 deletions(-) diff --git a/cybersyn/TODO b/cybersyn/TODO index 1db0f42..cbb4781 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -1,7 +1,7 @@ finish wagon manifest -add rail networks -figure out how to make the area-of-effect graphic the correct size close gui when the combinator is destroyed -Improve localization +improve localization support space elevator add an "all" train class +do hardcore testing +optimizations? diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 0111f79..d3d0d12 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -1,6 +1,18 @@ --By Mami local area = require("__flib__.area") +local function iterr(a, i) + i = i - 1 + if i > 0 then + return i, a[i] + end +end + +local function irpairs(a) + return iterr, a, #a + 1 +end + + ---@param map_data MapData ---@param train Train ---@param train_id uint @@ -73,6 +85,178 @@ function update_train_layout(map_data, train) train.fluid_capacity = fluid_capacity end +---@param stop LuaEntity +---@param train LuaTrain +local function get_train_direction(stop, train) + local back_rail = train.back_rail + local stop_rail = stop.connected_rail + + if back_rail and stop_rail and back_rail.unit_number == stop_rail.unit_number then + return true + end + + return false +end + +---@param map_data MapData +---@param station Station +---@param train Train +function set_p_wagon_combs(map_data, station, train) + if not station.wagon_combs or not next(station.wagon_combs) then return end + local carriages = train.entity.carriages + local manifest = train.manifest + + local is_reversed = get_train_direction(station.entity_stop, train.entity) + + local item_i = 1 + local item = manifest[item_i] + local item_count = item.count + local fluid_i = 1 + local fluid = manifest[fluid_i] + local fluid_count = fluid.count + + local ivpairs = is_reversed and irpairs or ipairs + for carriage_i, carriage in ivpairs(carriages) do + ---@type LuaEntity? + local comb = station.wagon_combs[carriage_i] + if comb and not comb.valid then + comb = nil + station.wagon_combs[carriage_i] = nil + if next(station.wagon_combs) == nil then + station.wagon_combs = nil + break + end + end + if carriage.type == "cargo-wagon" and item_i <= #manifest then + local signals = {} + + local inv = carriage.get_inventory(defines.inventory.cargo_wagon) + local item_slots_capacity = #inv - station.locked_slots + while item_slots_capacity > 0 do + local do_inc = false + if item.type == "item" then + local stack_size = game.item_prototypes[item.name].stack_size + local item_slots = math.ceil(item_count/stack_size) + local i = #signals + 1 + if item_slots > item_slots_capacity then + if comb then + signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_slots_capacity*stack_size} + end + item_slots_capacity = 0 + item_count = item_count - item_slots_capacity*stack_size + else + if comb then + signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_count} + end + item_slots_capacity = item_slots_capacity - item_slots + do_inc = true + end + else + do_inc = true + end + if do_inc then + item_i = item_i + 1 + if item_i <= #manifest then + item = manifest[item_i] + item_count = item.count + else + break + end + end + end + + if comb then + set_combinator_output(map_data, comb, signals) + end + elseif carriage.type == "fluid-wagon" and fluid_i <= #manifest then + local fluid_capacity = carriage.prototype.fluid_capacity + local signals = {} + + while fluid_capacity > 0 do + local do_inc = false + if fluid.type == "fluid" then + local i = #signals + 1 + if fluid_count > fluid_capacity then + if comb then + signals[i] = {index = i, signal = {type = fluid.type, name = fluid.name}, count = fluid_capacity} + end + fluid_capacity = 0 + fluid_count = fluid_count - fluid_capacity + else + if comb then + signals[i] = {index = i, signal = {type = fluid.type, name = fluid.name}, count = item_count} + end + fluid_capacity = fluid_capacity - fluid_count + do_inc = true + end + else + do_inc = true + end + if do_inc then + fluid_i = fluid_i + 1 + if fluid_i <= #manifest then + fluid = manifest[fluid_i] + fluid_count = fluid.count + else + break + end + end + end + + if comb then + set_combinator_output(map_data, comb, signals) + end + end + end +end + +---@param map_data MapData +---@param station Station +---@param train Train +function set_r_wagon_combs(map_data, station, train) + if not station.wagon_combs or not next(station.wagon_combs) then return end + local carriages = train.entity.carriages + + local is_reversed = get_train_direction(station.entity_stop, train.entity) + + local ivpairs = is_reversed and irpairs or ipairs + for carriage_i, carriage in ivpairs(carriages) do + ---@type LuaEntity? + local comb = station.wagon_combs[carriage_i] + if comb and not comb.valid then + comb = nil + station.wagon_combs[carriage_i] = nil + if next(station.wagon_combs) == nil then + station.wagon_combs = nil + break + end + end + if comb and carriage.type == "cargo-wagon" then + local signals = {} + + local inv = carriage.get_inventory(defines.inventory.cargo_wagon) + for stack_i, stack in ipairs(inv) do + if stack.valid_for_read then + local i = #signals + 1 + --TODO: does this work or do we need to aggregate signals? + signals[i] = {index = i, signal = {type = stack.type, name = stack.name}, count = stack.count} + end + end + set_combinator_output(map_data, comb, signals) + elseif comb and carriage.type == "fluid-wagon" then + local signals = {} + + local inv = carriage.get_fluid_contents() + for fluid_name, count in pairs(inv) do + local i = #signals + 1 + signals[i] = {index = i, signal = {type = "fluid", name = fluid_name}, count = math.floor(count)} + end + set_combinator_output(map_data, comb, signals) + end + end +end + + ---@param map_data MapData ---@param station Station ---@param forbidden_entity LuaEntity? diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index d41ee40..7c20737 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -11,6 +11,7 @@ local function on_failed_delivery(map_data, train) remove_manifest(map_data, station, train.manifest, 1) if train.status == STATUS_P then set_combinator_output(map_data, station.entity_comb1, nil) + --TODO: remove wagon_comb manifest output end end local is_r_delivery_made = train.status == STATUS_R_TO_D @@ -19,6 +20,7 @@ local function on_failed_delivery(map_data, train) remove_manifest(map_data, station, train.manifest, -1) if train.status == STATUS_R then set_combinator_output(map_data, station.entity_comb1, nil) + --TODO: remove wagon_comb manifest output end end train.r_station_id = 0 @@ -470,11 +472,7 @@ local function on_train_arrives_buffer(map_data, stop, train) signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item.count} end set_combinator_output(map_data, station.entity_comb1, signals) - if station.wagon_combs then - for i, entity in ipairs(station.wagon_combs) do - - end - end + set_p_wagon_combs(map_data, station, train) end elseif train.status == STATUS_P_TO_R then if train.r_station_id == station_id then @@ -486,6 +484,7 @@ local function on_train_arrives_buffer(map_data, stop, train) signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = -1} end set_combinator_output(map_data, station.entity_comb1, signals) + set_r_wagon_combs(map_data, station, train) end else on_failed_delivery(map_data, train) @@ -507,11 +506,13 @@ local function on_train_leaves_station(map_data, train) local station = map_data.stations[train.p_station_id] remove_manifest(map_data, station, train.manifest, 1) set_combinator_output(map_data, station.entity_comb1, nil) + --TODO: remove wagon_comb manifest output elseif train.status == STATUS_R then train.status = STATUS_R_TO_D local station = map_data.stations[train.r_station_id] remove_manifest(map_data, station, train.manifest, -1) set_combinator_output(map_data, station.entity_comb1, nil) + --TODO: remove wagon_comb manifest output end end end From bc9b0bd1889742176bd93d28132d48f108ece983 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Mon, 31 Oct 2022 12:24:50 -0400 Subject: [PATCH 09/66] fixes to wagon combs --- cybersyn/scripts/layout.lua | 36 ++++++++++++++++++++++++++---------- cybersyn/scripts/main.lua | 13 ++++--------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index d3d0d12..53ff419 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -175,30 +175,29 @@ function set_p_wagon_combs(map_data, station, train) while fluid_capacity > 0 do local do_inc = false if fluid.type == "fluid" then - local i = #signals + 1 if fluid_count > fluid_capacity then if comb then - signals[i] = {index = i, signal = {type = fluid.type, name = fluid.name}, count = fluid_capacity} + signals[1] = {index = 1, signal = {type = fluid.type, name = fluid.name}, count = fluid_capacity} end fluid_capacity = 0 fluid_count = fluid_count - fluid_capacity else if comb then - signals[i] = {index = i, signal = {type = fluid.type, name = fluid.name}, count = item_count} + signals[1] = {index = 1, signal = {type = fluid.type, name = fluid.name}, count = item_count} end fluid_capacity = fluid_capacity - fluid_count - do_inc = true + fluid_i = fluid_i + 1 + if fluid_i <= #manifest then + fluid = manifest[fluid_i] + fluid_count = fluid.count + end end + break else - do_inc = true - end - if do_inc then fluid_i = fluid_i + 1 if fluid_i <= #manifest then fluid = manifest[fluid_i] fluid_count = fluid.count - else - break end end end @@ -214,7 +213,7 @@ end ---@param station Station ---@param train Train function set_r_wagon_combs(map_data, station, train) - if not station.wagon_combs or not next(station.wagon_combs) then return end + if not station.wagon_combs then return end local carriages = train.entity.carriages local is_reversed = get_train_direction(station.entity_stop, train.entity) @@ -256,6 +255,23 @@ function set_r_wagon_combs(map_data, station, train) end end +---@param map_data MapData +---@param station Station +function unset_wagon_combs(map_data, station) + if not station.wagon_combs then return end + + for i, comb in pairs(station.wagon_combs) do + if comb.valid then + set_combinator_output(map_data, comb, nil) + else + station.wagon_combs[i] = nil + end + end + if next(station.wagon_combs) == nil then + station.wagon_combs = nil + end +end + ---@param map_data MapData ---@param station Station diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 7c20737..6fb0076 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -11,7 +11,7 @@ local function on_failed_delivery(map_data, train) remove_manifest(map_data, station, train.manifest, 1) if train.status == STATUS_P then set_combinator_output(map_data, station.entity_comb1, nil) - --TODO: remove wagon_comb manifest output + unset_wagon_combs(map_data, station) end end local is_r_delivery_made = train.status == STATUS_R_TO_D @@ -20,7 +20,7 @@ local function on_failed_delivery(map_data, train) remove_manifest(map_data, station, train.manifest, -1) if train.status == STATUS_R then set_combinator_output(map_data, station.entity_comb1, nil) - --TODO: remove wagon_comb manifest output + unset_wagon_combs(map_data, station) end end train.r_station_id = 0 @@ -103,7 +103,6 @@ end local function search_for_station_combinator(map_data, stop, comb_operation, comb_forbidden) local pos_x = stop.position.x local pos_y = stop.position.y - --TODO: fix search area local search_area = { {pos_x - 2, pos_y - 2}, {pos_x + 2, pos_y + 2} @@ -128,7 +127,6 @@ local function on_combinator_built(map_data, comb) local pos_x = comb.position.x local pos_y = comb.position.y - --TODO: fix search area local search_area if comb.direction == defines.direction.north or comb.direction == defines.direction.south then search_area = { @@ -308,7 +306,6 @@ local function on_stop_built(map_data, stop) local pos_x = stop.position.x local pos_y = stop.position.y - --TODO: fix search area local search_area = { {pos_x - 2, pos_y - 2}, {pos_x + 2, pos_y + 2} @@ -344,7 +341,6 @@ local function on_stop_broken(map_data, stop) local pos_x = stop.position.x local pos_y = stop.position.y - --TODO: fix search area local search_area = { {pos_x - 2, pos_y - 2}, {pos_x + 2, pos_y + 2} @@ -506,13 +502,13 @@ local function on_train_leaves_station(map_data, train) local station = map_data.stations[train.p_station_id] remove_manifest(map_data, station, train.manifest, 1) set_combinator_output(map_data, station.entity_comb1, nil) - --TODO: remove wagon_comb manifest output + unset_wagon_combs(map_data, station) elseif train.status == STATUS_R then train.status = STATUS_R_TO_D local station = map_data.stations[train.r_station_id] remove_manifest(map_data, station, train.manifest, -1) set_combinator_output(map_data, station.entity_comb1, nil) - --TODO: remove wagon_comb manifest output + unset_wagon_combs(map_data, station) end end end @@ -535,7 +531,6 @@ end local function on_train_modified(map_data, pre_train_id, train_entity) local train = map_data.trains[pre_train_id] if train then - if train.manifest then on_failed_delivery(map_data, train) end From 599978afb519403360a7848046c0ee743edca433 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 1 Nov 2022 16:20:08 -0400 Subject: [PATCH 10/66] added an all class --- cybersyn/TODO | 1 - cybersyn/locale/en/base.cfg | 1 + cybersyn/scripts/central-planning.lua | 2 +- cybersyn/scripts/global.lua | 2 +- cybersyn/scripts/gui.lua | 125 +++++++++++++++++--------- cybersyn/scripts/layout.lua | 23 ++--- cybersyn/scripts/main.lua | 38 ++++---- 7 files changed, 110 insertions(+), 82 deletions(-) diff --git a/cybersyn/TODO b/cybersyn/TODO index cbb4781..a2dd21b 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -2,6 +2,5 @@ finish wagon manifest close gui when the combinator is destroyed improve localization support space elevator -add an "all" train class do hardcore testing optimizations? diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 793811b..51b9ce3 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -47,3 +47,4 @@ comb2=Secondary station control depot=Depot wagon-manifest=Wagon combinator-title=Cybernetic combinator +auto-description=Station automatically decides which trains in the network it can service diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 34cecb8..cb38061 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -173,7 +173,7 @@ end ---@param station Station ---@param layout_id uint local function station_accepts_layout(station, layout_id) - return station.accepted_layouts[layout_id] + return station.is_all or station.accepted_layouts[layout_id] end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index cc32185..f0a7d9e 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -27,7 +27,7 @@ ---@field public deliveries {[string]: int} ---@field public network_name string? ---@field public network_flag int --transient ----@field public train_class SignalID? +---@field public is_all boolean ---@field public accepted_layouts TrainClass ---@field public layout_pattern string? ---@field public tick_signals Signal[]? --transient diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 6401114..bb6173c 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -31,7 +31,7 @@ STATUS_NAMES_DEFAULT = "entity-status.disabled" function gui_opened(comb, player) local rootgui = player.gui.screen local selected_index = 0 - local control = comb.get_or_create_control_behavior().parameters + local control = comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] if control.operation == OPERATION_PRIMARY_IO then selected_index = 1 elseif control.operation == OPERATION_SECONDARY_IO then @@ -42,52 +42,60 @@ function gui_opened(comb, player) selected_index = 4 end - local window = flib_gui.build(rootgui, { - {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={ - --title bar - {type="flow", ref={"titlebar"}, children={ - {type="label", style="frame_title", caption={"cybersyn-gui.combinator-title"}, elem_mods={ignored_by_interaction=true}}, - {type="empty-widget", style="flib_titlebar_drag_handle", elem_mods={ignored_by_interaction=true}}, - {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}, sprite="utility/close_white", hovered_sprite="utility/close_black", name=COMBINATOR_NAME, actions={ - on_click = {"close", comb.unit_number} - }} - }}, - {type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=12}, children={ - {type="flow", direction="vertical", style_mods={horizontal_align="left"}, children={ - --status - {type="flow", style = "status_flow", direction = "horizontal", style_mods={vertical_align="center", horizontally_stretchable=true, bottom_padding=4}, children={ - {type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}}, - {type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}} - }}, - --preview - {type="frame", style="deep_frame_in_shallow_frame", style_mods={minimal_width=0, horizontally_stretchable=true, padding=0}, children={ - {type="entity-preview", style="wide_entity_button", ref={"preview"}}, - }}, - --drop down - {type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=8}}, - {type="drop-down", style_mods={top_padding=3}, ref={"operation"}, actions={ - on_selection_state_changed={"drop-down", comb.unit_number} - }, selected_index=selected_index, items={ - {"cybersyn-gui.comb1"}, - {"cybersyn-gui.comb2"}, - {"cybersyn-gui.depot"}, - {"cybersyn-gui.wagon-manifest"}, - }}, - ---choose-elem-button - {type="line", style_mods={top_padding=10}}, - {type="label", name="network_label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, - {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2}, actions={ +local window = flib_gui.build(rootgui, { + {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={ + --title bar + {type="flow", ref={"titlebar"}, children={ + {type="label", style="frame_title", caption={"cybersyn-gui.combinator-title"}, elem_mods={ignored_by_interaction=true}}, + {type="empty-widget", style="flib_titlebar_drag_handle", elem_mods={ignored_by_interaction=true}}, + {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}, sprite="utility/close_white", hovered_sprite="utility/close_black", name=COMBINATOR_NAME, actions={ + on_click = {"close", comb.unit_number} + }} + }}, + {type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=12}, children={ + {type="flow", direction="vertical", style_mods={horizontal_align="left"}, children={ + --status + {type="flow", style="status_flow", direction="horizontal", style_mods={vertical_align="center", horizontally_stretchable=true, bottom_padding=4}, children={ + {type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}}, + {type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}} + }}, + --preview + {type="frame", style="deep_frame_in_shallow_frame", style_mods={minimal_width=0, horizontally_stretchable=true, padding=0}, children={ + {type="entity-preview", style="wide_entity_button", ref={"preview"}}, + }}, + --drop down + {type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=8}}, + {type="drop-down", style_mods={top_padding=3}, ref={"operation"}, actions={ + on_selection_state_changed={"drop-down", comb.unit_number} + }, selected_index=selected_index, items={ + {"cybersyn-gui.comb1"}, + {"cybersyn-gui.comb2"}, + {"cybersyn-gui.depot"}, + {"cybersyn-gui.wagon-manifest"}, + }}, + ---choose-elem-button + {type="line", style_mods={top_padding=10}}, + {type="label", name="network_label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, + {type="flow", name="bottom", direction="horizontal", children={ + {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2, right_margin=6}, actions={ on_elem_changed={"choose-elem-button", comb.unit_number} }}, + {type="checkbox", name="radiobutton", ref={"radiobutton"}, state=control.second_constant ~= 1, style_mods={top_margin=4}, actions={ + on_checked_state_changed={"radiobutton", comb.unit_number} + }}, + {type="label", name="radiolabel", style_mods={single_line=false, maximal_width=330, left_padding=3}, ref={"radiolabel"}, caption={"cybersyn-gui.auto-description"}}, }} }} }} - }) + }} +}) window.preview.entity = comb window.titlebar.drag_target = window.main_window window.main_window.force_auto_center() window.network.visible = selected_index == 1 or selected_index == 3 + window.radiobutton.visible = selected_index == 1 + window.radiolabel.visible = selected_index == 1 player.opened = window.main_window end @@ -132,25 +140,33 @@ function register_gui_actions() local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end - local parent = element.parent - local a = comb.get_or_create_control_behavior() + local parent = element.parent.bottom + local a = comb.get_or_create_control_behavior() --[[@as LuaArithmeticCombinatorControlBehavior]] local control = a.parameters if element.selected_index == 1 then control.operation = OPERATION_PRIMARY_IO + element.parent["network_label"].visible = true parent["network"].visible = true - parent["network_label"].visible = true + parent["radiobutton"].visible = true + parent["radiolabel"].visible = true elseif element.selected_index == 2 then control.operation = OPERATION_SECONDARY_IO + element.parent["network_label"].visible = false parent["network"].visible = false - parent["network_label"].visible = false + parent["radiobutton"].visible = false + parent["radiolabel"].visible = false elseif element.selected_index == 3 then control.operation = OPERATION_DEPOT + element.parent["network_label"].visible = true parent["network"].visible = true - parent["network_label"].visible = true + parent["radiobutton"].visible = false + parent["radiolabel"].visible = false elseif element.selected_index == 4 then control.operation = OPERATION_WAGON_MANIFEST + element.parent["network_label"].visible = false parent["network"].visible = false - parent["network_label"].visible = false + parent["radiobutton"].visible = false + parent["radiolabel"].visible = false else return end @@ -163,7 +179,7 @@ function register_gui_actions() local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end - local a = comb.get_or_create_control_behavior() + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local control = a.parameters local signal = element.elem_value @@ -176,6 +192,27 @@ function register_gui_actions() a.parameters = control on_combinator_network_updated(global, comb, signal and signal.name or nil) + elseif msg[1] == "radiobutton" then + local element = event.element + if not element then return end + local comb = global.to_comb[msg[2]] + if not comb or not comb.valid then return end + + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = a.parameters + + local is_auto = element.state + control.second_constant = is_auto and 0 or 1 + + a.parameters = control + + local stop = global.to_stop[comb.unit_number] + if stop then + local station = global.stations[stop.unit_number] + if station then + set_station_train_class(global, station, not is_auto) + end + end end end end) diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 53ff419..d56b8f6 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -300,7 +300,6 @@ local function reset_station_layout(map_data, station, forbidden_entity) local area_delta local direction_filter local is_ver - --local center_line if station_direction == defines.direction.north then search_area = {left_top = {x = middle_x - reach, y = middle_y}, right_bottom = {x = middle_x + reach, y = middle_y + 6}} area_delta = {x = 0, y = 7} @@ -376,7 +375,7 @@ local function reset_station_layout(map_data, station, forbidden_entity) supports_fluid = true end elseif entity.name == COMBINATOR_NAME then - local control = entity.get_or_create_control_behavior().parameters + local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] if control.operation == OPERATION_WAGON_MANIFEST then local pos = entity.position local is_there @@ -427,19 +426,13 @@ end ---@param map_data MapData ---@param station Station ----@param train_class SignalID -function set_station_train_class(map_data, station, train_class) - if train_class.name == TRAIN_CLASS_AUTO.name then - if station.train_class.name ~= TRAIN_CLASS_AUTO.name then - station.train_class = TRAIN_CLASS_AUTO - station.accepted_layouts = {} +---@param is_all boolean +function set_station_train_class(map_data, station, is_all) + if station.is_all ~= is_all then + station.is_all = is_all + if not is_all then + reset_station_layout(map_data, station, nil) end - reset_station_layout(map_data, station, nil) - else - station.train_class = train_class - station.accepted_layouts = map_data.train_classes[train_class.name] - assert(station.accepted_layouts ~= nil) - station.layout_pattern = nil end end @@ -447,7 +440,7 @@ end ---@param station Station ---@param forbidden_entity LuaEntity? function update_station_if_auto(map_data, station, forbidden_entity) - if station.train_class.name == TRAIN_CLASS_AUTO.name then + if not station.is_all then reset_station_layout(map_data, station, forbidden_entity) end end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 6fb0076..1afd4ba 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -32,11 +32,11 @@ end ---@param map_data MapData ---@param stop LuaEntity ---@param comb LuaEntity -local function on_depot_built(map_data, stop, comb, network_name) +local function on_depot_built(map_data, stop, comb, control) local depot = { entity_stop = stop, entity_comb = comb, - network_name = network_name, + network_name = control.first_signal and control.first_signal.name or nil, priority = 0, network_flag = 0, } @@ -47,8 +47,8 @@ end ---@param stop LuaEntity ---@param comb1 LuaEntity ---@param comb2 LuaEntity ----@param network_name string -local function on_station_built(map_data, stop, comb1, comb2, network_name) +---@param control ArithmeticCombinatorParameters +local function on_station_built(map_data, stop, comb1, comb2, control) local station = { entity_stop = stop, entity_comb1 = comb1, @@ -60,10 +60,10 @@ local function on_station_built(map_data, stop, comb1, comb2, network_name) r_threshold = 0, p_threshold = 0, locked_slots = 0, - network_name = network_name, + network_name = control.first_signal and control.first_signal.name or nil, network_flag = 0, deliveries = {}, - train_class = TRAIN_CLASS_AUTO, + is_all = control.second_constant == 1, accepted_layouts = {}, layout_pattern = nil, } @@ -113,7 +113,7 @@ local function search_for_station_combinator(map_data, stop, comb_operation, com entity.valid and entity.name == COMBINATOR_NAME and entity ~= comb_forbidden and map_data.to_stop[entity.unit_number] == stop then - local control = entity.get_or_create_control_behavior().parameters + local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] if control.operation == comb_operation then return entity end @@ -174,7 +174,7 @@ local function on_combinator_built(map_data, comb) map_data.to_output[comb.unit_number] = out map_data.to_stop[comb.unit_number] = stop - local a = comb.get_or_create_control_behavior() + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local control = a.parameters if control.operation == OPERATION_DEFAULT then control.operation = OPERATION_PRIMARY_IO @@ -193,7 +193,7 @@ local function on_combinator_built(map_data, comb) if depot or station then --NOTE: repeated combinators are ignored else - on_depot_built(map_data, stop, comb, control.first_signal) + on_depot_built(map_data, stop, comb, control) end end elseif control.operation == OPERATION_SECONDARY_IO then @@ -219,7 +219,7 @@ local function on_combinator_built(map_data, comb) local comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) - on_station_built(map_data, stop, comb, comb2, control.first_signal) + on_station_built(map_data, stop, comb, comb2, control) end end end @@ -256,13 +256,13 @@ local function on_combinator_broken(map_data, comb) local comb1 = search_for_station_combinator(map_data, stop, OPERATION_PRIMARY_IO, comb) if comb1 then station.entity_comb1 = comb1 - local control = comb1.get_or_create_control_behavior().parameters - station.network_name = control.first_signal + local control = comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + station.network_name = control.first_signal and control.first_signal.name else on_station_broken(map_data, stop.unit_number, station) local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) if depot_comb then - local control = depot_comb.get_or_create_control_behavior().parameters + local control = depot_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] on_depot_built(map_data, stop, depot_comb, control.first_signal) end end @@ -275,9 +275,9 @@ local function on_combinator_broken(map_data, comb) --NOTE: this will disrupt deliveries in progress that where dispatched from this station in a minor way local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) if depot_comb then - local control = depot_comb.get_or_create_control_behavior().parameters + local control = depot_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] depot.entity_comb = depot_comb - depot.network_name = control.first_signal + depot.network_name = control.first_signal and control.first_signal.name else map_data.depots[stop.unit_number] = nil end @@ -317,7 +317,7 @@ local function on_stop_built(map_data, stop) for _, entity in pairs(entities) do if entity.valid and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then map_data.to_stop[entity.unit_number] = stop - local control = entity.get_or_create_control_behavior().parameters + local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] if control.operation == OPERATION_PRIMARY_IO then comb1 = entity elseif control.operation == OPERATION_SECONDARY_IO then @@ -328,11 +328,9 @@ local function on_stop_built(map_data, stop) end end if comb1 then - local control = comb1.get_or_create_control_behavior().parameters - on_station_built(map_data, stop, comb1, comb2, control.first_signal) + on_station_built(map_data, stop, comb1, comb2, comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]]) elseif depot_comb then - local control = depot_comb.get_or_create_control_behavior().parameters - on_depot_built(map_data, stop, depot_comb, control.first_signal) + on_depot_built(map_data, stop, depot_comb, depot_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]]) end end ---@param map_data MapData From 89997e057feb4ee50558668122a8dd074d9d54a4 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 3 Nov 2022 12:56:24 -0400 Subject: [PATCH 11/66] finished wagon manifest --- cybersyn/TODO | 3 +- cybersyn/locale/en/base.cfg | 2 +- cybersyn/scripts/central-planning.lua | 154 +++++++++++++------------- cybersyn/scripts/global.lua | 4 +- cybersyn/scripts/gui.lua | 3 +- cybersyn/scripts/layout.lua | 27 +++-- cybersyn/scripts/main.lua | 100 ++++++++++++++--- 7 files changed, 185 insertions(+), 108 deletions(-) diff --git a/cybersyn/TODO b/cybersyn/TODO index a2dd21b..3756855 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -1,6 +1,7 @@ -finish wagon manifest close gui when the combinator is destroyed +play close sound when gui is closed improve localization support space elevator do hardcore testing optimizations? +models & art diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 51b9ce3..1b6e44b 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -35,7 +35,7 @@ cybersyn-request-threshold=Request threshold cybersyn-locked-slots=Locked slots per cargo wagon [cybersyn-messages] -missing-trains=No trains available to make a delivery from station __2__ to station __1__ +missing-trains=No trains available to make a delivery from __2__ to __1__ lost-train=A train has become lost nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index cb38061..139b3cc 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -74,33 +74,6 @@ local function get_signals(station) end end ----@param depot Depot -local function set_depot_signals(depot) - local comb = depot.entity_comb - if depot.network_name and comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then - depot.priority = 0 - depot.network_flag = 1 - local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) - if signals then - for k, v in pairs(signals) do - local item_name = v.signal.name - local item_count = v.count - if item_name then - if item_name == SIGNAL_PRIORITY then - depot.priority = item_count - end - if item_name == depot.network_name then - depot.network_flag = item_count - end - end - end - end - else - depot.priority = 0 - depot.network_flag = 0 - end -end - ---@param map_data MapData ---@param comb LuaEntity ---@param signals ConstantCombinatorParameters[]? @@ -170,13 +143,6 @@ local function get_stop_dist(stop0, stop1) end ----@param station Station ----@param layout_id uint -local function station_accepts_layout(station, layout_id) - return station.is_all or station.accepted_layouts[layout_id] -end - - ---@param map_data MapData ---@param r_station_id uint @@ -195,38 +161,42 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type) return nil, INF end - local best_train = nil + ---@type Depot|nil + local best_depot = nil local best_dist = INF local valid_train_exists = false local is_fluid = item_type == "fluid" - for depot_id, train_id in pairs(map_data.trains_available[network_name]) do - local depot = map_data.depots[depot_id] - local train = map_data.trains[train_id] - --check cargo capabilities - --check layout validity for both stations - if - depot.network_name == network_name and - btest(netand, depot.network_flag) - ((is_fluid and train.fluid_capacity > 0) or (not is_fluid and train.item_slot_capacity > 0)) and - station_accepts_layout(r_station, train.layout_id) and - station_accepts_layout(p_station, train.layout_id) - then - valid_train_exists = true - --check if exists valid path - --check if path is shortest so we prioritize locality - local d_to_p_dist = get_stop_dist(depot.entity_stop, p_station.entity_stop) - DEPOT_PRIORITY_MULT*depot.priority + local trains = map_data.trains_available[network_name] + if trains then + for train_id, depot_id in pairs(trains) do + local depot = map_data.depots[depot_id] + local train = map_data.trains[train_id] + local layout_id = train.layout_id + --check cargo capabilities + --check layout validity for both stations + if + btest(netand, depot.network_flag) and + ((is_fluid and train.fluid_capacity > 0) or (not is_fluid and train.item_slot_capacity > 0)) and + (r_station.is_all or r_station.accepted_layouts[layout_id]) and + (p_station.is_all or p_station.accepted_layouts[layout_id]) + then + valid_train_exists = true + --check if exists valid path + --check if path is shortest so we prioritize locality + local d_to_p_dist = get_stop_dist(depot.entity_stop, p_station.entity_stop) - DEPOT_PRIORITY_MULT*depot.priority - local dist = d_to_p_dist - if dist < best_dist then - best_dist = dist - best_train = train + local dist = d_to_p_dist + if dist < best_dist then + best_dist = dist + best_depot = depot + end end end end if valid_train_exists then - return best_train, best_dist + p_to_r_dist + return best_depot, best_dist + p_to_r_dist else return nil, p_to_r_dist end @@ -236,13 +206,16 @@ end ---@param map_data MapData ---@param r_station_id uint ---@param p_station_id uint ----@param train Train +---@param depot Depot ---@param primary_item_name string ---@param economy Economy -local function send_train_between(map_data, r_station_id, p_station_id, train, primary_item_name, economy) +local function send_train_between(map_data, r_station_id, p_station_id, depot, primary_item_name, economy) --trains and stations expected to be of the same network local r_station = map_data.stations[r_station_id] local p_station = map_data.stations[p_station_id] + local train = map_data.trains[depot.available_train] + ---@type string + local network_name = depot.network_name local requests = {} local manifest = {} @@ -333,7 +306,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, train, p r_station.deliveries[item.name] = (r_station.deliveries[item.name] or 0) + item.count p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count - local item_network_name = (r_station.network_name and item.name + ":" + r_station.network_name) or item.name + local item_network_name = network_name..":"..item.name local r_stations = economy.r_stations_all[item_network_name] local p_stations = economy.p_stations_all[item_network_name] for i, id in ipairs(r_stations) do @@ -350,7 +323,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, train, p end end - map_data.trains_available[train.entity.id] = nil + remove_available_train(map_data, depot) train.status = STATUS_D_TO_P train.p_station_id = p_station_id train.r_station_id = r_station_id @@ -375,8 +348,33 @@ function tick(map_data, mod_settings) local p_stations_all = economy.p_stations_all local all_names = {} - for depot_id, _ in pairs(map_data.trains_available) do - set_depot_signals(map_data.depots[depot_id]) + for _, network in pairs(map_data.trains_available) do + for _, depot_id in pairs(network) do + local depot = map_data.depots[depot_id] + local comb = depot.entity_comb + if depot.network_name and comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then + depot.priority = 0 + depot.network_flag = 1 + local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) + if signals then + for k, v in pairs(signals) do + local item_name = v.signal.name + local item_count = v.count + if item_name then + if item_name == SIGNAL_PRIORITY then + depot.priority = item_count + end + if item_name == depot.network_name then + depot.network_flag = item_count + end + end + end + end + else + depot.priority = 0 + depot.network_flag = 0 + end + end end for station_id, station in pairs(stations) do @@ -420,23 +418,23 @@ function tick(map_data, mod_settings) local r_threshold, p_threshold = get_thresholds(map_data, station, v.signal) if -effective_item_count >= r_threshold then - local item_network_name = item_name + ":" + station.network_name + local item_network_name = station.network_name..":"..item_name if r_stations_all[item_network_name] == nil then r_stations_all[item_network_name] = {} p_stations_all[item_network_name] = {} all_names[#all_names + 1] = item_network_name all_names[#all_names + 1] = v.signal end - table.insert(r_stations_all[item_name], station_id) + table.insert(r_stations_all[item_network_name], station_id) elseif effective_item_count >= p_threshold then - local item_network_name = item_name + ":" + station.network_name + local item_network_name = station.network_name..":"..item_name if r_stations_all[item_network_name] == nil then r_stations_all[item_network_name] = {} p_stations_all[item_network_name] = {} all_names[#all_names + 1] = item_network_name all_names[#all_names + 1] = v.signal end - table.insert(p_stations_all[item_name], station_id) + table.insert(p_stations_all[item_network_name], station_id) end end end @@ -464,18 +462,18 @@ function tick(map_data, mod_settings) local r_station_id = table.remove(r_stations, i) local best = 0 - local best_train = nil + local best_depot = nil local best_dist = INF local highest_prior = -INF local could_have_been_serviced = false for j, p_station_id in ipairs(p_stations) do - local train, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) local prior = stations[p_station_id].priority if prior > highest_prior or (prior == highest_prior and d < best_dist) then - if train then + if depot then best = j best_dist = d - best_train = train + best_depot = depot highest_prior = prior elseif d < INF then could_have_been_serviced = true @@ -483,8 +481,8 @@ function tick(map_data, mod_settings) end end end - if best_train then - send_train_between(map_data, r_station_id, p_stations[best], best_train, item_name, economy) + if best_depot then + send_train_between(map_data, r_station_id, p_stations[best], best_depot, item_name, economy) elseif could_have_been_serviced then send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) end @@ -496,7 +494,7 @@ function tick(map_data, mod_settings) local p_station_id = table.remove(p_stations, j) local best = 0 - local best_train = nil + local best_depot = nil local lowest_tick = INF local highest_prior = -INF local could_have_been_serviced = false @@ -504,10 +502,10 @@ function tick(map_data, mod_settings) local r_station = stations[r_station_id] local prior = r_station.priority if prior > highest_prior or (prior == highest_prior and r_station.last_delivery_tick < lowest_tick) then - local train, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) - if train then + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) + if depot then best = i - best_train = train + best_depot = depot lowest_tick = r_station.last_delivery_tick highest_prior = prior elseif d < INF then @@ -516,8 +514,8 @@ function tick(map_data, mod_settings) end end end - if best_train then - send_train_between(map_data, r_stations[best], p_station_id, best_train, item_name, economy) + if best_depot then + send_train_between(map_data, r_stations[best], p_station_id, best_depot, item_name, economy) elseif could_have_been_serviced then send_missing_train_alert_for_stops(stations[r_stations[best]].entity_stop, stations[p_station_id].entity_stop) end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index f0a7d9e..88874ab 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -8,7 +8,7 @@ ---@field public stations {[uint]: Station} ---@field public depots {[uint]: Depot} ---@field public trains {[uint]: Train} ----@field public trains_available {[string]: {[uint]: uint}} +---@field public trains_available {[string]: {[uint]: uint}} --{[network_name]: {[train_id]: depot_id}} ---@field public layouts {[uint]: string} ---@field public layout_train_count {[uint]: int} ---@field public train_classes {[string]: TrainClass} @@ -38,6 +38,7 @@ ---@field public entity_comb LuaEntity ---@field public network_name string? ---@field public network_flag int --transient +---@field public available_train uint? ---@class Train ---@field public entity LuaTrain @@ -45,6 +46,7 @@ ---@field public item_slot_capacity int ---@field public fluid_capacity int ---@field public depot_name string +---@field public depot Depot? ---@field public status int ---@field public p_station_id uint ---@field public r_station_id uint diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index bb6173c..edb05df 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -75,7 +75,7 @@ local window = flib_gui.build(rootgui, { }}, ---choose-elem-button {type="line", style_mods={top_padding=10}}, - {type="label", name="network_label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, + {type="label", name="network_label", ref={"network_label"}, style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, {type="flow", name="bottom", direction="horizontal", children={ {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2, right_margin=6}, actions={ on_elem_changed={"choose-elem-button", comb.unit_number} @@ -94,6 +94,7 @@ local window = flib_gui.build(rootgui, { window.titlebar.drag_target = window.main_window window.main_window.force_auto_center() window.network.visible = selected_index == 1 or selected_index == 3 + window.network_label.visible = selected_index == 1 or selected_index == 3 window.radiobutton.visible = selected_index == 1 window.radiolabel.visible = selected_index == 1 diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index d56b8f6..7ad6c77 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -1,15 +1,16 @@ --By Mami local area = require("__flib__.area") +local abs = math.abs local function iterr(a, i) - i = i - 1 - if i > 0 then - return i, a[i] + i = i + 1 + if i <= #a then + return i, a[#a - i + 1] end end local function irpairs(a) - return iterr, a, #a + 1 + return iterr, a, 0 end @@ -18,7 +19,10 @@ end ---@param train_id uint function remove_train(map_data, train, train_id) map_data.trains[train_id] = nil - map_data.trains_available[train_id] = nil + local depot = train.depot + if depot then + remove_available_train(map_data, depot) + end local layout_id = train.layout_id local count = map_data.layout_train_count[layout_id] if count <= 1 then @@ -89,10 +93,13 @@ end ---@param train LuaTrain local function get_train_direction(stop, train) local back_rail = train.back_rail - local stop_rail = stop.connected_rail - if back_rail and stop_rail and back_rail.unit_number == stop_rail.unit_number then - return true + if back_rail then + local back_pos = back_rail.position + local stop_pos = stop.position + if abs(back_pos.x - stop_pos.x) < 3 and abs(back_pos.y - stop_pos.y) < 3 then + return true + end end return false @@ -238,7 +245,7 @@ function set_r_wagon_combs(map_data, station, train) if stack.valid_for_read then local i = #signals + 1 --TODO: does this work or do we need to aggregate signals? - signals[i] = {index = i, signal = {type = stack.type, name = stack.name}, count = stack.count} + signals[i] = {index = i, signal = {type = stack.type, name = stack.name}, count = -stack.count} end end set_combinator_output(map_data, comb, signals) @@ -248,7 +255,7 @@ function set_r_wagon_combs(map_data, station, train) local inv = carriage.get_fluid_contents() for fluid_name, count in pairs(inv) do local i = #signals + 1 - signals[i] = {index = i, signal = {type = "fluid", name = fluid_name}, count = math.floor(count)} + signals[i] = {index = i, signal = {type = "fluid", name = fluid_name}, count = -math.floor(count)} end set_combinator_output(map_data, comb, signals) end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 1afd4ba..3156931 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -29,6 +29,43 @@ local function on_failed_delivery(map_data, train) end +---@param map_data MapData +---@param depot Depot +---@param train_id uint +local function add_available_train(map_data, depot, train_id) + if depot.network_name then + local network = map_data.trains_available[depot.network_name] + if not network then + network = {} + map_data.trains_available[depot.network_name] = network + end + network[train_id] = depot.entity_stop.unit_number + end + depot.available_train = train_id + local train = map_data.trains[train_id] + train.depot_name = depot.entity_stop.backer_name + train.depot = depot +end +---@param map_data MapData +---@param depot Depot +function remove_available_train(map_data, depot) + if depot.available_train then + if depot.network_name then + local network = map_data.trains_available[depot.network_name] + if network then + network[depot.available_train] = nil + if next(network) == nil then + map_data.trains_available[depot.network_name] = nil + end + end + end + local train = map_data.trains[depot.available_train] + train.depot = nil + depot.available_train = nil + end +end + + ---@param map_data MapData ---@param stop LuaEntity ---@param comb LuaEntity @@ -43,6 +80,17 @@ local function on_depot_built(map_data, stop, comb, control) map_data.depots[stop.unit_number] = depot end +local function on_depot_broken(map_data, depot) + --remove train + if depot.available_train then + --NOTE: we could remove the schedule from this train + --local train = map_data.trains[depot.available_train] + map_data.trains[depot.available_train] = nil + remove_available_train(map_data, depot) + end + map_data.depots[depot.entity_stop.unit_number] = nil +end + ---@param map_data MapData ---@param stop LuaEntity ---@param comb1 LuaEntity @@ -225,6 +273,7 @@ local function on_combinator_built(map_data, comb) end ---@param map_data MapData ---@param comb LuaEntity +---@param network_name string? function on_combinator_network_updated(map_data, comb, network_name) local stop = map_data.to_stop[comb.unit_number] @@ -237,7 +286,12 @@ function on_combinator_network_updated(map_data, comb, network_name) else local depot = map_data.depots[stop.unit_number] if depot.entity_comb == comb then + if depot.available_train then + remove_available_train(map_data, depot) + add_available_train(map_data, depot, depot.available_train) + end depot.network_name = network_name + end end end @@ -279,7 +333,7 @@ local function on_combinator_broken(map_data, comb) depot.entity_comb = depot_comb depot.network_name = control.first_signal and control.first_signal.name else - map_data.depots[stop.unit_number] = nil + on_depot_broken(map_data, depot) end end end @@ -353,8 +407,12 @@ local function on_stop_broken(map_data, stop) local station = map_data.stations[stop.unit_number] if station then on_station_broken(map_data, stop.unit_number, station) + else + local depot = map_data.depots[stop.unit_number] + if depot then + on_depot_broken(map_data, depot) + end end - map_data.depots[stop.unit_number] = nil end ---@param map_data MapData ---@param stop LuaEntity @@ -379,6 +437,12 @@ local function on_station_rename(map_data, stop) end end end + else + local depot = map_data.depots[station_id] + if depot and depot.available_train then + local train = map_data.trains[depot.available_train] + train.depot_name = stop.backer_name + end end end @@ -395,42 +459,41 @@ local function find_and_add_all_stations_from_nothing(map_data) end end - ---@param map_data MapData ----@param stop LuaEntity +---@param depot Depot ---@param train_entity LuaTrain -local function on_train_arrives_depot(map_data, stop, train_entity) +local function on_train_arrives_depot(map_data, depot, train_entity) local contents = train_entity.get_contents() - local train = map_data.trains[train_entity.id] + local train_id = train_entity.id + local train = map_data.trains[train_id] if train then if train.manifest and train.status == STATUS_R_TO_D then --succeeded delivery train.p_station_id = 0 train.r_station_id = 0 train.manifest = nil - train.depot_name = train_entity.station.backer_name train.status = STATUS_D - map_data.trains_available[stop.unit_number] = train_entity.id + add_available_train(map_data, depot, train_id) else if train.manifest then on_failed_delivery(map_data, train) send_lost_train_alert(train.entity) end - train.depot_name = train_entity.station.backer_name train.status = STATUS_D - map_data.trains_available[stop.unit_number] = train_entity.id + add_available_train(map_data, depot, train_id) end if next(contents) ~= nil then --train still has cargo train_entity.schedule = nil - remove_train(map_data, train, train_entity.id) + remove_train(map_data, train, train_id) send_nonempty_train_in_depot_alert(train_entity) else train_entity.schedule = create_depot_schedule(train.depot_name) end elseif next(contents) == nil then train = { - depot_name = train_entity.station.backer_name, + --depot_name = train_entity.station.backer_name, + --depot = depot, status = STATUS_D, entity = train_entity, layout_id = 0, @@ -441,8 +504,8 @@ local function on_train_arrives_depot(map_data, stop, train_entity) manifest = nil, } update_train_layout(map_data, train) - map_data.trains[train_entity.id] = train - map_data.trains_available[stop.unit_number] = train_entity.id + map_data.trains[train_id] = train + add_available_train(map_data, depot, train_id) local schedule = create_depot_schedule(train.depot_name) train_entity.schedule = schedule else @@ -508,6 +571,8 @@ local function on_train_leaves_station(map_data, train) set_combinator_output(map_data, station.entity_comb1, nil) unset_wagon_combs(map_data, station) end + elseif train.depot then + remove_available_train(map_data, train.depot) end end @@ -605,8 +670,11 @@ local function on_train_changed(event) if stop and stop.valid and stop.name == "train-stop" then if global.stations[stop.unit_number] then on_train_arrives_buffer(global, stop, train) - elseif global.depots[stop.unit_number] then - on_train_arrives_depot(global, stop, train_e) + else + local depot = global.depots[stop.unit_number] + if depot then + on_train_arrives_depot(global, depot, train_e) + end end end elseif event.old_state == defines.train_state.wait_station then From 7f555cedd70ed4711bead09f2732d0c2d701652f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 3 Nov 2022 13:03:52 -0400 Subject: [PATCH 12/66] localized math --- cybersyn/scripts/central-planning.lua | 19 +++++++++++-------- cybersyn/scripts/layout.lua | 3 ++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 139b3cc..1f40126 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -1,6 +1,9 @@ --By Mami local get_distance = require("__flib__.misc").get_distance -local math = math +local min = math.min +local max = math.max +local abs = math.abs +local ceil = math.ceil local INF = math.huge local btest = bit32.btest local band = bit32.band @@ -246,7 +249,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p if effective_item_count >= p_threshold then local r = requests[item_name] if r then - local item = {name = item_name, type = item_type, count = math.min(r, effective_item_count)} + local item = {name = item_name, type = item_type, count = min(r, effective_item_count)} if item_name == primary_item_name then manifest[#manifest + 1] = manifest[1] manifest[1] = item @@ -258,10 +261,10 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p end end - local locked_slots = math.max(p_station.locked_slots, r_station.locked_slots) + local locked_slots = max(p_station.locked_slots, r_station.locked_slots) local total_slots_left = train.item_slot_capacity if locked_slots > 0 then - total_slots_left = math.max(total_slots_left - #train.entity.cargo_wagons*locked_slots, math.min(total_slots_left, #train.entity.cargo_wagons)) + total_slots_left = max(total_slots_left - #train.entity.cargo_wagons*locked_slots, min(total_slots_left, #train.entity.cargo_wagons)) end local total_liquid_left = train.fluid_capacity @@ -279,7 +282,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p end elseif total_slots_left > 0 then local stack_size = game.item_prototypes[item.name].stack_size - local slots = math.ceil(item.count/stack_size) + local slots = ceil(item.count/stack_size) if slots > total_slots_left then item.count = total_slots_left*stack_size end @@ -396,11 +399,11 @@ function tick(map_data, mod_settings) if item_name == SIGNAL_PRIORITY then station.priority = item_count elseif item_name == REQUEST_THRESHOLD then - station.r_threshold = math.abs(item_count) + station.r_threshold = abs(item_count) elseif item_name == PROVIDE_THRESHOLD then - station.p_threshold = math.abs(item_count) + station.p_threshold = abs(item_count) elseif item_name == LOCKED_SLOTS then - station.locked_slots = math.max(item_count, 0) + station.locked_slots = max(item_count, 0) end signals[k] = nil end diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 7ad6c77..fbdab45 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -1,6 +1,7 @@ --By Mami local area = require("__flib__.area") local abs = math.abs +local floor = math.floor local function iterr(a, i) i = i + 1 @@ -255,7 +256,7 @@ function set_r_wagon_combs(map_data, station, train) local inv = carriage.get_fluid_contents() for fluid_name, count in pairs(inv) do local i = #signals + 1 - signals[i] = {index = i, signal = {type = "fluid", name = fluid_name}, count = -math.floor(count)} + signals[i] = {index = i, signal = {type = "fluid", name = fluid_name}, count = -floor(count)} end set_combinator_output(map_data, comb, signals) end From dc5e6f1d52b4d132f5f508d5186b27b6e482698e Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 3 Nov 2022 13:44:37 -0400 Subject: [PATCH 13/66] minor fixes --- cybersyn/scripts/central-planning.lua | 2 +- cybersyn/scripts/layout.lua | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 1f40126..4620bf4 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -102,7 +102,7 @@ local function set_comb2(map_data, station) for item_name, count in pairs(deliveries) do local i = #signals + 1 local item_type = game.item_prototypes[item_name].type - signals[i] = {index = i, signal = {type = item_type, name = item_name}, count = count} + signals[i] = {index = i, signal = {type = item_type, name = item_name}, count = -count} end set_combinator_output(map_data, station.entity_comb2, signals) end diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index fbdab45..476c839 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -242,7 +242,8 @@ function set_r_wagon_combs(map_data, station, train) local signals = {} local inv = carriage.get_inventory(defines.inventory.cargo_wagon) - for stack_i, stack in ipairs(inv) do + for stack_i = 1, #inv do + local stack = inv[stack_i] if stack.valid_for_read then local i = #signals + 1 --TODO: does this work or do we need to aggregate signals? From ebcb906f27e7cb9daa1b09dd2ed55d89c320f429 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 3 Nov 2022 18:30:14 -0400 Subject: [PATCH 14/66] optimized --- .vscode/launch.json | 2 + cybersyn/locale/en/base.cfg | 2 + cybersyn/scripts/central-planning.lua | 324 ++++++++++++++++---------- cybersyn/scripts/constants.lua | 5 + cybersyn/scripts/global.lua | 31 ++- cybersyn/scripts/layout.lua | 2 - cybersyn/scripts/main.lua | 13 +- cybersyn/settings.lua | 11 +- 8 files changed, 242 insertions(+), 148 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index dd52827..3e8c87c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,6 +15,7 @@ "flib": true, "cybersyn": true, "creative-mod": true, + "LogisticTrainNetwork": true, }, "disableExtraMods": true }, @@ -42,6 +43,7 @@ "flib": true, "cybersyn": true, "creative-mod": true, + "LogisticTrainNetwork": true, }, "disableExtraMods": true } diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 1b6e44b..ddccf25 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -2,11 +2,13 @@ cybersyn-ticks-per-second=Dispatcher ticks per second cybersyn-request-threshold=Default requester threshold cybersyn-provide-threshold=Default provider threshold +cybersyn-network-flag=Default network flags [mod-setting-description] cybersyn-ticks-per-second=How many times per second to check all stations for possible deliveries. This value will be rounded up to a divisor of 60. cybersyn-request-threshold=When a requester threshold signal is not recieved by a station it will default to this value. cybersyn-provide-threshold=When a provider threshold signal is not recieved by a station it will default to this value. +cybersyn-network-flag=Choose the default set of networks a station will service when no network signal is provided. This integer is interpretted bit-wise to give 32 possible networks to choose from. [item-name] cybersyn-combinator=Cybernetic combinator diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 4620bf4..5ae7ce6 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -211,9 +211,9 @@ end ---@param p_station_id uint ---@param depot Depot ---@param primary_item_name string ----@param economy Economy -local function send_train_between(map_data, r_station_id, p_station_id, depot, primary_item_name, economy) +local function send_train_between(map_data, r_station_id, p_station_id, depot, primary_item_name) --trains and stations expected to be of the same network + local economy = map_data.economy local r_station = map_data.stations[r_station_id] local p_station = map_data.stations[p_station_id] local train = map_data.trains[depot.available_train] @@ -297,8 +297,8 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p end end - r_station.last_delivery_tick = economy.total_ticks - p_station.last_delivery_tick = economy.total_ticks + r_station.last_delivery_tick = map_data.total_ticks + p_station.last_delivery_tick = map_data.total_ticks r_station.deliveries_total = r_station.deliveries_total + 1 p_station.deliveries_total = p_station.deliveries_total + 1 @@ -310,8 +310,8 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count local item_network_name = network_name..":"..item.name - local r_stations = economy.r_stations_all[item_network_name] - local p_stations = economy.p_stations_all[item_network_name] + local r_stations = economy.all_r_stations[item_network_name] + local p_stations = economy.all_p_stations[item_network_name] for i, id in ipairs(r_stations) do if id == r_station_id then table.remove(r_stations, i) @@ -337,56 +337,80 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p set_comb2(map_data, r_station) end ----@param map_data MapData -function tick(map_data, mod_settings) - local total_ticks = map_data.total_ticks - local stations = map_data.stations - ---@type Economy - local economy = { - r_stations_all = {}, - p_stations_all = {}, - total_ticks = total_ticks, - } - local r_stations_all = economy.r_stations_all - local p_stations_all = economy.p_stations_all - local all_names = {} - for _, network in pairs(map_data.trains_available) do - for _, depot_id in pairs(network) do - local depot = map_data.depots[depot_id] - local comb = depot.entity_comb - if depot.network_name and comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then - depot.priority = 0 - depot.network_flag = 1 - local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) - if signals then - for k, v in pairs(signals) do - local item_name = v.signal.name - local item_count = v.count - if item_name then - if item_name == SIGNAL_PRIORITY then - depot.priority = item_count - end - if item_name == depot.network_name then - depot.network_flag = item_count - end - end - end + +---@param map_data MapData +local function tick_poll_depot(map_data) + local depot_id + do--get next depot id + local tick_data = map_data.tick_data + while true do + if tick_data.network == nil then + tick_data.network_name, tick_data.network = next(map_data.trains_available) + if tick_data.network == nil then + tick_data.train_id = nil + map_data.tick_state = STATE_POLL_STATIONS + return true end + end + + tick_data.train_id, depot_id = next(tick_data.network, tick_data.train_id) + if depot_id then + break else - depot.priority = 0 - depot.network_flag = 0 + tick_data.network = nil end end end - for station_id, station in pairs(stations) do + local depot = map_data.depots[depot_id] + local comb = depot.entity_comb + if depot.network_name and comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then + depot.priority = 0 + depot.network_flag = 1 + local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) + if signals then + for k, v in pairs(signals) do + local item_name = v.signal.name + local item_count = v.count + if item_name then + if item_name == SIGNAL_PRIORITY then + depot.priority = item_count + end + if item_name == depot.network_name then + depot.network_flag = item_count + end + end + end + end + else + depot.priority = 0 + depot.network_flag = 0 + end + return false +end +---@param map_data MapData +---@param mod_settings CybersynModSettings +local function tick_poll_station(map_data, mod_settings) + local tick_data = map_data.tick_data + local all_r_stations = map_data.economy.all_r_stations + local all_p_stations = map_data.economy.all_p_stations + local all_names = map_data.economy.all_names + + while true do + local station_id, station = next(map_data.stations, tick_data.station_id) + tick_data.station_id = station_id + if station == nil then + map_data.tick_state = STATE_DISPATCH + return true + end + if station.network_name and station.deliveries_total < station.entity_stop.trains_limit then station.r_threshold = mod_settings.r_threshold station.p_threshold = mod_settings.p_threshold station.priority = 0 station.locked_slots = 0 - station.network_flag = 1 + station.network_flag = mod_settings.network_flag local signals = get_signals(station) station.tick_signals = signals if signals then @@ -422,111 +446,153 @@ function tick(map_data, mod_settings) if -effective_item_count >= r_threshold then local item_network_name = station.network_name..":"..item_name - if r_stations_all[item_network_name] == nil then - r_stations_all[item_network_name] = {} - p_stations_all[item_network_name] = {} + local stations = all_r_stations[item_network_name] + if stations == nil then + stations = {} + all_r_stations[item_network_name] = stations all_names[#all_names + 1] = item_network_name all_names[#all_names + 1] = v.signal end - table.insert(r_stations_all[item_network_name], station_id) + stations[#stations + 1] = station_id elseif effective_item_count >= p_threshold then local item_network_name = station.network_name..":"..item_name - if r_stations_all[item_network_name] == nil then - r_stations_all[item_network_name] = {} - p_stations_all[item_network_name] = {} - all_names[#all_names + 1] = item_network_name - all_names[#all_names + 1] = v.signal + local stations = all_p_stations[item_network_name] + if stations == nil then + stations = {} + all_p_stations[item_network_name] = stations end - table.insert(p_stations_all[item_network_name], station_id) + stations[#stations + 1] = station_id end end end + return false end end - +end +---@param map_data MapData +---@param mod_settings CybersynModSettings +local function tick_dispatch(map_data, mod_settings) --we do not dispatch more than one train per station per tick --psuedo-randomize what item (and what station) to check first so if trains available is low they choose orders psuedo-randomly --NOTE: It may be better for performance to update stations one tick at a time rather than all at once, however this does mean more redundant data will be generated and discarded each tick. Once we have a performance test-bed it will probably be worth checking. - local start_i = 2*(total_ticks%(#all_names/2)) + 1 - for item_i = 0, #all_names - 1, 2 do - local item_network_name = all_names[(start_i + item_i - 1)%#all_names + 1] - local signal = all_names[(start_i + item_i)%#all_names + 1] - local item_name = signal.name - local item_type = signal.type - local r_stations = r_stations_all[item_network_name] - local p_stations = p_stations_all[item_network_name] + local tick_data = map_data.tick_data + local all_r_stations = map_data.economy.all_r_stations + local all_p_stations = map_data.economy.all_p_stations + local all_names = map_data.economy.all_names + local stations = map_data.stations - --NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic - if #r_stations > 0 and #p_stations > 0 then - if #r_stations <= #p_stations then - --probably backpressure, prioritize locality - repeat - local i = total_ticks%#r_stations + 1 - local r_station_id = table.remove(r_stations, i) + local size = #all_names + if tick_data.start_i == nil and size > 0 then + --semi-randomized starting item + tick_data.start_i = 2*(map_data.total_ticks%(size/2)) + 1 + tick_data.offset_i = 0 + elseif size == 0 or tick_data.offset_i >= size then + tick_data.start_i = nil + tick_data.offset_i = nil + map_data.tick_state = STATE_INIT + return true + end + local name_i = tick_data.start_i + tick_data.offset_i + tick_data.offset_i = tick_data.offset_i + 2 - local best = 0 - local best_depot = nil - local best_dist = INF - local highest_prior = -INF - local could_have_been_serviced = false - for j, p_station_id in ipairs(p_stations) do + local item_network_name = all_names[(name_i - 1)%size + 1] + local signal = all_names[(name_i)%size + 1] + local item_name = signal.name + local item_type = signal.type + local r_stations = all_r_stations[item_network_name] + local p_stations = all_p_stations[item_network_name] + + --NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic + if p_stations and #r_stations > 0 and #p_stations > 0 then + if #r_stations <= #p_stations then + --probably backpressure, prioritize locality + repeat + local i = map_data.total_ticks%#r_stations + 1 + local r_station_id = table.remove(r_stations, i) + + local best = 0 + local best_depot = nil + local best_dist = INF + local highest_prior = -INF + local could_have_been_serviced = false + for j, p_station_id in ipairs(p_stations) do + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) + local prior = stations[p_station_id].priority + if prior > highest_prior or (prior == highest_prior and d < best_dist) then + if depot then + best = j + best_dist = d + best_depot = depot + highest_prior = prior + elseif d < INF then + could_have_been_serviced = true + best = j + end + end + end + if best_depot then + send_train_between(map_data, r_station_id, p_stations[best], best_depot, item_name) + elseif could_have_been_serviced then + send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) + end + until #r_stations == 0 + else + --prioritize round robin + repeat + local j = map_data.total_ticks%#p_stations + 1 + local p_station_id = table.remove(p_stations, j) + + local best = 0 + local best_depot = nil + local lowest_tick = INF + local highest_prior = -INF + local could_have_been_serviced = false + for i, r_station_id in ipairs(r_stations) do + local r_station = stations[r_station_id] + local prior = r_station.priority + if prior > highest_prior or (prior == highest_prior and r_station.last_delivery_tick < lowest_tick) then local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) - local prior = stations[p_station_id].priority - if prior > highest_prior or (prior == highest_prior and d < best_dist) then - if depot then - best = j - best_dist = d - best_depot = depot - highest_prior = prior - elseif d < INF then - could_have_been_serviced = true - best = j - end + if depot then + best = i + best_depot = depot + lowest_tick = r_station.last_delivery_tick + highest_prior = prior + elseif d < INF then + could_have_been_serviced = true + best = i end end - if best_depot then - send_train_between(map_data, r_station_id, p_stations[best], best_depot, item_name, economy) - elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) - end - until #r_stations == 0 - else - --prioritize round robin - repeat - local j = total_ticks%#p_stations + 1 - local p_station_id = table.remove(p_stations, j) - - local best = 0 - local best_depot = nil - local lowest_tick = INF - local highest_prior = -INF - local could_have_been_serviced = false - for i, r_station_id in ipairs(r_stations) do - local r_station = stations[r_station_id] - local prior = r_station.priority - if prior > highest_prior or (prior == highest_prior and r_station.last_delivery_tick < lowest_tick) then - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) - if depot then - best = i - best_depot = depot - lowest_tick = r_station.last_delivery_tick - highest_prior = prior - elseif d < INF then - could_have_been_serviced = true - best = i - end - end - end - if best_depot then - send_train_between(map_data, r_stations[best], p_station_id, best_depot, item_name, economy) - elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[r_stations[best]].entity_stop, stations[p_station_id].entity_stop) - end - until #p_stations == 0 - end + end + if best_depot then + send_train_between(map_data, r_stations[best], p_station_id, best_depot, item_name) + elseif could_have_been_serviced then + send_missing_train_alert_for_stops(stations[r_stations[best]].entity_stop, stations[p_station_id].entity_stop) + end + until #p_stations == 0 end end - for station_id, station in pairs(stations) do - station.tick_signals = nil + return false +end +---@param map_data MapData +---@param mod_settings CybersynModSettings +function tick(map_data, mod_settings) + if map_data.tick_state == STATE_INIT then + map_data.total_ticks = map_data.total_ticks + 1 + map_data.economy.all_p_stations = {} + map_data.economy.all_r_stations = {} + map_data.economy.all_names = {} + map_data.tick_state = STATE_POLL_DEPOTS + end + + if map_data.tick_state == STATE_POLL_DEPOTS then + for i = 1, 3 do + if tick_poll_depot(map_data) then break end + end + elseif map_data.tick_state == STATE_POLL_STATIONS then + for i = 1, 2 do + if tick_poll_station(map_data, mod_settings) then break end + end + elseif map_data.tick_state == STATE_DISPATCH then + tick_dispatch(map_data, mod_settings) end end diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 5012d5c..d8e1de2 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -41,3 +41,8 @@ STATION_LAYOUT_NOT_FLUID = "[NC]" STATION_LAYOUT_NOT_CARGO = "[NF]" LONGEST_INSERTER_REACH = 2 + +STATE_INIT = 0 +STATE_POLL_DEPOTS = 1 +STATE_POLL_STATIONS = 2 +STATE_DISPATCH = 3 diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 88874ab..15cdae4 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -11,7 +11,9 @@ ---@field public trains_available {[string]: {[uint]: uint}} --{[network_name]: {[train_id]: depot_id}} ---@field public layouts {[uint]: string} ---@field public layout_train_count {[uint]: int} ----@field public train_classes {[string]: TrainClass} +---@field public tick_state uint +---@field public tick_data {} +---@field public economy Economy ---@class Station ---@field public deliveries_total int @@ -57,17 +59,33 @@ ---@alias cybersyn.global MapData ---@class Economy ----@field public r_stations_all {[string]: uint[]} ----@field public p_stations_all {[string]: uint[]} ----@field public total_ticks uint +---@field public all_r_stations {[string]: uint[]} --{[network_name:item_name]: count} +---@field public all_p_stations {[string]: uint[]} --{[network_name:item_name]: count} +---@field public all_names {[string]: uint[]} --{[network_name:item_name]: count} ---TODO: only init once +---@class CybersynModSettings +---@field public tps int +---@field public r_threshold int +---@field public p_threshold int +---@field public network_flag int + + +--TODO: only init once and move settings code +---@type CybersynModSettings mod_settings = {} mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value +mod_settings.network_flag = settings.global["cybersyn-network-flag"].value global.total_ticks = 0 +global.tick_state = STATE_INIT +global.tick_data = {} +global.economy = { + all_r_stations = {}, + all_p_stations = {}, + all_names = {}, +} global.to_comb = {} global.to_output = {} global.to_stop = {} @@ -78,6 +96,3 @@ global.trains_available = {} global.layouts = {} global.layout_train_count = {} global.layout_top_id = 1 -global.train_classes = { - --[TRAIN_CLASS_ALL.name] = {}, -} diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 476c839..afbc365 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -32,7 +32,6 @@ function remove_train(map_data, train, train_id) for station_id, station in pairs(map_data.stations) do station.accepted_layouts[layout_id] = nil end - --map_data.train_classes[TRAIN_CLASS_ALL][layout_id] = nil else map_data.layout_train_count[layout_id] = count - 1 end @@ -81,7 +80,6 @@ function update_train_layout(map_data, train) station.accepted_layouts[layout_id] = true end end - --map_data.train_classes[TRAIN_CLASS_ALL][layout_id] = true else map_data.layout_train_count[layout_id] = map_data.layout_train_count[layout_id] + 1 end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 3156931..ef7ec91 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -325,7 +325,7 @@ local function on_combinator_broken(map_data, comb) end else local depot = map_data.depots[stop.unit_number] - if depot.entity_comb == comb then + if depot and depot.entity_comb == comb then --NOTE: this will disrupt deliveries in progress that where dispatched from this station in a minor way local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) if depot_comb then @@ -538,7 +538,7 @@ local function on_train_arrives_buffer(map_data, stop, train) local station = map_data.stations[station_id] local signals = {} for i, item in ipairs(train.manifest) do - signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = -1} + signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = -item.count} end set_combinator_output(map_data, station.entity_comb1, signals) set_r_wagon_combs(map_data, station, train) @@ -605,11 +605,6 @@ local function on_train_modified(map_data, pre_train_id, train_entity) end -local function on_tick(event) - tick(global, mod_settings) - global.total_ticks = global.total_ticks + 1 -end - local function on_built(event) local entity = event.entity or event.created_entity or event.destination if not entity or not entity.valid then return end @@ -740,7 +735,9 @@ local function register_events() flib_event.register(defines.events.on_entity_settings_pasted, on_paste) local nth_tick = math.ceil(60/mod_settings.tps); - flib_event.on_nth_tick(nth_tick, on_tick) + flib_event.on_nth_tick(nth_tick, function(event) + tick(global, mod_settings) + end) flib_event.register(defines.events.on_train_created, on_train_built) flib_event.register(defines.events.on_train_changed_state, on_train_changed) diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index be2a1c9..ddc7cbb 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -5,7 +5,7 @@ data:extend({ name = "cybersyn-ticks-per-second", order = "aa", setting_type = "runtime-global", - default_value = 10, + default_value = 30, minimum_value = 1, maximum_value = 60, }, @@ -27,4 +27,13 @@ data:extend({ minimum_value = 1, maximum_value = 2147483647, }, + { + type = "int-setting", + name = "cybersyn-network-flag", + order = "ad", + setting_type = "runtime-global", + default_value = 1, + minimum_value = -1, + maximum_value = 2147483647, + }, }) From 190741715d2d5ea658ec0156ec34473d1b15187d Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 4 Nov 2022 01:00:28 -0400 Subject: [PATCH 15/66] created alpha --- .vscode/launch.json | 1 - cybersyn/TODO | 2 +- cybersyn/locale/en/base.cfg | 30 +++++++++++++-------------- cybersyn/scripts/central-planning.lua | 26 ++++++++++------------- cybersyn/scripts/constants.lua | 1 + cybersyn/scripts/global.lua | 9 ++------ cybersyn/scripts/gui.lua | 4 ++-- cybersyn/scripts/layout.lua | 5 +---- cybersyn/scripts/main.lua | 6 ++++++ cybersyn/settings.lua | 4 ++-- 10 files changed, 41 insertions(+), 47 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 3e8c87c..7fd5b23 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -29,7 +29,6 @@ "debugadapter": true, "flib": true, "cybersyn": true, - "creative-mod": true, }, "disableExtraMods": true }, diff --git a/cybersyn/TODO b/cybersyn/TODO index 3756855..9a58a0e 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -1,5 +1,5 @@ close gui when the combinator is destroyed -play close sound when gui is closed +do not play close sound when a different gui is opened improve localization support space elevator do hardcore testing diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index ddccf25..59ef511 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -1,34 +1,34 @@ [mod-setting-name] -cybersyn-ticks-per-second=Dispatcher ticks per second +cybersyn-ticks-per-second=Dispatcher updates per second cybersyn-request-threshold=Default requester threshold cybersyn-provide-threshold=Default provider threshold cybersyn-network-flag=Default network flags [mod-setting-description] -cybersyn-ticks-per-second=How many times per second to check all stations for possible deliveries. This value will be rounded up to a divisor of 60. -cybersyn-request-threshold=When a requester threshold signal is not recieved by a station it will default to this value. -cybersyn-provide-threshold=When a provider threshold signal is not recieved by a station it will default to this value. -cybersyn-network-flag=Choose the default set of networks a station will service when no network signal is provided. This integer is interpretted bit-wise to give 32 possible networks to choose from. +cybersyn-ticks-per-second=How many times per second the dispather should check for new deliveries. Deliveries are made one at a time per update. This value will be rounded up to a divisor of 60. +cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. Huge values will prevent stations from taking requests from the network unless an explicit threshold is set. +cybersyn-provide-threshold=The default provide threshold when a provide threshold signal is not given to a station. Huge values will prevent stations from providing to the network unless an explicit threshold is set. +cybersyn-network-flag=The default set of networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible network flags to choose from. [item-name] cybersyn-combinator=Cybernetic combinator [item-description] -cybersyn-combinator=Cybernetic combinator +cybersyn-combinator=Place next to a train stop to add it to the cybersyn network. Adjacent stations can now request or provide items or fluids by train. Has 4 different operation modes. [entity-name] cybersyn-combinator=Cybernetic combinator cybersyn-combinator-output=NA [entity-description] -cybersyn-combinator=Cybersyn depot +cybersyn-combinator=Place next to a train stop to add it to the cybersyn network. Has 4 different operation modes. cybersyn-combinator-output=NA [technology-name] cybersyn-train-network=Cybernetic train network [technology-description] -cybersyn-train-network=Cybernetic train network +cybersyn-train-network=Train station controllers capable of coordinating the inputs and outputs of an entire economy. [virtual-signal-name] cybersyn-priority=Station priority @@ -42,11 +42,11 @@ lost-train=A train has become lost nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty [cybersyn-gui] -operation=Combinator type -network=Network -comb1=Primary controller -comb2=Secondary station control -depot=Depot -wagon-manifest=Wagon combinator-title=Cybernetic combinator -auto-description=Station automatically decides which trains in the network it can service +operation=Mode +comb1=Primary station control +comb2=Optional station control +depot=Depot control +wagon-manifest=Wagon control +network=Network +auto-description=Station automatically decides which trains in the network it can service (all trains are allowed by default) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 5ae7ce6..375b3fa 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -85,11 +85,7 @@ function set_combinator_output(map_data, comb, signals) local out = map_data.to_output[comb.unit_number] if out.valid then out.get_or_create_control_behavior().parameters = signals - else - --TODO: error logging? end - else - --TODO: error logging? end end @@ -101,7 +97,7 @@ local function set_comb2(map_data, station) local signals = {} for item_name, count in pairs(deliveries) do local i = #signals + 1 - local item_type = game.item_prototypes[item_name].type + local item_type = game.item_prototypes[item_name].type--NOTE: this is expensive signals[i] = {index = i, signal = {type = item_type, name = item_name}, count = -count} end set_combinator_output(map_data, station.entity_comb2, signals) @@ -124,7 +120,6 @@ function remove_manifest(map_data, station, manifest, sign) end ---@param map_data MapData ----@param station Station ---@param signal SignalID local function get_thresholds(map_data, station, signal) local comb2 = station.entity_comb2 @@ -229,10 +224,8 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p ---@type string local item_name = v.signal.name local item_count = v.count - --local item_type = v.signal.type local effective_item_count = item_count + (r_station.deliveries[item_name] or 0) - local r_threshold, p_threshold = get_thresholds(map_data, r_station, v.signal) - if -effective_item_count >= r_threshold then + if effective_item_count < 0 and item_count < 0 then requests[item_name] = -effective_item_count end end @@ -245,8 +238,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p local item_count = v.count local item_type = v.signal.type local effective_item_count = item_count + (p_station.deliveries[item_name] or 0) - local r_threshold, p_threshold = get_thresholds(map_data, p_station, v.signal) - if effective_item_count >= p_threshold then + if effective_item_count > 0 and item_count > 0 then local r = requests[item_name] if r then local item = {name = item_name, type = item_type, count = min(r, effective_item_count)} @@ -312,6 +304,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p local item_network_name = network_name..":"..item.name local r_stations = economy.all_r_stations[item_network_name] local p_stations = economy.all_p_stations[item_network_name] + --NOTE: one of these will be redundant for i, id in ipairs(r_stations) do if id == r_station_id then table.remove(r_stations, i) @@ -422,9 +415,10 @@ local function tick_poll_station(map_data, mod_settings) if item_type == "virtual" then if item_name == SIGNAL_PRIORITY then station.priority = item_count - elseif item_name == REQUEST_THRESHOLD then + elseif item_name == REQUEST_THRESHOLD and item_count ~= 0 then + --NOTE: thresholds must be >0 or they will cause a crash station.r_threshold = abs(item_count) - elseif item_name == PROVIDE_THRESHOLD then + elseif item_name == PROVIDE_THRESHOLD and item_count ~= 0 then station.p_threshold = abs(item_count) elseif item_name == LOCKED_SLOTS then station.locked_slots = max(item_count, 0) @@ -444,7 +438,7 @@ local function tick_poll_station(map_data, mod_settings) local effective_item_count = item_count + (station.deliveries[item_name] or 0) local r_threshold, p_threshold = get_thresholds(map_data, station, v.signal) - if -effective_item_count >= r_threshold then + if -effective_item_count >= r_threshold and -item_count >= r_threshold then local item_network_name = station.network_name..":"..item_name local stations = all_r_stations[item_network_name] if stations == nil then @@ -454,7 +448,7 @@ local function tick_poll_station(map_data, mod_settings) all_names[#all_names + 1] = v.signal end stations[#stations + 1] = station_id - elseif effective_item_count >= p_threshold then + elseif effective_item_count >= p_threshold and item_count >= p_threshold then local item_network_name = station.network_name..":"..item_name local stations = all_p_stations[item_network_name] if stations == nil then @@ -462,6 +456,8 @@ local function tick_poll_station(map_data, mod_settings) all_p_stations[item_network_name] = stations end stations[#stations + 1] = station_id + else + signals[k] = nil end end end diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index d8e1de2..4e15cdd 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -11,6 +11,7 @@ LOCKED_SLOTS = "cybersyn-locked-slots" COMBINATOR_NAME = "cybersyn-combinator" COMBINATOR_OUT_NAME = "cybersyn-combinator-output" +COMBINATOR_CLOSE_SOUND = "entity-close/cybersyn-combinator" OPERATION_DEFAULT = "*" OPERATION_PRIMARY_IO = "/" diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 15cdae4..d6a2ae9 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -32,7 +32,7 @@ ---@field public is_all boolean ---@field public accepted_layouts TrainClass ---@field public layout_pattern string? ----@field public tick_signals Signal[]? --transient +---@field public tick_signals {[uint]: Signal}? --transient ---@class Depot ---@field public priority int --transient @@ -69,15 +69,10 @@ ---@field public p_threshold int ---@field public network_flag int - ---TODO: only init once and move settings code ---@type CybersynModSettings mod_settings = {} -mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value -mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value -mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value -mod_settings.network_flag = settings.global["cybersyn-network-flag"].value +--TODO: guarantee this only inits once global.total_ticks = 0 global.tick_state = STATE_INIT global.tick_data = {} diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index edb05df..a31938b 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -118,7 +118,7 @@ local function on_gui_closed(event) if rootgui[COMBINATOR_NAME] then rootgui[COMBINATOR_NAME].destroy() - --TODO: play close sound to player + player.play_sound({path = COMBINATOR_CLOSE_SOUND}) end end @@ -133,7 +133,7 @@ function register_gui_actions() if msg[1] == "close" then if rootgui[COMBINATOR_NAME] then rootgui[COMBINATOR_NAME].destroy() - --TODO: play close sound to player + player.play_sound({path = COMBINATOR_CLOSE_SOUND}) end elseif msg[1] == "drop-down" then local element = event.element diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index afbc365..24cba3e 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -244,7 +244,6 @@ function set_r_wagon_combs(map_data, station, train) local stack = inv[stack_i] if stack.valid_for_read then local i = #signals + 1 - --TODO: does this work or do we need to aggregate signals? signals[i] = {index = i, signal = {type = stack.type, name = stack.name}, count = -stack.count} end end @@ -406,7 +405,6 @@ local function reset_station_layout(map_data, station, forbidden_entity) if supports_fluid then layout_pattern = layout_pattern..STATION_LAYOUT_ALL else - --TODO: needs to allow misc wagons as well layout_pattern = layout_pattern..STATION_LAYOUT_NOT_FLUID end pattern_length = #layout_pattern @@ -456,7 +454,7 @@ end ---@param rail LuaEntity ---@param forbidden_entity LuaEntity? function update_station_from_rail(map_data, rail, forbidden_entity) - --TODO: search further or better? + --NOTE: should we search further or better? it would be more expensive local entity = rail.get_rail_segment_entity(defines.rail_direction.back, false) if entity and entity.valid and entity.name == "train-stop" then local station = map_data.stations[entity.unit_number] @@ -485,7 +483,6 @@ end ---@param inserter LuaEntity ---@param forbidden_entity LuaEntity? function update_station_from_inserter(map_data, inserter, forbidden_entity) - --TODO: check if correct local surface = inserter.surface local rail = surface.find_entity("straight-rail", inserter.pickup_position) diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index ef7ec91..03345bb 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -720,6 +720,12 @@ local filter_comb = { {filter = "type", type = "arithmetic-combinator"}, } local function register_events() + + mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] + mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] + mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value--[[@as int]] + mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] + --NOTE: I have no idea if this correctly registers all events once in all situations flib_event.register(defines.events.on_built_entity, on_built, filter_built) flib_event.register(defines.events.on_robot_built_entity, on_built, filter_built) diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index ddc7cbb..dadd6e2 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -14,7 +14,7 @@ data:extend({ name = "cybersyn-request-threshold", order = "ab", setting_type = "runtime-global", - default_value = 1000000000, + default_value = 2000000000, minimum_value = 1, maximum_value = 2147483647, }, @@ -23,7 +23,7 @@ data:extend({ name = "cybersyn-provide-threshold", order = "ac", setting_type = "runtime-global", - default_value = 1000000000, + default_value = 2000000000, minimum_value = 1, maximum_value = 2147483647, }, From 8e3bc00492be3fb1acde2cede08fc864f2512800 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 4 Nov 2022 18:01:17 -0400 Subject: [PATCH 16/66] improved algorithm --- cybersyn/scripts/central-planning.lua | 150 +++++++++++--------------- 1 file changed, 65 insertions(+), 85 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 375b3fa..d84d696 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -7,6 +7,7 @@ local ceil = math.ceil local INF = math.huge local btest = bit32.btest local band = bit32.band +local table_remove = table.remove local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ---@param stop LuaEntity @@ -295,26 +296,28 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p r_station.deliveries_total = r_station.deliveries_total + 1 p_station.deliveries_total = p_station.deliveries_total + 1 - for _, item in ipairs(manifest) do + assert(manifest[1].name == primary_item_name) + for item_i, item in ipairs(manifest) do assert(item.count > 0, "main.lua error, transfer amount was not positive") r_station.deliveries[item.name] = (r_station.deliveries[item.name] or 0) + item.count p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count - local item_network_name = network_name..":"..item.name - local r_stations = economy.all_r_stations[item_network_name] - local p_stations = economy.all_p_stations[item_network_name] - --NOTE: one of these will be redundant - for i, id in ipairs(r_stations) do - if id == r_station_id then - table.remove(r_stations, i) - break + if item_i > 1 then + local item_network_name = network_name..":"..item.name + local r_stations = economy.all_r_stations[item_network_name] + local p_stations = economy.all_p_stations[item_network_name] + for j, id in ipairs(r_stations) do + if id == r_station_id then + table_remove(r_stations, j) + break + end end - end - for i, id in ipairs(p_stations) do - if id == p_station_id then - table.remove(p_stations, i) - break + for j, id in ipairs(p_stations) do + if id == p_station_id then + table_remove(p_stations, j) + break + end end end end @@ -339,7 +342,7 @@ local function tick_poll_depot(map_data) local tick_data = map_data.tick_data while true do if tick_data.network == nil then - tick_data.network_name, tick_data.network = next(map_data.trains_available) + tick_data.network_name, tick_data.network = next(map_data.trains_available, tick_data.network_name) if tick_data.network == nil then tick_data.train_id = nil map_data.tick_state = STATE_POLL_STATIONS @@ -478,13 +481,18 @@ local function tick_dispatch(map_data, mod_settings) local stations = map_data.stations local size = #all_names - if tick_data.start_i == nil and size > 0 then - --semi-randomized starting item - tick_data.start_i = 2*(map_data.total_ticks%(size/2)) + 1 - tick_data.offset_i = 0 - elseif size == 0 or tick_data.offset_i >= size then - tick_data.start_i = nil - tick_data.offset_i = nil + if size > 0 then + if tick_data.start_i == nil then + --semi-randomized starting item + tick_data.start_i = 2*(map_data.total_ticks%(size/2)) + 1 + tick_data.offset_i = 0 + elseif tick_data.offset_i >= size then + tick_data.start_i = nil + tick_data.offset_i = nil + map_data.tick_state = STATE_INIT + return true + end + else map_data.tick_state = STATE_INIT return true end @@ -500,72 +508,44 @@ local function tick_dispatch(map_data, mod_settings) --NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic if p_stations and #r_stations > 0 and #p_stations > 0 then - if #r_stations <= #p_stations then - --probably backpressure, prioritize locality - repeat - local i = map_data.total_ticks%#r_stations + 1 - local r_station_id = table.remove(r_stations, i) + table.sort(r_stations, function(a_id, b_id) + local a = stations[a_id] + local b = stations[b_id] + if a.priority ~= b.priority then + return a.priority < b.priority + else + return a.last_delivery_tick > b.last_delivery_tick + end + end) + repeat + local r_station_id = table_remove(r_stations) - local best = 0 - local best_depot = nil - local best_dist = INF - local highest_prior = -INF - local could_have_been_serviced = false - for j, p_station_id in ipairs(p_stations) do - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) - local prior = stations[p_station_id].priority - if prior > highest_prior or (prior == highest_prior and d < best_dist) then - if depot then - best = j - best_dist = d - best_depot = depot - highest_prior = prior - elseif d < INF then - could_have_been_serviced = true - best = j - end + local best = 0 + local best_depot = nil + local best_dist = INF + local highest_prior = -INF + local could_have_been_serviced = false + for j, p_station_id in ipairs(p_stations) do + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) + local prior = stations[p_station_id].priority + if prior > highest_prior or (prior == highest_prior and d < best_dist) then + if depot then + best = j + best_dist = d + best_depot = depot + highest_prior = prior + elseif d < INF then + could_have_been_serviced = true + best = j end end - if best_depot then - send_train_between(map_data, r_station_id, p_stations[best], best_depot, item_name) - elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) - end - until #r_stations == 0 - else - --prioritize round robin - repeat - local j = map_data.total_ticks%#p_stations + 1 - local p_station_id = table.remove(p_stations, j) - - local best = 0 - local best_depot = nil - local lowest_tick = INF - local highest_prior = -INF - local could_have_been_serviced = false - for i, r_station_id in ipairs(r_stations) do - local r_station = stations[r_station_id] - local prior = r_station.priority - if prior > highest_prior or (prior == highest_prior and r_station.last_delivery_tick < lowest_tick) then - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) - if depot then - best = i - best_depot = depot - lowest_tick = r_station.last_delivery_tick - highest_prior = prior - elseif d < INF then - could_have_been_serviced = true - best = i - end - end - end - if best_depot then - send_train_between(map_data, r_stations[best], p_station_id, best_depot, item_name) - elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[r_stations[best]].entity_stop, stations[p_station_id].entity_stop) - end - until #p_stations == 0 - end + end + if best_depot then + send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) + elseif could_have_been_serviced then + send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) + end + until #r_stations == 0 end return false end From 1cb283c1fdd88dfb11f6c07c86bf076661e66eb2 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 4 Nov 2022 18:01:37 -0400 Subject: [PATCH 17/66] better localization --- cybersyn/TODO | 1 - cybersyn/locale/en/base.cfg | 10 +++++----- cybersyn/scripts/layout.lua | 21 +++++++++++++++++++++ cybersyn/scripts/main.lua | 11 +++++++---- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/cybersyn/TODO b/cybersyn/TODO index 9a58a0e..8092850 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -1,6 +1,5 @@ close gui when the combinator is destroyed do not play close sound when a different gui is opened -improve localization support space elevator do hardcore testing optimizations? diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 59ef511..4a39218 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -14,21 +14,21 @@ cybersyn-network-flag=The default set of networks a station will service when no cybersyn-combinator=Cybernetic combinator [item-description] -cybersyn-combinator=Place next to a train stop to add it to the cybersyn network. Adjacent stations can now request or provide items or fluids by train. Has 4 different operation modes. +cybersyn-combinator=Place next to a train stop to add it to the cybersyn train network. This stop can now request or provide items using the circuit network. Be sure to set a threshold signal. [entity-name] cybersyn-combinator=Cybernetic combinator -cybersyn-combinator-output=NA +cybersyn-combinator-output=Cybernetic combinator output [entity-description] -cybersyn-combinator=Place next to a train stop to add it to the cybersyn network. Has 4 different operation modes. -cybersyn-combinator-output=NA +cybersyn-combinator=Has 4 different operation modes. Primary control allows providing and requesting. Optional Control allows setting thresholds per-item and reading all in progress deliveries. Depot control allows parked trains to be added to the network. Wagon control allows for reading the desired contents of the adjacent wagon. +cybersyn-combinator-output=¡Viva la Revolución! [technology-name] cybersyn-train-network=Cybernetic train network [technology-description] -cybersyn-train-network=Train station controllers capable of coordinating the inputs and outputs of an entire economy. +cybersyn-train-network=Train stop controllers capable of coordinating the inputs and outputs of an entire economy. [virtual-signal-name] cybersyn-priority=Station priority diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 24cba3e..1d88205 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -450,6 +450,27 @@ function update_station_if_auto(map_data, station, forbidden_entity) end end +---@param map_data MapData +---@param rail LuaEntity +---@param forbidden_entity LuaEntity? +function force_update_station_from_rail(map_data, rail, forbidden_entity) + --NOTE: should we search further or better? it would be more expensive + local entity = rail.get_rail_segment_entity(defines.rail_direction.back, false) + if entity and entity.valid and entity.name == "train-stop" then + local station = map_data.stations[entity.unit_number] + if station then + reset_station_layout(map_data, station, forbidden_entity) + end + else + entity = rail.get_rail_segment_entity(defines.rail_direction.front, false) + if entity and entity.valid and entity.name == "train-stop" then + local station = map_data.stations[entity.unit_number] + if station then + reset_station_layout(map_data, station, forbidden_entity) + end + end + end +end ---@param map_data MapData ---@param rail LuaEntity ---@param forbidden_entity LuaEntity? diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 03345bb..3d48b97 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -231,7 +231,7 @@ local function on_combinator_built(map_data, comb) end if control.operation == OPERATION_WAGON_MANIFEST then if rail then - update_station_from_rail(map_data, rail, nil) + force_update_station_from_rail(map_data, rail, nil) end elseif control.operation == OPERATION_DEPOT then if stop then @@ -287,11 +287,14 @@ function on_combinator_network_updated(map_data, comb, network_name) local depot = map_data.depots[stop.unit_number] if depot.entity_comb == comb then if depot.available_train then + ---@type uint + local train_id = depot.available_train remove_available_train(map_data, depot) - add_available_train(map_data, depot, depot.available_train) + depot.network_name = network_name + add_available_train(map_data, depot, train_id) + else + depot.network_name = network_name end - depot.network_name = network_name - end end end From d7341865081cc7f59035e45aab6e9d8a085b8eca Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 4 Nov 2022 18:05:05 -0400 Subject: [PATCH 18/66] cleared warning --- cybersyn/scripts/layout.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 1d88205..51b070e 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -237,17 +237,18 @@ function set_r_wagon_combs(map_data, station, train) end end if comb and carriage.type == "cargo-wagon" then - local signals = {} - local inv = carriage.get_inventory(defines.inventory.cargo_wagon) - for stack_i = 1, #inv do - local stack = inv[stack_i] - if stack.valid_for_read then - local i = #signals + 1 - signals[i] = {index = i, signal = {type = stack.type, name = stack.name}, count = -stack.count} + if inv then + local signals = {} + for stack_i = 1, #inv do + local stack = inv[stack_i] + if stack.valid_for_read then + local i = #signals + 1 + signals[i] = {index = i, signal = {type = stack.type, name = stack.name}, count = -stack.count} + end end + set_combinator_output(map_data, comb, signals) end - set_combinator_output(map_data, comb, signals) elseif comb and carriage.type == "fluid-wagon" then local signals = {} From 9f027d778f6ad56a8f489504284586108241eb6f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 4 Nov 2022 18:07:24 -0400 Subject: [PATCH 19/66] updated todo --- cybersyn/TODO | 1 - 1 file changed, 1 deletion(-) diff --git a/cybersyn/TODO b/cybersyn/TODO index 8092850..92f97fa 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -2,5 +2,4 @@ close gui when the combinator is destroyed do not play close sound when a different gui is opened support space elevator do hardcore testing -optimizations? models & art From 795e19336164dba20cb760d8a01ab704988b6816 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 4 Nov 2022 18:58:18 -0400 Subject: [PATCH 20/66] fixed registration --- cybersyn/scripts/global.lua | 39 +++++++++++++++++++------------------ cybersyn/scripts/main.lua | 36 +++++++++++++++++++--------------- cybersyn/settings.lua | 2 +- 3 files changed, 41 insertions(+), 36 deletions(-) diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index d6a2ae9..961980c 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -72,22 +72,23 @@ ---@type CybersynModSettings mod_settings = {} ---TODO: guarantee this only inits once -global.total_ticks = 0 -global.tick_state = STATE_INIT -global.tick_data = {} -global.economy = { - all_r_stations = {}, - all_p_stations = {}, - all_names = {}, -} -global.to_comb = {} -global.to_output = {} -global.to_stop = {} -global.stations = {} -global.depots = {} -global.trains = {} -global.trains_available = {} -global.layouts = {} -global.layout_train_count = {} -global.layout_top_id = 1 +function init_global() + global.total_ticks = 0 + global.tick_state = STATE_INIT + global.tick_data = {} + global.economy = { + all_r_stations = {}, + all_p_stations = {}, + all_names = {}, + } + global.to_comb = {} + global.to_output = {} + global.to_stop = {} + global.stations = {} + global.depots = {} + global.trains = {} + global.trains_available = {} + global.layouts = {} + global.layout_train_count = {} + global.layout_top_id = 1 +end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 3d48b97..df6d9c9 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -703,6 +703,20 @@ local function on_paste(event) end end +local function on_settings_changed(event) + mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] + mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] + mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value--[[@as int]] + mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] + if event.setting == "cybersyn-ticks-per-second" then + local nth_tick = math.ceil(60/mod_settings.tps); + flib_event.on_nth_tick(nil) + flib_event.on_nth_tick(nth_tick, function() + tick(global, mod_settings) + end) + end +end + local filter_built = { {filter = "type", type = "train-stop"}, @@ -722,8 +736,7 @@ local filter_broken = { local filter_comb = { {filter = "type", type = "arithmetic-combinator"}, } -local function register_events() - +local function main() mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value--[[@as int]] @@ -753,21 +766,12 @@ local function register_events() flib_event.register(defines.events.on_entity_renamed, on_rename) + flib_event.register(defines.events.on_runtime_mod_setting_changed, on_settings_changed) + register_gui_actions() + + flib_event.on_init(init_global) end -flib_event.on_load(function() - register_events() -end) -flib_event.on_init(function() - --TODO: we are not checking changed cargo capacities - --find_and_add_all_stations(global) - register_events() -end) - -flib_event.on_configuration_changed(function(data) - --TODO: we are not checking changed cargo capacities - --find_and_add_all_stations(global) - register_events() -end) +main() diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index dadd6e2..b31e060 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -33,7 +33,7 @@ data:extend({ order = "ad", setting_type = "runtime-global", default_value = 1, - minimum_value = -1, + minimum_value = -2147483648, maximum_value = 2147483647, }, }) From ccaa7e989e096aab5dc3bb10bb93588289ebeeb5 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 4 Nov 2022 19:04:22 -0400 Subject: [PATCH 21/66] fixed changelog --- cybersyn/changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 156965f..37bbf6a 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -1,5 +1,5 @@ --------------------------------------------------------------------------------------------------- Version: 0.1.0 -Date: 2021-09-28 +Date: 2022-11-04 Features: - - Initial proof-of-concept + - Pre-Alpha release From 2c5bee0c6a9131c3ead4b2d84e66918e5ba40ce9 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sat, 5 Nov 2022 10:31:14 -0400 Subject: [PATCH 22/66] added graphics --- .vscode/launch.json | 2 - cybersyn/TODO | 1 + .../combinator/combinator-displays.png | Bin 0 -> 4690 bytes .../cybernetic-combinator-remnants.png | Bin 0 -> 23141 bytes .../cybernetic-combinator-shadow.png | Bin 0 -> 2845 bytes .../combinator/cybernetic-combinator.png | Bin 0 -> 28224 bytes .../combinator/hr-combinator-displays.png | Bin 0 -> 15385 bytes .../hr-cybernetic-combinator-remnants.png | Bin 0 -> 77621 bytes .../hr-cybernetic-combinator-shadow.png | Bin 0 -> 7923 bytes .../combinator/hr-cybernetic-combinator.png | Bin 0 -> 98625 bytes cybersyn/graphics/icons/combinator.png | Bin 2532 -> 0 bytes .../graphics/icons/cybernetic-combinator.png | Bin 0 -> 14603 bytes cybersyn/graphics/icons/locked-slots.png | Bin 2532 -> 11063 bytes cybersyn/graphics/icons/lost-train.png | Bin 1184 -> 11584 bytes cybersyn/graphics/icons/missing-train.png | Bin 2532 -> 12204 bytes cybersyn/graphics/icons/nonempty-train.png | Bin 2532 -> 10471 bytes cybersyn/graphics/icons/priority.png | Bin 2532 -> 8243 bytes cybersyn/graphics/icons/provide-threshold.png | Bin 2532 -> 16446 bytes cybersyn/graphics/icons/request-threshold.png | Bin 2532 -> 16406 bytes cybersyn/prototypes/entity.lua | 84 +++++++++++++++++- cybersyn/prototypes/item.lua | 3 +- cybersyn/prototypes/signal.lua | 14 +-- dev/locked-slots.xcf | Bin 0 -> 15017 bytes dev/logistic-chest-passive-provider.png | Bin 0 -> 12136 bytes dev/logistic-chest-requester.png | Bin 0 -> 12112 bytes dev/lost-train.xcf | Bin 0 -> 10272 bytes dev/provide-threshold.xcf | Bin 0 -> 48314 bytes dev/signal_X.png | Bin 0 -> 4222 bytes dev/signal_yellow.png | Bin 0 -> 2121 bytes 29 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 cybersyn/graphics/combinator/combinator-displays.png create mode 100644 cybersyn/graphics/combinator/cybernetic-combinator-remnants.png create mode 100644 cybersyn/graphics/combinator/cybernetic-combinator-shadow.png create mode 100644 cybersyn/graphics/combinator/cybernetic-combinator.png create mode 100644 cybersyn/graphics/combinator/hr-combinator-displays.png create mode 100644 cybersyn/graphics/combinator/hr-cybernetic-combinator-remnants.png create mode 100644 cybersyn/graphics/combinator/hr-cybernetic-combinator-shadow.png create mode 100644 cybersyn/graphics/combinator/hr-cybernetic-combinator.png delete mode 100644 cybersyn/graphics/icons/combinator.png create mode 100644 cybersyn/graphics/icons/cybernetic-combinator.png create mode 100644 dev/locked-slots.xcf create mode 100644 dev/logistic-chest-passive-provider.png create mode 100644 dev/logistic-chest-requester.png create mode 100644 dev/lost-train.xcf create mode 100644 dev/provide-threshold.xcf create mode 100644 dev/signal_X.png create mode 100644 dev/signal_yellow.png diff --git a/.vscode/launch.json b/.vscode/launch.json index 7fd5b23..cb7c79b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,6 @@ "flib": true, "cybersyn": true, "creative-mod": true, - "LogisticTrainNetwork": true, }, "disableExtraMods": true }, @@ -42,7 +41,6 @@ "flib": true, "cybersyn": true, "creative-mod": true, - "LogisticTrainNetwork": true, }, "disableExtraMods": true } diff --git a/cybersyn/TODO b/cybersyn/TODO index 92f97fa..cc65589 100644 --- a/cybersyn/TODO +++ b/cybersyn/TODO @@ -3,3 +3,4 @@ do not play close sound when a different gui is opened support space elevator do hardcore testing models & art +move signal subgroup diff --git a/cybersyn/graphics/combinator/combinator-displays.png b/cybersyn/graphics/combinator/combinator-displays.png new file mode 100644 index 0000000000000000000000000000000000000000..ef1db3dfbdcc48ce8c26bdcd6b640cf1aed76d7e GIT binary patch literal 4690 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�$wpQiEeLGG`@Z?*L^t%oBJxl#>hk45Nh< zMm3-kVM(mU=&yJ>$lPcqp<8O9-fGX&{e3WdhhtMbS{EKWm~Mp{)Rqhz)LyE2QE4}s z>|7h;z{b70%z&efcJ|s`<;6-_Og>c&D5PlsP$_n~{0t>mWLXC;0C^aw`LUSglXO-%g^i5hl%Qd@2nF zb#lEfs%M-RsZgMfu2Hp)a=gh@9TH!^YS*2e@)N&5Js3Satjzsx08#b;)m)JbrF{mBfUKeJ*OEEdDLvJjCI8n(=E%KNpmwqhqJI{E$t573`awXSJZ z+Vh5?@h88?z6Eg|Ri)HgvKBo&qpzNK+Db-!h$i36#lq@6p&PL!$QsFC4>jvfV}cX8 z1Ve;~pT~=MD2k5*G4@1^pEwh>_=7L71R0?u9un6eson~3ym@;gBrIf^jauc7!ptpq30B%+eM9z!4~%%~ z)84pX4`eN8eb?V)%4C9q%8WC3P@3!u68_cRCDR=uMd+7V&yFIC#qRddsL5kDw@!V~ zBOKVhf0v6}ntCNKr0+Y3_h)Z<%NWq6u&y=8V^tKcRfDBnB8E4c3@q;w;??70qjyaM zFlY3V(8nr161f!g5R~cQ@ocvD4EMjyn+`}UO-=vqW?Bd$Ay#{PQG~u=3!>NR5JhTmB%uH0$--c|KpYATb4eg-X*eTxS*HoG4G4hld>c3 z%CPP}_S&@LL@hmubS-22@%eiHGp)@oQgGYn{4TQ#`Sy>WcgQ=hhWb@QVqi7mmO{0g z%`or$f4nlvR2ynvg-TAhrykrZA65SK%KLDy9P_VN_713|pyvwko`HMiMkA2e1KcZz z|Mkk^LDUv=V|4%Jm3=Z_{2~<=_8NC~ZWLZxI-0QMkMC8ah~5@y?a<^(N~2TC57?}F z52h_O*;ta6L+J-Zrt{67VkMI(y-8GVz<<~_hI{3<+?zcWs_fu%1JCtP~W;wVFohok;&4F&7HZ`*SVj|M?faSpGCm1HM}RE?8Ju-+it?YUO5l% zuUB58n>Qe0Fx08QtMkCSuZjanySU>11f(frO}T9mTBZ2oB*}1RfB=Wf zw*V$QHy!HYgR_6UvfRI3*(a+1GP88h@DpaSY{8r(eJqiNq7BEQy=OS5?18$NhLlqA zC&h2Wjf6E>T8@saUOwWgQnLh0M8N^0K*n#HXj@OZjz7|qnQ)7Eu!C&Lrn~1^3mzN{ zltjBpQF|uzzMg;Fbx@yT?cylOx{5f12X*$Wyr;DgYc!SpZ8V1Q^_RAOP#NK~wfw+p zxm8G+OUMzAeazA~P33{jGMc*7_0;OzdBk`Y*uLxr0le^JC6G2 z)oh8fJeBZR$37)5FJIcgIdqiCzx+^vm;TiU!mi3N4ox;|+@=C;8#(m)+tPf`pm3pH zQ3ya~&|tD@*~4%sXoRn-)`o*tGJxip~Kg-IQ?AW|=8{IBnM+0{GmaW`F)nE(b}G>EkIVM;BLeh+>7uniADLhY{=h-0A5;Wv*n3 zPyHdk1V$Z?jH5>RrYC>GP;qmehkBb+)WE<*F#%9 z0j{-N@KR)%Tea#F(f>-zJ&wL<2?(@IsMpR>7~;9;X|qHgC5dU1Rb`kht)-BkZX=x{=iMQg?&~ z=a3MC(L=E;yNoS^0Eh<-05q4kr44p zAELfgrdQ~E^}q!8%6+k+uQ}3*V#zLi;9i-Ec77YJMH?qwTfAw`UbG3m{m-JThvCIH X@>&bF2eiX;qX9~9)!x*|T7>@}N~7R2 literal 0 HcmV?d00001 diff --git a/cybersyn/graphics/combinator/cybernetic-combinator-shadow.png b/cybersyn/graphics/combinator/cybernetic-combinator-shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..65190a9f73f09292f51bdcbf76c2996478562263 GIT binary patch literal 2845 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 literal 0 HcmV?d00001 diff --git a/cybersyn/graphics/combinator/cybernetic-combinator.png b/cybersyn/graphics/combinator/cybernetic-combinator.png new file mode 100644 index 0000000000000000000000000000000000000000..d95e3764b84eb16ca3d1980bb76e7bba2fdb0742 GIT binary patch literal 28224 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 R8z2Y{qGJ{%cBd^6B)Xm&puXD$6*XU+_TUXE3 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 Dy0gd| literal 0 HcmV?d00001 diff --git a/cybersyn/graphics/combinator/hr-cybernetic-combinator-remnants.png b/cybersyn/graphics/combinator/hr-cybernetic-combinator-remnants.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7cdee9887a148261afbc7eed5fb632993cfd59 GIT binary patch literal 77621 zcmV(;K-<5GP)|nC5hPQ{NDISd>)>C9daKg z)>BFS>;jKXzvR=K|H}p&r)+j((~!*+aF2g)!?l8zenM( zP678B0NuwsnAzZ;GTdh)rKNlfU}F;i&mT3EG6meH&o*TbS~|4pG6$0VoPftCOJ}ys zP678(%gp%TJU-CI2Uvn_!a3N_ljSn*^^sX`3b;?YZ8I*b5_msfunpU^g5?thxN(`u zkQ4S8ETfE^>ml2*tmCuT<`DoVW&I<_KFIy2fcxk`mp$;0&o=6VZAk{0GW&c4In9vo z47)A^@KeBLe@NM^HU3Rz8SFQ1l#dU(G9sz$@$Vs<0msfeJSQJpKfa?NkDmhW@!9F| z2R$1AsvP_w;P|7axiajrNgy9?n`}DpsSWp+=P0nOeTMsGOW6b#R7RL483Hf!+E|99 zv59u$5Oi|CvE#Dkls#(bWD2+s4n43(J}w)eY@)Oc2S1r4tD}vnUu=S;#%M`K^~htDb&UahJm{=Uu;=I_0-o$|3b;qi z4rDP`COF6KCuMlwSPuc0PLR^9>ll~AaobIq0dkXhh-?w0Ouk!zIIR8wDJn@cvLz$L~EI>1WJV9nAwzD}I0O%ed_|I+zpwo*d;L z?yEm&8k#z}KdgsM9o;_Rx?~%X@UiCT`oq^$E|ru~piiDVJkZMNq) z8NBKcbdqi2Nz9u80502P4^Sz2Y!B$=Up~rN)|I;JzVgR^_J3Z# zd-qO~ZSRi9CO-|*s?sND$!4YT_}2LT#Y_GIJX+z-qUFs|B=`4xKYAMQx!`2M?+3izY3O0yGH#qd*&B90Sc90%4eS$N<~0pUFZLyVH;zvN-pHWK*P|ZiSewk0`a_wm)n@X#uA9R?Gn+QHZ3t}n{==5D zbH>KT>F)+n{Gx7{>hk(YnweXq!+M>1gFeN@619_v2A-$coNLBX)twryNEckW*<%+J4| zY1-{>x7*KPFJ%(@jeyON=fIVo-Z=H8LAU#4;Crmw>k-%sd#bdzvrAE$P>ODnAYicD zy6rf=i_20~g@s_ZP3$}1SVllL7O0-a;Xe8xv7!QH46kiNv&xQH%o&x3K#+w|92`ZlU1(ex+2r09MH<=!>x-V~sR5&+ytF>i`fZ(u=lUX2dW~Jmure1OTxd z2DD%q>N5ISW7EqhashDO1*9eH>Mr>N&Hqq6sN>pY~F;Q@qHqC z2ju2Xsv;T9dQ834#|GTEQH~cH$J#P9r|pIVO;!1mtFymlnTFr$dmU3#i$Roz0NtV= zB|T|r4?R*Tr$Rxo)rsRYLO&;~<>F~8=Y0Lny?f>W-l6~&F_y>j>{`md{!C8)>hopi z#V_P@b3u~sA4YL7K~~j~bD3gYE|-hnc=_eug;Q{e6Ibrty+?h|ql1G33WIC45^TYGkBm{u;zMxoB$Q(nT6`w zm!5g%Uz|C2{%hRrEG0=??)%ZUA0}R$@&xRsaQ-kPB5gg(vGh~W(la?jT?YlG`C{3K z;^-@kk%BdWg$Qg%JdV{fVUVk;%1X%C>6M)I{1cY>SL#9Zqeh(gBP=&+zZnmj4x8}e zD?j|UDE zo!!QBqZ+`20nzf7J`4H8N-g}tl_ySI{?hZmVU%Z!iPR3vLV#LH#GK(M(F3>-`x`cqi(O;i=*%jAZw2}-%sKo z4C8bYJ%N?eIuW~x3*XI5=M5MBY4BLJPp*`XFA>mDD_D6L`3syV6!uq~su#~*x}K*U<4+jHmrw5QxiY05521&G6$Qc)`T&_0g_nGGh zltSEK%<~`!1l#duk{+l$U9dP;dSuYP8nDaN{0rBZRyx$Q6|m9WUa$A>dj088rK0fMauVbF%Hb{*LLb4{Uk81oLg3P#L{bi&+P zo;9!c^UiRYfSV+#Hlk63AZA$9eh?490O6VC`QKVUbAEC2?iSs;b%Xp~FTHxNzTfl0 zeK6d+LM9&sbroGzZ7|#{WQqwd5X8hnNET0Gh0;_}RCU+$ylaf7*YMsUVsh!RJ9v*X z)~Do_%D7lS$>eI0Fon@4eXVlPrxX*q1sD}w3GMG(6_+wGm z)TbOXWmt?TVWt1|XMaJP_qJ%zI;N$(8hc?(W!qc;Ym^j5Y2S}u4-?)2FiyZDbn=R) zWBYuZDv1}wJIzk-eXt*-4WQJOJQ4Y69GZR{rm3oUx0AG|hKaqQE5D<1p6^B>eX&wz z{{RR4{_Zh5N|G5OI*-;OxeSX0R!pOM*2(>f2;%mWgmea?R#+RrY7u9_x0i6P~FG`|G$D+=WBnmTcptbEltpZt2t+Q_~Hq z6>MTbcW`s}=zUmMJ&xnRh<5?h+Q!DIXQ6Dr>xa>=BGS*=J@-`%^djdJLKX>1+QTY_ zjo3!mg~B*FAkGI#O0ASruazYG7c9e@)7euiK;=T-q_skxP8vGD*X`ar2qK>oK?=!z z2sTD!{n||N`BP5rtAjXY*Lwr%bKX8k(j7xrb)5Tu=5q$`xWP|xZZ`$svOk}R-YS1w zQ{^+uaF0CyVAv>#6CMqC2v$1)_~zhYF60cuE!XDk`K4u20l0&$nC{;4+JhkaQ$rOC zD~ixa-ZCy$%cXA}H`@glX(T6Szlcy?1IBq1%$5iHG~hk@De({s5B}t1>3#dw!lpg@dk?Tjdz-&HV^AUzpMgTTq&t;dnkG$s70?f=A=)-;R zpF-^ZAmlA=1rEd6lk2O`lxJr}c@GTnW3bzRyKa9cZ|WyHUi7v_idM~=FAc)z#{OX= zMV^xK#Ud38#f0hl0+`K39Iu}Th~Sm-YNg^l{q*I_nx@Z0VSF9o5#4ce4r7WV>_5cG za`$d;7H<6PH9Bav>G?FFvx~E|QOfHtYtg0GRD*&uXI@-hUk{Ftj+)&&cizgkc2Br* zOt<s!4K|x()^*bkz{0CBrqSj2s3-qHJ{pWG+e-FS^!y^ zFIyX&vvgy5zH{lyvoG2+^X&GmThu1FPVu}NgDtU|SD5h)mBQT{;m1>1)Ig_@x_bI0-w02^R8l4{XyIs;O zoo1gnL)UKHq7_i9B zU?v0rn>&o*$%SRJ+xTRn3~)(6P9OCkw`GG*5CAYI64}9g$=Wb<8*?491SBe*=!fR&{nE20LB6b?dgF$nFwy{g8wN=S+=il@i^&y-huU2tElF zW>j(^yKcWvHo&NWz51?8zM@jD)h0tX$xjlpSq|XqQb*P4)&77Y&|@b$B={19&<$yu zH*qFTcOa!(1CMU>e2R!OU4f&JroN)F9eB>SA+0>cb{coy_=Gm8;4>Ym@fY%_u2Pf9of0-1` z7WqoBq&!0yMKWP-SQgcfnvkE-ByQu)1s@UEs~y85P4YGYL?0*;95~aM_^DFofcFUA0$bS7$4vs zdO4<+-5_pcL<;!CHg5AOAM|vCsnd`NKD1#u5+pT_=F7RIz89rAUDtHGTry0^U@iw% zK?dMz5F!3n2WdF)EaCj69n&w^7sjmr5PwVfRG+HD&2y0kb~A-q9eDj3aU~xKwi4AAjAQLK5OwHqPFI1=HxZ+IQhwk(mAz=1N+;J= zXzy4g&0BV4w)gg^ z)oM@}Kqys>@`WND!@lc;U`wXb#_9^y>qk;4o91*>W zR!8$jWbI*7Coo->9C8{0FPRo#et?`#Dl$Z7Nktf;=$P6Wr;tCra$-&2+uNo_w0j)GgaktFV!-`L0()a-4)tGDRcT_oNic`ZIuGckhS-fTjS(Ljs>{Cr=E3 znVXxVv*#~R1fbiH-Y1{DM5`w@WEDxN;LzpEm+92WQ-qKTm5K#gUtg#7exVS{8&pbgQuwSZFDW5M;0g`+Dy*KH-Yc~m{=CpC@Bvo-t zv(*M8HpsD5vP?tvaqH#{T3MJS6|8wsZ;*cX4z0}B?gJUJ>V*;ZHAy3diurr_y0z_y?}P_-lg@b9k+XfE@E{@NLQ_)FmoH^lecr0 zBJ4KP%mGv-s?{pAd3TrYA@4fBFi%U%%VZiRIgTwoRgr}d1(-j6^;Noi_cm2ZWkNC^ zmAQ3_LSMcH2(b2P4?v5Pm{42^8*r9deV4$d0GLC$oK1?-CM62Ns1j@;$D%2$l5pMx z=uC#&!uA-OWByS77+b~O1#J6c+t5=5$iqGF5f+iw!?``NKV&(006vArftF4mNSINn znz3CDauC7rDcQ>xPA)&4E6pv#3*{y;wy_G(#QhL)x;-yU-bO0RKqg`x<7wt>+pmL> z00ddzwzt1Sp6`=t7&4SnfbA6p0l@_!2P}8v#x=Pe6KQC5b&U?|`&6)PTH82HGXU20 z_pegXaUii5X%73uQ6%j>6|7kXIIq6_hMevVDPEa zK-o4X27~2uS}s6X0p1_Kv3vA7(@e7>46Mkt?=@RFV(C63N<>j6=adKsqu=eZovm&1 zqnN5QGjzDWMLEl)YITNc@Ju_$hjjJb_oNMX=JaV=U0MWSx&$v5=`WiC&!N2oWH2D9 z1w$bN(tHTO*6Veu0aRAbA}Af@#9G1o=)0KHNEH_nrBtcSAeB}k26mRFBj-NW4{nK6 z@wXy?l<*Gz*06(^ZGXM&m?bdvx|b?0l^P_}lKTVQn5T(~ORas>)Z-OYY&`Aj%U})-0 z2RS6AN+muA<9}xd5;{9eE?Dc@oqM#uw@Vm6+E`hXj>HcB7A4=6qC*}SF9a*1f9yjP z;P1w9LstL7E0$pxFq&jtAKOX*R1x`E_|TFmLq(S?D$CS`6QXJaJjBHC^8Q?kK>Up$ zc3%KPQ<+2e?YlZW>zsj#WTd=auPj!-!D9Z~U}w*<)9+#0;;XvKOkB3DU;&HI;4`U% z(VOD%3EpVNFYaUnfIqLfR4@TyfKs&3i{hwgn?2RkHW|^37^t=zY^7Wf357@*&BH_8 zL+s47tz5DJ<_aU93T0#!VDEK1ebOPR23oz@2516WUS5S)dX6@4-XzO1Xk}%M7M50M z^Tzv>hoqiFjLyNDNXT%3s*wzmIjffv1XVtq?My=stNB}e0#W0`u1{7zmoFwOL5RG!L}^T&CdR< zN?ARJ>;73e^1ssT_FhR6N*qf&3BYy6C3N^m+36E7A2X*4VDX+8^$IzCC08g5R`@o; z9%+~ce_&~hk%|1HYq5eKH*s+qC(|@LuDtA{YcnzGSFKq=BU%_NwyQ} z{Kcov(VzV9V;Q4chyy9B2lYb%8k#?~LAGrX=10rGZ>NEfN*GaN{{VsF1=7zNkZS~n zo5vFQq-g+Vl>$FhaV@i!sgb5Y0c8oY!pn=RsZ*`V`%)EpP9?4Yl$0>_ucut4D2eqh zSl4PFlVh3-b2H^{*t+_1f@q}M>pXjXYwJIC``#wxcd6C)Z%s;nW&HHO=M`|-)8BMK z1y~ns>(r$u&;65k-oJSn;Rl|}Id-Gj_&tXA0({%CPZ82nL`)$B z3JI5MP7#kNF>;h!Tp_DiA;a%7jfe#iwnvGY%NL(nnP2$l`|Rl75RY2$U`^b6z&W2j z0rMjOqI4R>#Ai`6=B3&eBK{|cb1|0=Z_!hn(@S6q#<5WpXNp=)0JQ3d0gIxL5)5V^ z;f^=od7t`y7ffZ#_}f9d2bKyUsmpW{f!w|yl4Tf@{hqvZiBwg1pCys{lC1U@|M&a! z_FHce`h?e7q!PSb2t8QLr|$6q36FUHxG6ke>3Gc4YBKDiY9>s&^o`o={9$!FQ5?45D()O6qf!P zSbq=fo_xr9I5w5k>_tc(1AuhQuv$SKTW5Q(emhF}WlU7&khN_9v2SQfPJ|wWO#^T) zM{(*x3Kbg$y#sbiz)Gud5|nyFe(np9L5m_$ssyGArI)!lTcHDZrAL?%UvP5dI2P?d zGT(gTbvm`SB!P8rdxtvR9-TR{OjxAE-ne-SNqtK=5t31ZIF>GB7)nP%ux6WSdR#ei zrlbO31l9wui5rt)7)3MgPwS65lvt2`rbt#bLtnC=8e<)~q^1c)jLZIE<3-K>pvjoH zbR3Wj^QNLrADcgUVHvMk3?$(%C5)4 zSQLzy1N%kP?e%~aU8cT5E=rkPA*o3CpQJwCtCj&U2!9!1-PJVmz>4`|0VbK&e&VSs zPv~~uN&!F)lt9bDlnKQElmX!7gC6zW0i`&ORD%rFA`{@!laP2E5UWyReB7gf z=K~-y;i3R8m1>JC%NBBHnw}#lKc&7$Ea=Oiv1US6A+1FUsRN=i&=g9+Xah3hfS8;{ z%Sq<6@LFdnWV<2mcMo3-hPzWC*Td|>BZBTz%y5rCLu-8d5NrZqOwsgOxv->ByrlAI z=Ah-hj?cKC)3rQfB(elLCdquK(eB)9b-FuXyCXx_=j(?JwzGEtuX8}_i;&Yarb}l| z(bD<`2-1|XvKdIKe7=V$veY>p939F0tcrnE7|B%E&t7{?v9>mG98$=xoX`aQo*Fc79raXS?eb#0olCz)7L170DG-K$x*!?@iejQ69(lfW#XOzCi93>biarbea*n>XyEvJdv%Vm zi}b9yI3Z&_;(UAH2gd+fs42`*m~!9+$@h?z7#w!}EdyZVP+du7qo3vD|I9i6lmRaL zqQ_Zn{JFitPC+TS-XC}}ml-Dsi!jr@m^02pTMjiyCxAE`hCannEWADj>6-=kezDy} z{K~Xs=Rf|*Pw43@U!c<$o}x`W5<(9mS!N!1o&XSozDv2oBlH&@qfa-CM2BfCV%=$)in65MgM4WM@B1d~$41wSx?wB}PW=dk z4{SaxUHaTI9MAeq(~KvOi)e^?+!^dK{pjBtrt#Z|pDDdg_s6i^V5cQVhN>>Ydn<$7 z4x24+9pMMY<%AoV*{m zk-=url$Jv|2^iNuIBwoqsFcqlA8fmUzga~k!0L(hWl$|^x7tCPChCKNWS#nQ$D8axi z8>1i!0gxs^$?Ul}4bwD`p$qRjfuGo7nfIOFD(AEgsWdnbj8Ru z;I*Az+-$wJ`Ct8>r?{TKgWcv}Pjw)P9&D|Krm2ZxnAN~{FAn;BSsJb~PC$YT>c+8< zUUvTi1V9olpCnEk+vIjGFAVY~SV{tD7+MvyB4t(dAty>B!$FXEQqknYafBYiRnA#P zT+Tbn;c=r2AgYDDqebnc2M}?~&YN5STTI3GykjAgkk55gs8o=(;Mji)U#2$zwpSqe zhR#%Ajqi8-XqN-Dxa9v2Y@c@8R^|5z!O*3BcvDoT-f z1x9xmoZERi_xG;8^4cG4-oE=i4E%fonSrv_IFSp6DC$`9&J4#mG0*h``AHyg#?a(M z3DO{cV$i`JqzR`G`v7tHPm$6|g0UDt2ZTYkrDqEUl1zw~a+VgyKTBs4l%}c2X@aie zIEd3=t6C{90u`F;dtn87XreRZUk4V9GTU)-!n?(_ad@_UBCar5Gsl@|$dKeh-&;bz z2}}zO<>v2z={fEZo-7|Nvg9At&yas6>*2=OZG85TrdhgCd~)R<6$;h$wSJimygs>M zmvoU3PZ%>jb-hxlU8#HfZ^C9fB2I=VzyuNu=qi*i)GnMq8ywV+EfF|{H!E|hsQ{Kd zfs{U;CMhr^g03MziKKhABukG5kU9x0B6&<4C-Qhsgmoe%SOLo|p?)sKwY3mn28M%b z-ck3PZWo!1*ka5|;sEeS*>ZKJFyokf)8B1WXKMeCy)XHZBfHc4y?C+bzSdr3lWZ=% zQR+o9BtT#HjQ5;)$xf8Rt!t&sy?b}*j*{iKL z8iXHn7zI1C-2nh%yEX$3@j4E(Y~OjGt4o$v$|P}J6F*<_N``|A&*80RNg{oG$x6FH z71z^kh${~Au{+~A&pL^MXr%RC5xr#yx{Xv@pNJ+(Pcbiw80F**$mty%;|{Mgnx56u z)@r!uynk|_ia4trdiHR1Qod#Xpm5c{3&KFaljEYt782pvk%48!$Lv>~on>5X|#CckY}(T3u`&LMLEE z*Fac9jK@L)hV!N*hy%rtZ7yM#y1{y(tD9;#{&w8!Gtlgkq{Ws;C?uc{J~+`{27`Ax zPs#QG!fs8uR*}JP=nFygXr-GJPo@CdLj@25h~Z=G|C_Bi3v3tlaA$i$47#WN<|;fL z9q;{Oe&zBp!UW6BW{o?nj{5@&;!yVj7669vmD{hh5v=9-8Q6kUn&YC1?XL)|#KBoZ z=+$3%Qs(~Ai9&Nq0VhI9QfArKAga-2B;OAX_LFztefI!Nni+=FSp%!x?s$+;c)jKl zV$23w1Q|^DCE)52w^WAhS0bFxrAm!jg-qmRA8hZ^7{F>)3xG_LAqA~;tJELP==xfh z4o@?k{g92aeL0G=T{u2h^ISQAoK1orXt0q|iTZ8-sd@_V`1M5`?z3$;^#sVCocO~L z-0}GI@Z|Vp1FEw$p3a1>Ka-t;nro6Khp9*oq8!O~3E(kn3H*m_4+midamOM-NhAjM-LKDm33=gvW@nMonz$ZF)|;C)hP6CkC_t1T+K z783P+y8c{?T&GrP)@R>*|K7=4_}(+2xM6xvf!WS*1fCKSIUjhxKaJ4mPu~_#`3Eed zAw+L@+B@9e`!CIQi#8u@ehW}CYHMJTOFp?ZQw)mOK!RKcb6LtBZa>sj99oXy@f8|i z6ChU`+i?!a;b8P=R)<$9m2KS?*sM6zMNuZ4TJ_@}|IIcLWv~m&9whbpwQHRhUwB~! zPKk`dP=t@huOiIC04K#VOIZ}LWMfnK#hA71Rv8WjN0#SNL5|6j(P8J!QCnA~Pw)5W z>o2d61Q6f-XjfRaS$^gDh37xmINAlvO~H0s^jl`*{c)f@cI?dk!fALst@Y%HDSp&x z8%Rz3@WH*ihARF$j=hoPsR>Wh(HQ{8gA^`i;-~Mtsp5G8b1j={n_il-fc+K~iUX*) zPBDHo$T=pqqPU?Ta{Y?mtozhyS25h?8sWN%1my_7Oh5eSqX7V#xUSPjNW^^Y)z`j> zq*N0iA{^H>YHYumjsv;0DZ)p`kZ(^X_odX;8zt1u30>Nr|tnXOxSs>^YeQAFGtK-5HTb&}o%xNO|4u`F^s*Jxw#a@k2)DbiIKKG>rt zrD2sl$FN0VDZ`|=u;HOD@wCf7{PsV(q?xMU>*tf%1POv@b~+x%h^FZLCQn7usWK@g zd$ZMggCyRk85H{zp_rzg4T5t82$deKHZw#GUyx=hk-jv8B)z0anLm4Hi^29BWcBTL zw<$W9Bt@Ff4V_St;w7-$&mRZfIjlyHOYHCa!XV=D*XR6906=&mjEXH5b21(RN3X%o zI`q3{HJN3mAWl|U@jO2kDVfqRG5{~ZO$!lT3lz~d0JpD5?Ay}Fgb)*e*lU!^ZP;Sn z#dclj@JK7SUw*lB^Tu@t*XFp}0X$^bh0Jix zc9-XsoQ{U5ZlByO`6aucqM|)y$Zn4C%Q2*V2(ZoY-7{ngMkUuCC26)$m!@UnwN`j~+bO8uohq=-}XRjH;3oOuyWQeR%Z7>#waKFf4;0!bHq_ zx}TN9DUJ35QI@FF(dZB!>qr`63jt|~(8mOzI)P^k@77)tf+S>iAV^Q(%Kwf?(E3uf zbJ!bOz0E$|ax4n+oQHcIWyK*7vuB%f502CHOrV`DvBlHEBo_?#8Rg}V)y3(#xY~)3 zQIb8d3^AdZQq}A3&2(Or+`3KD7hW_>1H6v1LTv(632s*q z^eX7k;h?VT)6v0UzPY`rmc~Wm-d>PDAQ{q*T8U;%6{abELHfkDP~`_p{lJA3u-OUNq614^W|5Db0$+vrPhP zEu0?3AJk6Wx5_K_cWR5M1Rh0Zbeb+6kAjiPi-m&dU4V2BdaRjJbO5$M-xSEHc0X&5 z$~=)3RBP914K04Q=O%moehkBFLfRhV^J$l`;kos}hacJ>-MLdjFt`mn?P|&MuOjVL zvQ02kS|mXjr$Vs&<(cndZH!w{J3A^?fRek4XjBxG7J@9LsE&(xnk_1RxvEUZEOeiO z>Cp1>5)Ajt&hs~KzBnFF9`^dZ?Y+Id6jJJgt+Ll&d+mj##RZp0#9I#^rVqBZMy5ey zv;&q*L4>P>T=YZ&b{gqQ&`h%_g4LEZ&0(orZXzxxCa0&yd_FY{7yyvm5;7yg1Ypfp zlX`fx4i1Tu40I@W(n9H2-IXxO#$fgEb1)b`|AKaz`WP^YYJzw+`m$8~Qa#KP_INt{NWUurFM+d}t5y!G}ws#Gqe3hHel zjg`fLLMi(cw$_-4PO%3h_%}pmX)+6g;4hY!SH6q3tR(b3@%R|-#OQ-NX@B8$m&tyFldIaT@xB&|Y zZe^?8>T0Fps{SY_O{bxE{CBgR)-~_QtJur6j!UCOr{T7uPSne;sQx8I>XG~8S%SIG^6x@inR2*KVY+lqxsCBCzc?Y;i`8($+; z7`k#~Hkju-&>OutJuJD_M2LLb;JyjUpT=-! zd8QXO+_STt68s>f=g9gWd}aNQPI}Y-IG)D{-m*@L;c55}%5~`uKMLP0JvZ$w-Ka0r zIvx!d=MAS3HS(UiLW;Jf6jkI``$S|e`Ti~6lJ;B`1S^VArbZ}T(%tmdVyA7jTdi_V zMHjPv)$aa&c5>3Qj!sUr>|%85J~z-rkk*2fMi)(^G)&V1`b=}Yo@h@Rj}Z*c@H>C} z$N!{^i|c+0_t8WoMS>S8!F&>CxBdM+;?_miq1k9iO(oB^mz_mJimnj!9PB^6uDlBtDm>R;$>Dv(MNZPTxh!dPI-*T5%|{=9LMMYhEq1!% z`RA`1-B!y+09Tljn{1G$-R62XI1*eLlH=F_-PYQpEdVw{@Sx~KgAUUIRHxm%3dR(= zeh#@p;-Xl4!(dOnR1z|)!z6Tr%70+f0bWXl7T1!&eWzTw5kdRU4myxn;6>w zcG7HwTDA{$BMs+EL9h%wO5+0Z8j_$95jS^5grhkC*y(mWWFYE1T3;1hY^9xsa2{}1 z|F3`jEyZ@@k4_#op*?Ks#$$ zq>BldPuXl!O3#uN6MzFDH9&Tr0DWbdB1Mvl0@8%n8br9$fsvCR?x!D5PGVj&t+D2t z{;Jg$hG;|LYz2(!TxpS{&G9@Wx8CHEa%m_oVcOk0BxI*ZSDxIxzk%1gTx(W8m!10Qy_i%; z%eZmxbsLozgd!(RQ?40tz%C$3Ud#g(#N3$$T7q=$vSdmrxv+9?cbCCo;(R!ye4dm7 zUh8a*Tq)R7go=pcD4O94RYVory!`P;9~m3>w=oTU1MiO*r~&M>N~6sy-(d2&p1`TF zh}>OE6>$eZTX7u)unE@?hLz`wh}XS{g#IBwXU1t3o@2Y6;iaB0r@up~t!Gbhfz@~j zVL?G~wfM4`Ak6Fa03CVLR3C#1+#nVOh2LzyM9RB5@IzvZll z!lhA8xaMA`(Yn3V@Shi^CCGMpRKqSDfj58g7WE;ej^pr1eE~48t*=w3-6D9b^y%HZ zx~!es3`hDr%hr7fsz2zTAeFF1M8^Wy*G>bT&w-VEwdVd&Fc>c_k4M6X3`eHL0}26J zo{~O6^55jXShHA7;_M~MxYjkzRb=^%i4I6BQm2I)1O{z8mKh>zvJFLz&#>M2@x0K; zMOfqvFxsXB;LNs>7qxkuO?+e_2!^oU3-c&5C58k3+rB@0RWNO{Dp=D1w7i{}h1Lr4O6ID9bzBc%yX zNqR)Til;b*;att;vmp*0LQA!M6+ur;0?dJx;&?du**|lwtMx3YR}Fk;AgB+!Q9CQC zim3wTL~klNa9Q0sevu{-WHhBrd%x_KilCPl`$1N`59@6MwygYIxzcCf)bG6w2X?ax zr5qpZrW6?!*Lq=Tq2qn+)vux_bb-%l)f+JEeaa0-VoQlDO4K|UW}DFq-(2vPbpeUKEi9Z&<)%jb|ZwzV5B{ zc=lXh5w9LS$#aSn07q($d8!|zhL~18tD?PYq`0EwbOq-MJ~R&?8(xK z)s8leBvFMMB`LT4832GyL=gZqfVWCnPK{tju9U5qcMU!Oka{)TNLi^v1iP4NDRq^ zoM9C48WIIIQ_vjOJm}%NfueyW#0PMQwgBopeR`p)I!8*?#S-M7W4$e1`(H0P72L@6 zwN~pIjO_17Q`&F;`aQb0`H*d_qmv#fp7*E>*Ku#40%Say2Ya|-Wz@y(l-Kil9HeFG z802-WwmfnUUjN$bD!|PSA&*i!9RNdRWr==t*yp9lQ`l9D%ZsGalsmkL7i@#`G34$n zuI#PV>-sQ3=F3+umDiFa_E2sP#Z6pf55@?g6yN&hH~zHQs5bADC62(R^Hr03To;-h zp1`!WswmA8iggoo7^>WHcBfJJV4h|Zuw@8>v<1gpZ?tn*iE!$f{0T;fsZO^I6AQ*rd z$1)XmIw=mKNq${gIXNZid!1^_v28gE#-yS-xvqovd9>;~GzExr*QR0bgm%Ft*DkNK zo9rTPR+tW@rcE*m+0ILmPdz?56j*q7$#?zlATv?qS+0viL!_@x%O!90ga7@*YNq>G z=K&+YaXt2W(Y3>lN6v$ABxf&7+ZO28?xKKGjwfSe&qlkLX)?Bin%Pp!Gc*A~&tn~K zDF}3|lu%D)v9m`yTuACU<5va#)O@=drfP~r<%>}m8_Hmo;1PDF71wUi zLpTYoOi|0WX%2AN)h4Z9x zaslWhjCk5+1nI2T8@w`&U-kh&gBjX1P3#t|25-f*rQe!r&_rRu<&%FJzmGxI+q*m3QQ#@)X;E+(#=|b9WZ=Qe7M@GCXWK1+u!Si;?fm;U-+TW@hL8nv$-3t5+$xZZ@}$qP4wS+MNgdS7?ulRWv*|2A1N0D{ z<33VWlImAn8t+Fmf0*%{4>EX8n@gmeN9EWeoMcuWt3Z*?n`;nQI`_nKc{CVPhybn! zwy6Si>uF9U$R&9tQg9rKU`oAtz?0A85P-tD1sQbj!*@w2MVGKHmk-dmo`<8{Rj(1x z_Yg5Q9w#A{;4~Jp4b<&)dG#9rk!#^B4R+X!kWt-AhIzmz_-LP3l?IS`RIJ95Fr>hv zaJ`C%L{wHv{fQyXcXE{p0Y^eAj{QBQVdiX5U95Tk^o_@+rnx!*bL|EQxE&oG+4Xis z*e{z@?${KZrew&1=X*d;mF*fcgM^=xN(JzRV+BNKY}!Us=zyzqAj8D@*KWH>+uKvx z-G0cefnBhXPbuZ|0i7K8Xm5xPs?}8<#cC+x?8E7x&!fVieN?U2;l+-45;=0A0F}en z)#c_Dq|7Y0Ld z%@&1sykpz8t&VMXY`bF{9ox3;!R*f&AY1k%o^hzJfoo{_$`$P z27gUG`3^^q46^oH_f-ouo9iH&jq-kkbrw}6R4i?odg%>*peAQF%0j_H!yt1FbHZ6S zEJ4_9j2^%xZp*~iYtzK_+!!imB^mx~`>lmED+~*fhX56RkGuA)EVI9cg$=uwHi3(x zIypw|>YSNGQk6*Zia+~cLbB&|=jFbN1)=C+otczA(X12~&yDxE;hHwvyXC2!72a!D zStK8E4R2Z;3C}dJf(a_YfhpW+NgAows$aiRR@%;2)90`@KSV=h(Pf%c{gXc^R$@~N z$9G@?X4Bf;1)dTA%))1a7p`uoXG}{ZhL%3y&D`gkEhPwP)IpP>h9}XZM=7A>e?WUC z5XyJZ+G7NFqt^{Mj@vapt_g&W_cE_s%|r~bJ#f`#JGSvMgN%=$+=s*T^Yfe0P6(Ch zDJ6uKP}hobecy|T9DUeS&<9*q3iAuiMn!22CB4Pd4At?r=^f9Vm~q!OY5(#?_piiI z6l?i_YTjFBTs-}WoK@}6`tGgmwnNjWdD;d4Uo32{!|+lq)8gF*-m%iP|F84}Y7%9d zO|2Qp*z^Q(V6ZkUM1j|(diP4v$|N44h&QJvme$Hdy*e)gdaP7QzbvnrvX!K%vQsRf z$h0<>+PL2^=-HBQd$cXroL^`4)@#}T?{Ch34S_jq2kE&HH2c@kzH-;NPVLR@QmZpU)~g?%V}t;Mne@i&)(VjOn45cW;O{;=Ga; zr*ZYHfxMYF-UywA|1Hb4G<1oo(&9bF8!u_ma1Zz<<7SGo6zmHeG3Oi%SHomYl4qgw z8qtNi{Cq>4GTCHWMvIcFLrKN@5|$mX!LsTcq~G(@6Z{pJk_CW(XPgxm(8`cX1iR@y z=KDnk^;2n|T$OB_el5M>pE$EET7Jk9aDD5xiE7SV)vNX}T4~0j&Bpz>IjQqBcf!4*{ zk)db4;1i16Pb3X()A~kZ?er1H(4UU*ktfS9{F689m_kUJC`D*%x`W}##1L4@LVlXp zOZd(0A|h|5@Bv~_BP@lZ4K)hUmo5^}po8yc#-;Nm^VS-EK*xCwAR~NM#S`8D5Ey54 z3}C_u>pp(eypH$X+iiYuQtju<1*KpN_}ViA+MY? zIg2Tdm?sKZ1H3PGZ9ASZT7?Kit8l+9-dV$b z41p|^(`^Bz*J7%a{h44dn0sa5dj2NIvZs~cw4eW7lC?_oR9dEL25IG1*&-ePp3^v} zZ7N0tsk_LeRW}@LM9=DX-qruu@_vam!ufm82nwK(ao%x0=o%#CXeB5g@lQMynPAl- z5khmH`|CltT%#EVhLQ`7st}J{(buQceEO3gox#f)&jlqt)-6!<<;F}`fej3>`pA=_ zvfCqCe{hlhS`80StRP_cS;lqIfEg1vr|Cyb!Yd)~$j!gc`O|)Le%0vaXe0M%c7hfn zQmE&?(VYS3pfyQ~Mshc)qKaI`(PgYyJFR%4oSGO>1EZN`?=%Bjj@WOYR^ROqBl4EP zT9h8XVWZ?C3rW7ql#rH|TSuAd9r?bcA#vA>lY6Vtv^0P{vs@^Nglv{8-A`_XlzS2K z+WCfn1~(V!osJm6+R;+(m-b;EczTPF%z6s?OCDiU(GzUp{ zV^pPjJ!;k`?Tm{BwZX?PXjBXN;zqg)ftm$c{Z&@Fzmmh1W_t{qmw0yh1VMF~M?HvBV> z9YB&}9Rbfj(#`|Czy*ePam+L9NRV0S*1xWx9kj!-TmH1tY@C)$ zI3gcT>Tcuc4Pwj3!?FGiDZ{cm&L*xEp{-Y*LR#zQh6kw+*sw~~3r2?!-fb*7TrL+C z<@;nSFAxPFHO=CN0&!@hPwXJds{o!ezX;G88TCgEz!tr9f-`Z8jq{Hixf*3%P52@Q z>=z*ooRdl`Z8v=uNj?C9-CFLK0hN&eJzd6+f1BsWAmP9W&Ltp5unX}@|4v`#nE}JJe6WI#+SI7*kD(~-~({=>7vXI zusKTQ0@`0K@UpZ$g5Lobx$E&1TAC5Fo!l#b>6)A9Z0qZ`H@6((1)n@|1T3)Tq(cC( zu;CgcxBci=6vO<|0;IR#0RU!cfqO`B$Vg*C7SFD+yr=1o&HvQh7_~d4%r2gPku3>Q z>hoe~n0a*CjahHST}mFzL@@_g{vMBcTFn2aayX0JB{9{f-Pbk7_}~OvrdH(k8A`;I zVr63*cnrSKeI}t;0um(pvGPHh5b?0h@yrT~4io>EU1MOW1DPYDMwmc)$+Y0tyk;ld zPlP0S<7p|?fgV8<+ZHD6koYct{qot}W$)Wok@-oG6CJUQZS8VP`KUH`&Ag@~pO#?zLAvKvvWZ`In9Drz) zOhH~}$q^GkL2H)Yv{GiCd7e_>>*d$$4SL_)avO`LBa7=yO=B(sK5#^HA~tXk670-6 zph$;KPm)rBVWUPCS0YC;6t23VItktUmzPjPU-Dr&x&}jVQ&`r_YQBaeH&>cKWM4hM zHh4AMhmD%(_1vA>FPLx0E7gjegi0d4U%pPoUHr48l`lcnMCax(=9UO`t}1J$5T9=; zqSK!sn{Tn`r^T^+xuP-Ic*!mTB6!g#ih}qYt;%|VK@9m)MSMC1PpjQ$nZw#~$*@+U zvFVw24!dyjs4yJ@Dd9FX562b@VqBArR>Eml01FQE*OJd{Is8q`o_;LovgJXl>e4!< zmPh0x@@=f8ZWfp66fdi!MKGnQo!?22gg%HLYB#D^O3sK?l`1r*uf{!UrX^+&MGc?^ zg#n%b-mbhLIjQ3&zLXG?!fFBpnmJR+1iJDlUdb147_wspmc)p_kv*~lub@O={<%`E z6YR)?jUuggqE#(QIiuek;4&|tx2h%#pUIr3xI7~QzuW#4;!_%NRKH;bJ$|jhi>ZOc zyiaY0R*rlWUkV&-ew|1pF`$S-t7Mrcr1s~g=`zOME9)!9vgyPog;3PS5nlvV? zH;pD7sfGR<+%GAWJfP8dN0AcR#F=zNx-TolE?Z+RG!p*$4_br}JW{Nb9eMY~q~%w+9N1OjY;lO@>4y4y-_uu>?e|YB4JvHq=NA zNBWCcMEeKon->-G$fw2$#Xy2VqcS_+DO>-p*7DcikUpkZ3T&m^m=0&^wR;f{vW4!qbus)JVpq}sKzBOaXF=*4;oorR3cf@dkjnR z*>6;kAkRjN;nT*hVrzzpf_~XBpC6!sV4u^BDks?;UzYeRZ-T;}qxhwGz`knPl(^3C$(ssZwv@B)4j zgb4PJ;U0m~=Nj;MCsBH0ggfWPk9^Kl;yoDYG$D(7R^={qUoRX>!PBZ}rXT*!)}=9f zKL{Vi1;A+Tcw-2sN(w`-*N~Oy*;m-YvqvaWH0l0IRRHf64VDDsj9>HMN%%}xIj2hY zl1)dbn|X$YRVgNMu3F^N8ckvU-g9Y()0WEl<%LrkQ98{|Hpn;0rt9dy>RCq_SNa|j z_8nqIEXY#Q*Sc1grfOE{S^ryg{@>+LNl4If=a#KQKN_etI(iPlwdhk>{~+kY{zWLj ztiTlg;lyx!&}8|MXO0?q<~zXUnHKeI4TEH<&Y_MWayixHCZ#s55^v`-@OuDvt3m+D zNCNIfCQOmPy5X=>0kef5Of6OM09?y%go%}yLw&5^A{_>Z<%?scjg^VomLt@*|HEaK z6{@4f`SArRA%{XPb<8!BF7%GV=I2RpG+63kvQ?BRXXuCdlkv@A|EPK^J}ds?LY)xX2t zB~7{AxYwpfxG!O1 zx71d^>A;(!EfBPZ!-zT*9&I*v`?89H)2nzeR%)leZ{o4$?ev*BjrQ6VL=Ib0q_?1- zmW*yzQch){7Sq_WfzCynEls28uVg}vS8LG#n8Hc#i|Nr+-BdDa>Bqc%Hh~L_%=klI4wXWpA`6fhz}UrDtpbz}E6AX_p7m^~Rf+_k+hts(j zp|T5W0_PcQJkv0dqHXLDSRtgPX|40Db8vq{4$b|HWw(G)xqyd-4UVadM|m&=bJA1k zDL;23Q3L$OJYk!fm7KT}QCqEKLcuIGJ=>9a6+WOu`k9bRN5>)d%bCigBROC{qbrxy zOd4I7Lp~N)iLZ>|6wS%sywQ*L!H*oFm<4Nh9q-TlC`n|J$k5T#aD1CJZ;~0Dt2HPJ zt!y-(>#*c(smRqprM76WZan*lLwR2iN)+ngqZ;?8GS8nh_oiDSCQGb$b{32YoKY zqPog^4KYhC1O?p%oUBOUs`OlnIu36v)fy$D2D%=o#^S1qg#4EZ(BzTE2^wZ^ zj@hY|UnBkquKJ}Ncsfi$U8DRwN~T0;Mh?4}>>N5RYwjPdnW?PNI-49u!w%0qxi<5F zD<6;TMW6o+RvdOb%l zB0IN(q<|HmRkHBY?oi>P`gk6Pw|0)$a9N%8v6WXG`-&NYecf%S`cs)PPR?I%z3SyN zV;ri}?gY^*vwttEi7k{oo=c#Fjsq5QO89t zwN}5qh6KyI+_+ZZgU3oU#$3&E*&5{?TsT_hh-XYv*mc=9HM+~;BS#vIAeI_Mic}GL=6P%J52uz4<9@eE*qSmADq$F8Oe&yNSl7rVmZ{(eeI9`LSXo4qXFyq z`K@MiFau&6pWA&FWCK_~8wXWa@@`%Nnv8T>tylBb-wMA{&30{;*uD8brozIJ`zXK` z>U`}`_$jwY>#e8lZP9L(?@Prgi#^fv5Lpf6($M$#r||EN&IRv1A&0H^>^)CDLDfh* zqa2sEz(`wBY57pI=;&Phh>&e_h1I>Y)N1_Y(Z9>#<}Jr(h%462ExnKR-z!lP;I`>p z{;S2~&R<6>j&^nbfnfsNzaYT*pI1R9+~`=P9y!QXS&GFxS{a{Ga= zE3p8FvPL<3I~o98<^z4-n|+q1yW_XUb1+sL;k;-GiGs#(;VD*{lj_VbVe(<}W*{7T|&UzF9KEKtq38R1S1{*f2fh}Rz@ z{y$G?=Zz|2SS?V^LKU7NimEp{bC=dmmG^l)_cz(mfI@K?!@n8nHw)mnA1cwT3R=$; zB}~PAK?H6NHa8Ij5d=YHPwpfv1^rkRZn_%L58n!ZoPKjv9MtQ=@>dEFYtUY1ebFPX zxm>Mde){&&>f$)v$Lpcs!+JzY{Nt{0V3UQ=hb5CoxDmgmrmplCu-$P7dd#^%B|oGa z2DzZI7kvIdky3rfT%ikhK>*LL^{!U!$JwwmV}xjsO>30`ffSX@9Sc+p>De&L?}|h! z`K%PSG`0{3rdnqE9#3abJCaoLrb>?V_YeTu3*T(`=RDhqr(Xvoo_ zJbuA;Wm|~AzLh3s7uAl^($as&Nxcc$7mJhRw>>_AmIKEZ$=*QNS1)}0wM2qEC4)G^ zhqX8ymx4?XOW7ltqBg{@IU_t^rZ`3A-R<<24uy};7AnqCfX8ky=oPY5OJsNz$3%a% zQ|ns&=eh4=)pjtV5U};?3E;~eUO;c0r`da#T7Bc>gYDpV{Vp6B4C;FDhJFZ#C>RIm zdB7E2hX}cn-1*IATC@9+248uv+KhSebL3S>g1S zppx|Y+!NZ`xx7w#i%=2)AvBtt@lhJEl8tLFANbK}!tu>b)G-_wzViI*`#Nx~Qz;_Y zlw=y*fL(%+a~{y!ThYg}`T~8OPh*Z-y+er;<8`YJ%jrfH?R)1TxhOr(kXHtf7A6s< z4+yJ|B(d2f34u8fWFhJ(bX39Sr$*>Nk&|RJ)?feqm|jL$`;pPFw-q{+hB%G6*R5S4IQ|fz@9FeiXNj{J(Dq-zjb+JOVS@IkNl9s0t>PuLv@{B9UNz2t5fN8D zqHt(Bi3bC2cu`~d=uRaL;A(u5i{nZI7BS}LA?`k3&nSCuSN_;NW|O!#?)Hm6IgfA6 z9nnHuP9P*V)D_ps^%pltM{VC(}vX2IG*;NDCSzj6vUyc)hZ23Tia%Lf0s|&f1lwBj(_UtbEjmn z3N`QzV<;Af?AK_A!^yn}MnI+>Q&u=wOk*HMi&pD{|6-uwMXbKPwI7658h<69NxM&^ zE;ci0`JCJ7pd%q1F2}ZE@lp(+M~$*maeHtsgF%SLw@Hy<*!$}-EMJ@7W6w<}@*1rm zR=lY~W4R8j-+zX!AF+ra-Dl~~M=7IQLkh_W*2MRpKnhthD$(6A#ZSxJc_VSKKC2~L zhRB$V{TzsD120Y_G$SSEd6)+L2#g@-FC|FP8IEa-9C04Ea((d%bc%^9V;18WLKId( zaRLo{QzAgk@AMpQ9jp_onk6mH2^)f9mK9h=U@}S)PdG&)KpcD(_e37q#PEtYV*4q` z*$PB|!Hxsp>+BEl!{rkWQ1EwsC*9-f?`yAq&fy2rK;^43I&KI=o9 zdG>ll(Y&3gr88+r=I9rlc?~mIQ_wXG2CjUBHNFznJIvM)68KA*z28n#&9)ias0vMA zY0yg+s=^-3&VNwqc;MOlBR5Vk+oxA%aGS&kBtT#33}`)eFhmIcbL{$^p4>Zh7i#Bpgi4!PqM z_Z?^cSlMF!+u-Qjx9sO9>HrCeZAVt-4=~1hgYS z(C!zOXlW1wmtoHee#FR8vZZAxQoa+{# zM<#_<8;ycPVz&TRzT^A~y8CN*9rHH=7+`E~7&k8Wzgy1+=muCVX2*edWRHUPKLgz* z91({8_tDtnBSnR*Yui-7-uphtTBLA$2&vFcP4ut#6?+yAL}}+h#UFDou2U?02-@YU z)#k^X8f`As^ujh;q?q5oYg7MVBhExp4pHR9G@%sf*vc+>3O`Psg06=Avpik9Vf;}- zkJ*Iujei&-n=vRDg;Q4`ok2=TP&ep48jF23u=Ec=9=r@}|X@ocPRHrY6KF zYXOVg82Xo`X1P`4d?cHB2AL|Z=l(zbozL0tx2OC++HXo%VPCq@fsQ~+Z~xLd(9Nxw zr;{aQ?h3)n7e=rMHXTFYrApK1w&L}5FwH9rosw4^aV&4qul(&(KNmwEXmb=yaf%8G zGF6$9wm}>w<{9pCX8)(woydJFLP*lk8tibeQRDHV{_hwBa)%7m3=PDydrYIcZvWjT z2@DfTxFK4x8Zc8-_M3jYjM|MXui7>Xz2$5kL;WSM6Un*mYt@-QFaa1rC~WwMWM#LC zWCS^HLnkc1${olKLNgQ~Ui|m$2si zx(Y2axvLpLfK$L!Du)CZOWSPJo9F_zU z;r73|KW%hXt2q<*<+p=8>N?M+Ew6noL%<-5V2jWt_DG{RbB(cQuYJ#TZR_dqR3_1y z7cr1`@J;V%L{M#i{=py#<7je9@>NADvgTrJYLc>23vN@u@gNcl&tK$Zz-fM%{qg?c z-j3Vb+jeAPYYTDh=enh>_wz!%Axu_O7jt!4JM0&sI{bgQcNA@I-Q>Y-Jv}k0BXx|G z(zNzznZxY5{XhO;IR&czn%SMZ8oArTZ`;*2@ z$>NW>6fX_5-Mt5WQzvD-YP(nVji?I{ve4H9$g$V!5|1?DKGe8zbM$ERttw$5rDQ`R zPD6}o5~PLM(YaGI-aqXI^2DHq7Hx3)N=hMH+<16-RULH4 zDSb88vGO)To2D$q054T!Bej!6lCJ((aK}+Up7;rUd%^X5EV}1Ul+cs4WZ<|0BMdFh z&bPHWeV2|Fp6Xpk>yihvS;L=gGN9Grdkd%Ng_eQ1e{lo{Jtj3b79s352#xesi)AAc zIGqSt9llqbq2=>uOjHiU#x>>7H8EzLm@Aykib+}gCU5|%1iPDj5+o{y5&m($Y3LJ= zq&TJ`nV31fe5Md*O~?;3#weA5Skb6GkX?FTY~fz5seNyK+V_nVqPctM%P`~cypX$RZsYyDu1SF}!GJ?KzzCPgAsEx1 zpq`aR>fJBd!+wFw9EYoAu4~aULLord_lPw;SSbjoZ*F*yqxm)54CzK$Fbct7@V8*h z+W7fP9|z~});4(rDhY+KVM#GASuv@RatUjfo>s})Y{XF^?Zc3ZExw_x+gWy3D#v}3 zopmjYb6V~&?$iJ->-0TtbiGXF<%nIn<%hxV>1ffu`>%-nnRYx!84t*n8P%(yXwY!s zv3Obu5IFf6!Cw$VMy}p|y&ZDR=e|+9dJP(l#s6uL0~7DKte9TP=;GvyRkD2wdop2n zA~1_0SN~g~YWwYsPqfZs65e4NVKFRx??(xmQwNepo0+R#Xkf)4?dKk6_^CrZcfX#; zReY$c!>`oJHUrF8Y<`)w8$6vB1o8GgZb+0|#Ra07N@=%SjE+3^FdsNqBiIA0+NzQ= z3WnUOWDqFe9H#YKUK+BRLb<2PRwh(9g=a&kAmG$AF>uS@6$n}_z?qCu2MQwBl8CE& zkp@3oSt=nbz6;Usv#Dq-Hh}C}brC%rj2D&4L0FblD@ha~)sUekKzTNhY)W%aji_Qu z+6W7M)BrM%Ap`HgPV4P4=ewkx5SO75Uq@u21L(5lq?goFvkLgiH- z!`FNsDs*ZCd5ZAj>96G2kv0;cGYxm->ljZbuiA5aqs}m^DK#5vjCm^w&DC-!B2Ty}M6aYCq!IKH-fS5L` z_jq&c%J`1A+sAYk7^ztw8TyaB$H^F-6w$@mH-?{EC?s6m3Q(rb!z&<+L4Bg*h#}@9 zV$#x9ShSg|o2#3FoxkH1NP++Wnt3_IzUY;Qq#jaw7q*4%ZHK^k?k!OMaNG;sv-77a>v(jz(6JAO`%H9u@`;p% zX*9_F<=)(7#eb~E!{ebv)<073KwEzN2MwM%0|%sHwmWPUIl^DM%hR3gJc*Dv3GXIR zjL0$TkE|T&9NPXPHl#pAkgT3TuDNW+ou_6YE{Ng{Oh1y-eMab<6N7d9c@K$F*?GR5 zoMQfpb|}Bk0(Cn6@V&VaY8>}94RCxGq$}skErWqa>>C;jHx@-GJN&lC9Ugr7t_5p8 z;(waXv4%;1di!TsZ*BhE{jUMb!0}~ZO4XJE-1GR0G?exNmS>2Igy5Zrho?c%sjC7F z0fuu>S`NBimf&QnWd$n8{Yd9B6^_RJI9N?AG8sXuzBf`s^EUG@+j+IAFz^YdX{W65 zJK}rydUMtP<7v~19#$GJAY4K;x>~DlclRU%Ab;`oZ&yFT1{^t;o#YxAS#Qx8&m0z@ z9Wa}Et!BNDo4|PRw(X2`>@PSz#M(lO{BC+Bh) zUGPGwP(`%A|M?JqD&lKriK^!@%ha;PNs>bb5iiEcQtqiHTVlSnKnUWv3;{L)a8)(D z-JQ35asuB`u`pG^o^08lw`Y@42HR^N$crGU>paDzXF3+8(OVKrst5Q$^1K#x!-1uh z=5!jy7*$oZrioN1tgP)aP~?b}K$oJGfr0jpxQ@AVEz;K}%xWX=YUp*>jULQBygtoX zjqV4x$K|ahnfLAF_R~UnXu_~J0-o7G#KRa>h$6+52=x5Dy)$6R{bNkuBD(L(HUP2jc10JG zcBETx6%$+(PTlx%XtvHJCr<$q>FK!^ibmGnPdjMyB0YI}%&dO@RN z`#5wEuUUNannkU}a^{bq{6o6X`8MT?6w2CA@#I;xL3iJGb$0kRnj4WV1jA1>L;*6;vFy;}| znX4UAi-ZHYjf1K^uq)BZA$pZUwtkw-tEc__0Tk_d+1K!WJGE>}l?F!;;-owLaqi`V zQ2y+QhN30ymZ>H;_V-$f<)}z~oj@!l;C7+3YG-<->kpe+O`1{8c$i4UKU`xG-;+IVBx$ntB@+;whUOt#kOs^Q z(l>k2taU7n1Q(&<{ss)I%f9EL`-Rb=n{{>6>Yd2~M6^vD^A~DW+a+ZflTtHU5u~H! zR+N`e{mJqE{QMRI6GzQoW8JHHKpDkA-P5oGd#WM>vEu>Ux0OlLC;5m6JmlTFG%5Ko z+*}ax-D`Y!Cn>AST8OHyZIFGaq$qH$a`!IUbA7iS`&9KdSbg7?dpHz!dx5FiF7fqj zc%{mDXPK;tWhxk77z_r5yyf>zB)WJ2qqvVf+p51B8F@h_-5^1OuPo^ zki>E=bT?dO8q2~qbKsy;YYZgQ3c^o5f1;oW~@5Ayq&Ik zs$sDE9oBHyC;}acQAQ;F{M-h9-CaEuolk`Inol%#>Og82k)cpP6irMw>+b2vL1GsG zFN~>4f2}2O=%JRU+(bynZ9QR z#h>pVJfE9w#gr4oAS;?CEFE(6rk&S=g@ZR&4vRd9CHhviUQ!QQ=O~ly$kjC6ein4) zS75;%KpN7_o?9pnY=%%zFy+^KDw(+UKlbSTI$G^zDx8}1JzhqMW&M$Q;(C6u0?%=G z_b2zgdm2e;NM|cIW^BFqbLji+zw6J-Yqyuy{HqUe>eFa^{as^sw0c{k*#{fy! zP;qEXnQoCjy>)Q`dv=*SgHZXn>%la zSmu@jr&q*&-^c;ae~&Ea%jwEwysyrdr)+hz@%NKw>(cA{+%HAPUDY;4?d{Gj!rG#u ziH3;8c*l;7JBPn-lxD;{apAfvjjxE=SJ(I4t;}CB&At`{#}~-Z$wH`89`@TVZz!tG}f;TP%c+xS&W^qyUe}2n|V*~o^RF$SDkbpa7@t3 zy~TsLYIE*e^(w5rJK2&&HKH;&Xfe=#8;qN&%0LyX;61Oo*Cx`+RQ#J2nFy-KJX>3J z1kPmjIh>v<;+_FqW^(2|5)>6`n|jp z>>op(_D8MjK4h@pYDBu&P)OSM^t@E7ZgZpvwcg3^1Wpe3T-`p7q_Jfe8y$gyL_U&{ z5JG{(D+v(5jw?wYq+>$!c5VQ$ex~Jq9TIdp9=D7#=hN=Ep8&!~{}bj8@fjk>;B1)A zRJVI&7QUq&PDr=esEz|4#24S_ZGjqrFrPD0;jCFUF-s2y8uOapoEoKuQQbB)V{~Q( z`jtFN~U5#M@C|`mp^q|fW?dJ$7ePixD?)# znr4$oG;LfgdnHz_-576)P98vw-!opi-b}#s%0slSyWRx1u2yMRlUZT;hj=Q*EX@Vg zoY_H%Wh;S*)%yp4x;wHwShSe`nI`Ymg&e7%=T+M=Ud^AP49Sb70ZNxGa+BvXMgwH? zIE~#KzpZxgz7uwrsz5M73M?mpInRqz_3>MkEx}sn%B?oQMzo}it5;)WvVG|CTW_>= zk#hdINh-FUhx5Ea&cq(7ss%tIk>9IQNuRP?o`fzkly&rXlwF6eMF0}1=2_i)Fvksg ztyBn2r+s7>YX;c3e_ZfCiA<1=FDyIazMK}%&Ks9;kZq`gpsXA&w0}ou+ZT+vgScB* z*QA8(NaC~O(D83sd!aQHCu#%$6yLTgx8%&OM=2cSxW=O<@2mQ(s^u zt9ppy@?jvMcTIZTe^IuMJz&KI_zqldLE&`sr?~}JoixP=5a9)v4Cnbs-X8x9d(pj9 zqpnh34s$7&Bn^Ah9(u|qD!<<4LzF2Jx`rd^_WzL}E$;UxXWln`1tnTDngW~b`IAVz z8fujxdC8P*FBGy3H~Y)muLkAR@QZM%?7;_e>vASB#6~}q2u2<#sHOXuSbwGUIgx?$wIp+++-zm1kN-wSTMKFJSPvNAj zv4f~{c~-eb6kYUJVvwL(Zc@SdF_q!ZI>uiI-v2Ste`UQIr#@0@7`GbxOcBsJg%jCw z5bdS7l{ZaJ-t~~-npQ<}NAEpJq7Wq`GNbr7f6zn~>6b1UGO0^+x*UH^EZp6#Z(UyA zc|IFgcF2WmRi@Ja#4BLE?yu#vCpM@E8eK;`fvchr12Z;c{`Dh2hV|efxvHc9{GS@} z@CEXoENpBp53P-yOOlOYlclz`%^-1aKKv$TvP2F7ajgl)7(llM13N0IGUJQy9ioY? zN?ToF?=8d_7(y~k-Cn0Qfq63VT^xJf(~%%0kAxNcm}pioj$k-i~X?wPZs zQZ>Qq1zz#Q=;_Rwp7GT2r`%V_3%t&fX6=4G&x~KJsD?$BcJUySF}CtB%%Yc$cYXI? z+RUJHKbH$mLDpgjcmg!3hMnCZtE>q>rK|WH9j52a4fT^*&QX{KDp*K?D#=aS(F4!v zWT(UNY|8x8I=(9faPdI$W2Am6o^z>wYkLw%JpN z+1U0qjX+6zWV3DJ@HuE@9aOfzr7Pru})IQBw4<0!_;Wr$1Ceu&dX| zZ#{?;$!)P|uORU18qvNLERG*xDGl|hl0W+-f6~Jn88TNGI5-kir|P+A+|U>$TF{lHmK1nM=AxVh3JP zR$GIOH&^5G1{)y*N38&4u2R%)->&Ez39h~P!@RMvO_;{)IcRUsP#sl$VO6t4tpt}X zj{{6mAJC%(2My&LUe(>==M8;c4uNuEu+}JAEm619;+G}D`Kquyr`hB#SZPk?oI^o- z@Aui>Qx3oW6p5Eb4>kGVU4DQ zUxw};d~Q4X z)M_FrsiqQ9oZ?g^S*>Ck11r%<|B`cBgb1l_^_KR4Ki$|r#20V^wG1ScumLw9jvyKY z3JQm42?Ds<{<qy#nl_zHn42VhTFdXIbcPd;O5 z#1M=}LMiK?yvn`OS}(diUVh|tCjg`Rk3Y>O1S4e5aODSSp>j5+1mDhku_)@jGt-d} zqhzaHW~q_7beO88=5DMw;8FltJozDKJ|>Za2VKr}V){1)w#pW+sOU&N?4xb_J#CGJ zo8)1k>#lmJylhsLNei9lVpU?KqzfliGjh|0p}QrUr1ZtP=Rx;wZTxsk2&K%`GMUC5 zxGh6j`1{4Kdd)H!|7NxOI@IwTM63W~l9m*<6&)dMEY{i7i2D>sp%Mx!K1Nq2fWopI zrMw3VPT{sqv4rwl2&@0g_N)`=3&|{Ht;!doLF@6I*w?79(M$;lY2{u^9c3y?R>FCJ zn(v~omWp-cM*5(KFSneMbiCm>@C=J*YoK9S^BB%=hiwzixh=XBg5rNd9<3HhAq9M^ zZwe%dk)#v7GI8(oT*Gn!YbX_&F}BkEel7oq4;QJcXl+p9n%DvO! zbQ`u~D!uc06VmqG?9rTs|{Uhg`CQl3x%pS6ypqwUlivE<6E_&!~(-6k6UcSjmuSAE8T;Q`u z93kk;1vA)67TXKc9Zx|w--M>+_i*TLhaT~ASLiJ8e*msPQNQjc2t^>3tx;Wl@iJ9y z0ycx-n5Lc~%C5h@xnUvQBeN{J_WK?6bkKE(R_L zFEnh*fDX^WmghNJj#%#+e50|r^!nDT*M8Y(?JFr4*4NLzy1Kgd>eABcw`Q~H`BIyW zg}KfZV-VARjD<~T%$VYq*L{bwtaJT@r~3sa?Zp$fFBdo?9xqY|W=-Iuiotm!92VWf zzgY`B!4GFg)u#b>nr+O#@Djw_0^AO7^UH8)y!{T1I2FmsVstyU6`L7t1o|{vD$#_b z;LW%$X0C%$Nc8EWm^s>ngA?@@CeQaOAH?lyXf#_mA}my11S~zoRLLM{L1%?^+0zO* z?oShVqdW*Ht*0u5!qhkzIdx14K6~%bZgqIy77X{I)9&b6;N+&vvpCE7Co{|{#Vkq% zZGnuVnUkl9m&dW|DaGLwqdYG}8pVzzILe|9LGK4xa8O1`!osZh2*4dF#eqWn<0u1~<*IpB=*5>zn}7X`>aCVW23bJ#>p8!>Ko>u$SE-i57a5?0x0x7&l! zP*&E1oHWztjR1O&X9Btq6Z02kX*4j5DjMA6Ka z32aA+a$qw_0@FL~SH92o`g0+GLqb{D7H~Ux-)iFg#q+qyc~H%Q^Za}hCQl$8kKl%I zT>3;*Mm^={5MXSCa(vWKqLz;lO#6t9MrnYOuZvIsVO{u@V4+kUj(?%ZAPk32syXI7 zO=E`HL`PY!$|MiQlbLIZyc<&aeBX^SZW9+}t;RiQn-#m&{!^ahhNAbS|D6{$7c)Cl znJFX3wia3)4!mFMb>mW6E#zUSt`9y^f);fQ1ymcsjA&yGa#dsjOwI zi#()nlpbINAOw_<*3?IZw%r`XbcA#?Kth#BtCIx{K|ngKmfe?IB{6P*sy{>e4D`OI z!baVrZ}mg^i0*t#Q%xv71%b@mBF#MdUDs8Lg6dL9q^;-8l6aq9@V1iTP-_g*qI?i% zO|lnfZLjUQU%IESbvqCLIvOdZqdlqZWjr(Xp2t3_ zo}TKi>8@Y>pG!Sehd=L7c|iC+WS03q!W_&R+_#C|KdP#4AC%R1PNBAwL~mBLtW*lR zN_)amjq1}IR|1?42TZJn%=k~--45PhQnt*{rT^*|!S$0T!{OieQ?o$E=W<(3usEmIW^j=H4E8AFhAm}2Tm11S|}L)FNN z2`rUHxMR6P3EUWr#~pC9nX6F&f;sPA^tM#>=pNtuYZ~g;678<{+?!0@`_h^#yj;tV zh!ED?!L10?;OM}q6)#srnq@A2w4xvaj;dh}8m_|wS}fHecc-uhOi1$C+)hTP!i$%i zou~4~)*ibd#_`Z7kb9Hj>YXn zL>^d?r*u>GWzIz7!v=p=6~-Jrx`R{%rbksPt@Qf|Huo-wTU3f-- zncyl0heycr30}LiiHu;48z71XkR^;KQ|A^aoS>g_)qInfk|(l{DiX1rrq?!=>ffb% zP6+d)s${@mmq~)1Fbfs~Jnw8psB`GjuR zTH9M&?q^IY=udlEiH2%?skQZpt;~e#0ik-2JNk);Osq&Qf&xG#t;s?}n%cG<6UagY z^X8_Hi9z2Ap{(tW(t1v}`i_!{-CcYCvVL>(b$%sVY z4*K(~s`F}#*L5y84U;r?-K)_Cu(r9!hf7e>&6klW zfb4+mIH+~fciYv%p%QSj1YPq=vd!zfZlY0%DaVYE4HQsoqq)PI?twIAR|)$t8Bp|k ztLy?{xA#={342OtZ?W}#M#WU)5p`^ixhXNNU039|wlW5A#O;z)DFD>1;WtQH0U9Xs zX8WUA{vHqTM?Bisin5*qI2FP=wepavJ7IUr2XyCeuzN*{s#0Q0oJj3Lf*pd^gf~2p znxw0o71hT|sdW+Q0r&#P!)K*%suQ;Ggu2>_C{m!BJ~+5&NP#C(U*QABwUBvJnSgBd zCfw1Z6W5A8qrCT(b)}D=GN`}L3rJYdgwmSX<%)BYbT_ULY;5I}jxMlZ@Vl#&N=g#W zFsbHFTwiN?;1Yvbo@bh+hf!G^jHlBdTc}gUJ~yW0NirC^Ye&Jzp0CT)cyfvx>>?%& zfLK^%Pyr6I$`ZUYm4XN?6ik|F#&J0_P~rB+3fN<{f}8GT6x=E6h6T4dWJWz>{I|zz z7Ea5uF8WE@XM?9XMnpJpC zSC>1Lgm2c&u4tHDYSjRT5x8U`Ghny#4;c~^i9(+t&6efb*GS?p%g}Gy-OCa`eEV(T znGIe{T@4geb#^-J9gc&}bFB-S>6q!)v+!)qiP2ytT?t@UC{>(GYpbl~crvfw&veCL z7GKrcm>&JCsb{dIMPCE6+G2z5b?e3r*ZgSV`pFz{^c|2!1tEd9Rqfn=TLgcV^{jt2TJ1sM zKmjAF<#bFb+}Yp%xUw>5*1XI{&xGJ9W{>UyYKRCgjQ&Wh9A~L!@+u!w?S=r#z;sCU zubMiYuVsE>XWS2Sjv2uzd9lw)qVZOu?5?tPpY^`&>t*$&N9<@F74!Y0!y_{s41YeF zjDON(#Y!u&A|n7l;W)aC7|wU0`jh7Nv?7)nj^YEmLE>1sZj>k|bmda2x7f?ir+c?+ zX@D}gnWc>iy84>c36xPT*5V65vT!nRT^KHo6Rxw&*HlW52czj;qi_I@jL}bKf}awW zuQg=|wn$(-(YTZ7WD8iklj?OI(M^!9I0%)%xvjKU^mxw=o(NYUbSwQHR8_##1rl33 zMXe#O9;dZCCXvE6v0QqzzdL5(XS%g{^InfFDy{=KN%{nHL`8ld0>)r?Y$1=D09-4z zUlsE`#!pXJ+Bv1yT@vWNZ6t{9Kh=$QU;c_R>72j+k+pV0B@_`#g+^c=SK&cH z`0RGSXHE($Z%rs=2lLru-qq>D?VX+fX20NvsZpzC&4j^91KK}vJiLsgL4{6n_#cX= zNGLAiA~2@z6%~(=B&#cZbC>0k`%2^qVfyNHns2;%`<6RDNjHU)DS@gi3JcekeL9|D z?23~WOp=m^89_|2wYG$V(R6z{D+YuH5d4J8Aw8V(VWXIXrZ8G@G|Sq` zL?qv-?Z?}K?R}`H2YLPjm85yU-`lE+nPR!Z`Ti=OV^s>2TpB{ItU$SC4NYKIS<1+n zu5!)>zNKn>&xUKbOiaxrRG4rbXTa#Dp*F zvkSN=%6D0re5SegH8D||RGAh872h%~OVR)A09u)2nMAv4{5TbE` zBb?nM1Riw$;{bU65@+t#^1vUmCFBt&Lr!OTeMlF6zX|7-(j?=_{9ml~)9IK6!K*Mq zuj-2L*uvb@;=m~?^@NWZOVsqNDFX^=;tB2qS?QTKwZbtAQeU|9%BxE&ER$r_#C3iU zLKsb0m2%l!MP*^18=w{3O=9X&wvR_mP_3-UW4_;0F3AxX`*3GdMId>rOn_Td0oRu^)tq@andJ87YL?}dZQSAOiB{>+xcFXTFksO9eb&W( zZ;AcZJ5RQ;e>%p&$q*tM&HAMUU=07e^AMIXQaPT?4>;-Buh`zsSjV+_D=Qc=9QeX(uipK`!Rcg~AivxrLxsDC339F? z^2=n}KK-!a@`DfF|C{M({8yB= z<z|I4|wUiYO7gHk!=ov1^`K$XKhMZ;N0|HM6Y*pgn|U z%aRfgJmIiDT&uiypIRE+Dl6WzV&z z_cY{iFuXrF9sVs{;sd&-0f1CN^_e`p1f_0RD?4Rb?ij6v@16S)dCKLo&p-wW-z|FY zQF^sHzfR{yAlbtL7u1{^i0WAZ%o`YM#s;EkghPWy)mtHW<#nFlZWNq5`z52*9SClo z&dWV2;JwpX`H;`wX4U(kpPF^9cf!ZV5EM;g02MTE_1_~jDXm{G^yVJra3+QUzR_yK zXnmW?`HC^gSBx@W*Mc==OqM2!j@D?n&QM*lO!I9{`1V~ucIk#1Wh&sb5UFsu_jWKt z&a!ED@8EWuwg*P-0aOJ7|HiEM*m|$=p@Xk@{?dQ$IRh?y&ecB^X0$d|#56G0b+c3{ z0Fx4`1}LhRaGj)T9*DT93Hd;K#p=)z>$@J^QaA12z(p&??V9TV)~(2D)6ZAe;-IyF zF-or|#xxh&?38u2XRV9>#%IGRSPSUXyCN#M;{w2OcuEz9K|IL(r5oJ}7M^|Pxd+Zy z9es+phS^+E$C!?@KSpuf4dd^3=F7>nXMJp$H^9 z(th4wXA$tuLA~TE0w)2ux}bAEd$8%&bAh8W2(%xkxe+?zJrHy|qx7=CgrjT?zuaM4 zu2uWMd(gpGy+92uP-E)^Lp<05(7hN6rve}fXn~zyw^b?N{^Zldj2?IhY_q*XYzvaI zwLRx9TW&S=GDGJ|1Er2&Cr%)4b-??sH3*`=&S!U-ste$&4_sLB#B*7J^_7}b4nmowpEY)P0D>3Qk|{ zt4#Pvlm%l($z8Z!>H(vav0&cEo1LrGXAX5y1qrDNj(X)KYvjlQZcR-ToF@_6U z_Yv3)a@b``0Jh*`4?62-tF57Y&^&i)LF<6d`mVP%+|L1+YyesWfRhD1&ed1AU5 zN;RR7!B@Dj@^pU&AIjPTZt?H-ikiZCS$7+{`b_HB{a&hb{qE;g;Ia1ItQYH5UcJzU zRVNI8M>x^#Ru?z9tg?tXZ`c+9Ll$}eXL#^PhZ_$RP{YlR=hQ(fUHxlapZhARp1CX) z^_B1f;OkI?&PmM>xTZjY>n&iyk*U4>0uQYSF0 z1n$LFak*lyO+}ab;(;m8!QUhWT09V=MT-_KTC`}_aG%xusl@Hoh~b>c=M}!>b97Lt%Li?7Fe7j; z4s?P`v2OUI!o3)$wDcvwrUJmrqNS7w+*Mb!Z~|dlF`ZmcjO&9Zi?~jI-3Z+IyP5FO zy*MEL4tB5wwxKRS5c3ayNf7y{JbgEqhZUHqox1HV%fZ@;DU)kmvV%FTHw7@z@=6W zy2ZOkG`h_P+%G!oKs<-V!3#qdfm`tGK&Ntb!|BIQy7jq}7H~G~=EHwSz#aOrn+_&4 z9052a;CdHzvl4*|W~O1-0x_KGa)Tad@LXJdrgGL-t&X_O2TnrnyR|+4vs-@~eyr-B z2aX-T&&8V=pY$UBfzEzJ4 zyw+O=wyi<)6fyCjla+p7|HZvu|I6iTD}OyX8UCr1#h(v>Z&k}He|G2PyL-Dk+wV@N zQ|SsB0*W3o6@d$Uks+vp@lp3Ws*+objMT;CD&OZ9Drv_RcIu^5o9z z-(N)hkG!+)s_yFQd!~Dio@23RXFZ-Bdw0iRYr(7xLI|z!%kqd264FY`14xJ$o_K)= zq>`Z7Ywvn&&*kwmV+`_9A%!BdvNF0W z_0jitM&!!!lKRS*zw*`k>dN0AZTD98l0*&%!=TsiQ^6eXkH#-LXZ|Fc3{!0xN(wms z*@)8#a5y>wJO(&%j30ND8dU(y0Eby~`U&xvR+6CbQy=Wp?s!yxxU*9xS?=nQ64vpA0ryGsgU6}d32?{zY~qMO0LK6zegY`! z32YIbfN;3#a13m8MDRdIz$g#-i84@1eC=<3^@R)P&;6m%_VU()d*bfLcR*nT)M^+G z1{e;K({$f3pLUL)vyO+2SnXK{$IPD?x|1jsz^AS-91WhBF@u3!;W`jsQFjMieL^XVW{!rHFnWRZjk#CwuAW`NT{SDF!4F1&6?ztt!m= zRB^RFfr9*~*X!XkSFZf?)s>acE2Zv?Mx)6w>Lrd+zZuvn^Gzh^(!~p(O-7?{@kDvG)}l1#{pHnFcGlqjolR)1K|~;>kfj+!7#1aS@4%X# zl470!GS)2_=RQmHVx^yFm6Jb*=9G@BNlGC^6v%L{5pBeg+K_@|KPe}H2Z({7 zT@ShhGS0E%A6KT3h-ylTRvgOHexEZ;(!KohOJ5IFuyHWx6I-5-qUg<{(9~_lH5l8M z1Y%UP+UJ;nM$P2;Wn<~<7- z2Tq;CrV#W^06%2r^r$Y9*{a7$l={;HN3*~YpxCc*s2YQ`Oq7)T?3txMh@&v?PqJa8 zWV_I=^w6#4+K!)i+Uk(M786mO84Y8c_WPWl@3t?+wZ<1ezIQK5k^~G8h?;)9vf=oP zFV%w2z1D5K{%g(Ja$()$9c_vu%&MIEx1#CV?RMK=dh^Xc@Nq9-oqrzu)V#F zvM3OSp>>XL7Daxm(B)R;CV+B5D3uW3vh4cIpR9%J(|pb+z#U^MD#sZl=UegeI5(C+ z(dE_ojnBUH(m%U=r!&St8*ACw51c8;IvxokYpwhD0KTcM>jU7EJly|t zb^Gz!s1hWsW7UjdPu3QgA6rwDrE#0g z7&~@7fNnYFN5|yf4iElCh*;Inu_?vu(3W|z6*ogARv9R#{+Ds`xu>6hUTi+>AZ*R9G;!Q~$GF-1Hi-8bygNXi@5*U<*hX5Bd?u0wyY%L#muJ>16;f@9F zD3e-`0+keo{E(BvB#ybfe0t>%&R)K{wt4qHZr{F%d^~m^-s?RcXXRtBa32ss0I&c= zAZ6Vv+>*AArcod>Q)ykFtr2ie2q_H2nZTHUnEE3BlKv=f1oY1)te2O6r#m*4fP*7&4&QAyq>3 z;y``3(DsgFo*Zg@r(;^kkLjWNWS{%VRS!pe$e|L-r$5#LC0}Snj{FF(QzZ)DZ+X$<5oP<)H`ToO7k$E5-?%73Sf=aQu-Jv;~F; z0?`DJJ5xrvDV>udyJOv0mbQK|5Pv9{nFadFo;PCNDRjVqU)diB(W%aIB~EVk;PVwBu^wEIyYY0Ln5MZD`lUA%bl z!Ykgg{ZU@(zZQfM4aVtPk&v$lAKxynYm=o*JcZ_l0+Q-ktVYVF5 z3+tZB)nNRWtc5&Gi|-L3b3><*-$ zrg3iHInEupR7$~-96GqdS)NQc5kfEtfo~)DvoAFoX9sEW=3tmoXEDUnb1nLPffqXS zi{ZmCy5NESmLNRb+ut`XP1Qymem*xWMrDBmZEc^>w}2Clw;e;LxxUTJxnuB*LWvo` zX8gHh+uUbg95``A7h;5BFQ^x6_GcACMPNTy9c9>KYp*-e{?Q22Dc|UERYO5*n+6;7~7qC@XW!a_!a?q}vMhuUs45sYu3x7l&B#Od zbeg2FMkCL2Ff;u7Xy1cf>H*xBjhw;kiTwFO+O1tdNAZ*ATP4wMXbx3?M>8sWMu zTB1xQ*f)k#@{8jsT!|EP(4Q37l^~SHxns^P%toT_u-YAl=?GtrPg=}p$I(S;QXYi7 zv~IOqU+oR@JIkHg)hiooubjX1RG1h>z0tsEE64rE+qVzK`KUDZT`4GvgP`&G*IxVU zlPr5FO>#LJ_QjpeO$>*9>5QqjI&Bn%fhjV~HDlDZ!S&uQsK_xlKS!z-;lYE)s40Ps za~s$njxiaHpyB{aPhH0Ko44USbbCdKYpaV`ibK4jH3rELR{}{32YZzGz1|;;v9Arl zC>hWmY#S&+g|UT)Ej^UOJML6JA90|Xj)AKJ^5m)qfIbO)#yNQA$XHjbbmEI)Af8@c zS>9kFXfQaa)JFz^dDKrHXk$zx3ceI7aesbk)p&J!?)L3Fp{TaANb?d?6I_cF7Uw#+ zb>{)b9->ewv=$`NEJHe(pzdK5jxkA7A0P}O&0?k5>;j&o=*1wsbwTrLznoaE+?AYuompAfw95tC-;?0t7L=U(|3 zpYH!VlkdfgeFS!-59B`q^wFO1thclPY> zK&X0I6i`9{1e97=1@kbD(c3*hX$(XVpfSIM-rfMrz^OB5Ft@ydM-T2J@~@q{bQyDV zT|BsV51nQcS6_G$f`Czfh&+n%VN^${1X@V~p5%xnRlxPN#cpAcGNUEQY+E}9aiS!R zYb3jAVfK&76!1}V*5{o8j4(4!f`;SaqeJaTA}@_z>DEq7GVN+Xpn`h06Gk!WwHoSC zh{qomIx9_&h#OMMG}mqzMbZ2yNlBL)^=2J|@ub?a)9K*Kl`D{N7$+GP78Y^QL$$lR zi*x5MVP$0rd6t696gQ^o7btX_H4GXxj0XcG;}O=D=ipyJ)-SUYj)88rA=AyMn>+F0j)x7i&{@Yw3ZqNQ zyWM(tVPS4zG`F_i)Kj%83WUUdP4~66LuP#F*?iaM=$-}Oz^>XwDT(tN>)6_x7R~$c zEw6aKfRB2k2_lK*7-7<5_&{_m zb+wof^)!ebbD{)wtm6rQ7JxSZd>O#612_dBoH>AVTzkB%=8z_H z9=C7Z#QN$Iq>|X~?IXDRG1gZWQGuN0$V!bkil89SZng*&@?h&YA%Y{KkeQz-5IO|3 z8MsKmE(1RhMB5-}=NL~0%c@g`%R`^n;navd;u_bZF8<*YlX^6iBm_wi$#GU@%{Uk} zDSzfq{?mWNqj7;pckklteBBJj$*2~}-AKueL0UeL63u!o7E`}1ifSIJ4i*>ZuzB|p z?)kdw%IXSEoj#2yjL>M*t4MWPg-DVFfBDv1xO?{w<~m(uS%%(D54y~&$Ih`|b8LCg zj5YAhmEH$jLVuFNTI&I8pjN9xhyjEyA*h0_WIvRkIm>)Ngor?b0mICO@x=TQodI); zng44&44e=|{%_s_fN5FJInJ2bXzPk6@QI%Qnmf6W5TO5??h)r0g|@uXsh@u4{OK2K zo#oSoaU6l@;u#N(nUr~Hx3bc{pKBX7V|5lJqFTM4_jY$H5P~Se*5ilB@*FY@!OUp2 z+R#OTKm}-cmAiTKdi8UEkOpVYY+$GN7_E988y7EO!Gm?8e11Z-1 z>kA+Z0CxU)!#K{Bpq3*FCb{m{!YaZY9=eEU)>`0*0E170hEL#Q3L&PMb2BVW$DcWHhFS_a7kF2J;IG*m-;(wK&53`~ntz zWIEj2!G|CGsPf@1U%G@dr`9|$BS86lUnMJ4?gl9+$XD7u%g>g z^Y3jw{0j>_cGkXB8W$)j&JYP(XdN@qF_z0HpaAS_XKv11@y`Flhu(xPEg|))b^YUe zc)axpLJ%&luT_D>L;rl*@;%pXAS()#UKRcR5CSOt^ZmX3s{2=KU8P~9Qd4@Wl2^wA z)o!<8bXh57DS}Ext2Xs%5F!;AY(OAcouxp*U}LPC0B}lD1E~DFo{y|+VIb#x$?NU8 zwfQfR;eTkY%^G$0jnQcIRv;-7gu3v}-XJaRI|dP<43t=z`uYY|PpxC~=0|AO>p1UobPc}(Q#0Jc{NfTu z{XM+%&byE*z^kvm3Sam~e>i{w0VEN{5u_mKvJC1`ctJ>+fsrtfYi+b;XH*y&DFoM| z@E}yu1xj*`;KAv|;AO||v!)nzyl>fAURwG)b6t5Q2*V$Ab^aR%qw%+`g>A&@ya#SL zYeElSDSL7Q=1*BuwT^k5>2a$Ttk+uI27s^TY352-{D)XcILCuPigudo9T3Hms3nNR zG_sUJqT6bq>lMd2O#Qhk(Yx5LVK^Q`2&&Q_pLyX5{`9}^RXMscpED#s|cPBQHyIBAM91+Ln#mDJc_)Ol4vQ86qHa5A_xfX zuAOm>`Nit*k^-+vu<%gAlKfkaCA2lc$g6HV*h3?VR+ksLU#bW4O{+_rj)$+@xPSlO zj3(KpH@~O)ll=D4<*%c^df-$-#^&@ zw*+vZ?T~?XAgQgL(?J-(8jUo`s>pI<<1}_ZxQ<7U9$`A3yL28-8{B)eg(R8a%-Two z=*@aPz&Q|HWl}*@`D;NK13avv(_A~`c~(WKole_>IKnp4O^fB%JOR(<8p^6G^3uZpK0rO`8q9CFukRdaR%&GfHnx6wRS!)FmUSP%>)-f@d zr_Gthvi5+^=Vd`!m$1%ak|cQdhaX`wNxf3ltNh#cV2q+Hy-7Wd^FEO~$xFmxSgGH$ z&p!_-r_pC;TKcL~?a%+`H}U>^?_s*)^NnBO_QDK*kY75Mh|) zNX9wJtUx&$6PISa8Hc~?Re#Glwx6&cj!sPKCm2Eyd4PgeEnN3~Y{Lgzy*t|RwPv%) zMX4V!^CVQFW*wWUPvhY{t&K}{S&DkQjYchowFYN=kRW>d00^tz493vYRwd+lSw)#^ zOLN%X?qPRl2dzd8jYf=z-ekV}&fB=KajF9A-h+o2j>for?lb^YCidp7+vqgoDu}Q~ zqc8>%Kv`CS#8fp0qyjT{?s7+Zzz_tmkbn?iW(XC67$7}_jhk>J03t+XUa6lne$^?Y zv(UuV{*IkKm?Q^8RU?gng*)uaom`uL^q^xNld+CL5T&uBMo5c;gWZ1t8N6b&UerZ? zqY(%9^U@^FaafOnRRB$?NO)h2qB!#4G?Atm7U$=D&)iOx$hGgjjrG+m#L* zc~xw-TaKj~JoUoMPX+a6>{I|I0-T1{7KR-{A?YR+N}Vxsqs?{rD8UaI-!g|FQ!|hu7mSbDo;hK~koH6!0c9)Ha)R>UQ&B zlKp2v6iPuXg;0*2($=Mc67!|CeR5h5GejW05)JU3w|;=Ms7$E$=gwBCjLy79*Mrub zTfp#OzXIyK4-htd3gyo2kKkd)^VhCbFn;fQ-$SXlD-%1{jIgu)7<&hOD5>z=v(I8O zJn)r|dq}bz&KR&1V3Jr|TR$E9+L;P!a7=J{1}Y|C=b)knRM0>hC59vr`YGTbb%M|o zpz0VcqXfWCBOvF^_C8A?wybr;5Yiau@ML^i=zbX#?$e*pI{GV$#1t?RDp>5cPAQmE zlJ&xNKYQCaH>m}xNrY*YAn=D|zP3LY-s=xX553y$hC#5>+u5gwTiY0q6P#UJgK!4V zU%r4-XD>nqQI#uO81_-!?;#{=0b_f2x2n%dau9^7)b)e!f4{1oYGaV5DL(k&d)PR= z0&OgGSymN^I1tc&uSTbXxus=ndhq7`+Gw>!u|KFAtq~{t9{a*i4rO<3dFi)B5dPhAcb|Rf=_9R+ zw@d9FbQcz#F&d*J0X$$V6|ByYj|ZTrj%}|{-dtAB|Nj28ohYsRG+2w>X>vFSJxB_| zX^e+Ml-lCd@)Fw3CL~ELFD>B0xectYuA$LrAU=Bz);YZOz3-#Y1?tUKl?2YyF`7lY zigI-}hBE~OIgkRJ*5Kh|m@q<2gu(+jPA658SrL$=$#gnh+9cQIU2E-*WpG+s>76hWu`d!t+@g*JDD zpte%#R2bL)hBt*uN{ONwwls|WO+v$j1gEji0*x0?9P*WbYXJGXFe^ET2ace4#3 z;FvrN3R-JCb>WJEi_~7`}g15>anq=imcf> z2cj{UA0&CPXPHYSs3AyfXNCPn7|3L2l;00MY|JPL0MCAwKmFI7^DhQ)pZ=?P=m>LK zO=}(bR`6hwWK}KGSWDX4g?25xnwDlqdDH1ZTq?^PMjJ3QX=9c=e7`jqjYvgtwfSFu z_q%xU4wfY=CN~5SjXch^-`;iyXF1I{ZXbzrKuWDbM3hBY?c7{SNZEJ z`MB>iJ>~{-A&BJvX75XS?V7q)QdE*;2F;ta~SzxV+I%t z5Pa|qV8HOf7aKnMW}kdD`~wUaumKyMgJpXx!yZX3^&+*@-E4_$7Rg#zYgT3D^6e4v zIx;}Oat*2K_RL|RW}pJ!mz7yrMDfXYecmSqqhz0glYbRP;XiiuGcg&Be^d*Ui_^?W zDCLUPW-bWS@!2rDl9wg1^#eBmn1(L<>@O@}erXAx{o)RiH1jhJ?tOfRVLY0kQIBD) z@jp|R8d_@*0lLTmV-eRI_>Hf>f#cyApWglyQ5b@NS#sflr_0>;S$hs(E+a*=-tcbv z*k!`bvLQ>R7Cnj{4|>Zz|Rio{rm-Ie!OuJbe0*7P((T#-$o_>g)z1s zkeQT*voH|zv!_)UmSGe{QVNQr&|4d8jJHVBY2vKR36@-p6~{50J{y!Eym9MQ>>r=v zEFM5eiDske$(5EF`E=qt13F1Caa%b6Z9h7|%)+{!CW0si&wy4{e(u5H1xf|}nmG`s z?&l#S#^)nnY}S@l&CyAul=^*)J=tQz@mADhLDSs+#KI!u&$N-RaVKlFnTFge_Yo#WD%mwALm*JOq!4 z=(D0Io{yq1%kttsDM2PHZEmgS*_mf2LSLRV*w1??8%ZHMudZz@yu9+y49mAoAi~;d zVyo*bTf+~w{(U~lM4IJ$Qpm-kC`VfB%oua3lr&)&^@==wHJMI5HCz${mVHrIYwMf# z1VGFHV%RH*g9+rn&kz8VBxuVXK>xS2N-)N|RsS9}C6HGJp_GtrdvP3Qlx1aWaj55- z4RJg;9XlY(R-T*B~j2 zn|oiW{k>0r@WFrBy8rOs2|-K7SSjVg)msK*5Qh(Of3W==MNgTyd-3gae+8|3acA4!IU_qWU$28lnLsBdAY}GQGMkv%ID?g3` z!BP-w!xu@G{FikM`L8P=7-AqBWB z!7S15bv*SbNm6)V0sd#SHvZ=s@EL1W`5|hou5ZF-Qw%2wlt9sIHsrxsI@VRGW1`sT z!mzcp*6X+Wjfl7MN2k61e7(E4KrXIB;rz8OD?#MtO(t6F$hFKk%kvY5+CiX1UkbAI zP<67h+A)^PXBxj>tY3N&a2HQ<@%No8{eJJt{6G0h_*|7I752DZyT^Cah9I;>Fda(1mO*88!$~nAhKUqrJ4YR)4s+htXg#og|Y}L7pDB zFQq8l+R3x@ZY@xwQri!tpv;A=55TAZnAyHSCAW{i#~1fStMz5z!|~@4#=G#xvqB15 z=2n@UjHt{@I8gZh+T=fn(v|&*JfjgYf*n5JEO#Fo#bgg z1hZw}T|u~27R3O-m}e^H))viJb)7rC4L~zspEC5!PssZ5Z1Vok3#!o5=+M)L!{g>l z;dgAz|1ix`DzeOz9{as6((yT_AY^$_>-Bm|R+kT&txkVwVgC8W#f6t0kT$wb5QGS^ z1Bz(1n&gYLdEpr$T5B)}p#$HH$(t8BmRDABeN2#_pmS!Pq^F^)iYDXf!E;xx+OyL$ zSrr9dxp~vaUI&MV$nwnH2Xu9F<9ELC_S=7>>E`~XlW|~-kpST9ak_#~3TcuepMgzl zn8$Ur)8RP=C#N19Q51NZQJN;^_U+rp&b7vF)m-VsS;SaWTGMDmGr;)1DF`S_?I}xA zNIzJ!va~oGs`2%=qmzLLTG)_S?$_Xm1o-HqV?dAx?vjBu)zRP- zX_3K{#yVh#uiSclUIoh6&z!XYR|XagB0^pk@Woj{m}Z$*%6xQq%x*ZvCTYqNteW8r zL>*jp&G-HNN9Bhfez@-}sTM-cx(31g{6fvSP?z^Tu!l zfe?iIGq8cVWI$ht*Deax@UuGHua4pPBFLVk_+u^eXmoyXd~|%*d7YKfWJ)vsnOv~w zZAgp{Dk}GL0F*#$zY_<6binggi$Exi@kQA2@iBJx_GZC9r&(r}APNK|5wg6()%8A_ae(dp0bYErk2vbIdUMln z{Oq&icMaPqF)R`Gm^ph&BJhNg$mPKM@(e;>uD(rA_ybxgh)&MVPYxdK{mb5bA6xgg ze$&8+&W1wwN*!@W(WxzjYmm>1x!#pM+A=?Q>|RMJ{wa?8GeDvY(G$3o5p06~BRL>|{$_1;_^ z3-fI!ZgU;(eHjGf$}f|TKl$X;7*ob^G;k#m_2wIIej^Bjo&%9j6p}RY@tcYw1S&+X zCn(ASwJ-!pSr}^@jyFeXnn9Zqo=9T3gh6=s!Ku7)bsnu&i2wD@7H&MZh+e<0KE8Xf zE(9G{#-1=EzhKSm)2!SVOal1JE@tzz3b}nO)5S?%W_t$*?^24eQPZl zWAXU3nX_?TrIqGV)}>0z>I#F^_uqfu!SdHzw8Ht(8b4>JVy>dAdqNh%v=hh zObC%Ouq^~VV$h>d3Qa^~2jW_{(VTa&*y8g192q8Def}!G_x*QH{_~&w$(=|0`y;n< zFfx~swr;)l+QQ8nFGf-bUw7L-JanM*77o4nMXy(KK0I^m_VJxYy%Ad2mNjPPc8%8h z$g(}FG*2zF&dO@oh{Iu7=_PAGU%$11PwpImc86GzA}B4+&x{=q&;#%>;4PiKQ0%q8 ze1Azo;jdpHrsH38z+Fzr6hy)@N^8xa5lLB%%s5_`Mk|Z&+R87eAP#;L5oQU4H z%=Z}B50#JvngKAb3=bKw?w(c_muDG3Z^qn>1BoBK|6_Iy{=@tCADo?>4D$W`gOOvE z96R;PZ5JONzWvslYpyUX(=>OAn4h62OJ7du&MyJFA z0_C#XaD@ocSbIvCDR7w|0))+#_QJu*C^*>~;H4-)Y7BuHhuR)kU@ug1&sv^5322wN zSo@?h$ukT0E9m7HcyUxz#!5jlFZKOE&=^zZ_KWe&DzS}tE`+R8EkCPL_c+& zpAFGm4p0(ARB2n*LWNL?rSrq$qt1E!ZgVaCW@ouBCc|9i=hgDjFdee7OBVRBn8iba zI!e-lWljOuK#E!V*@;-!NLzH~=dj^M{K8sX?hOV788Ze@uzx*v0f3o3&CQL8`i+Fp%N1xPDfrdd%DdG}|epa4K!DK&K#k))7VSzZ>ezV@nPpTBe? zFY?^E>{Lmazm#St`fz_!?(P6P+t37|E#HHnQC`_85wTnLlK9O&WBgu{AbcSOOmbgt zKW@&uiSy!v*;%QFA56dd(zh4ijyiHl#xX|6ad*_u=k=jo1M?jQhSs98gn$`yf~W>S zx9ZI{t4LX6VN`_G)fE@GUyWY4dGnRgX#DVGaI(F(w^un=T6fk;zxC#uFRv^w#Q+r# z9zLw@KX@=y0wXsL*iZymn2fysEQsZMWCcQrl>P&C|Q-f3J1bx8qhQ<~C|_`a-^to=hKEgXh3FV`hHmj`romCqoIxYdSpbA0#iowHfX>elP8U611UrmnOfk1vZVcY@RXrNwzl(hTpu z_dYk8jf$Q3R_zXSk+P$*Ppst;fO7yT$bUkluF7earvGtub?sZO|J!PJIygKygk@kd zosc7w%xLRA7JP7ehMJNHC6%VIr){UgK1$2?;y^S290RaVsmbB8!tllQWmix7l~zwl zA+?2c{{c&dv3h;6z20iYJQ${p$t0^C{cyU|zg|16wZhfTLX6>Z)Q$W3LVlvJ8|FIz z^2dUIDTuiqsC5PIvo!zVlyqP8=&&N+Dc7G`q67S#54^biVfXTes@< zNX)tj=c9A%?Cy{|&mn>?qLrwDe00ufnj{2_L12Oh8)<7(Mbf${|5v3DjY?~b#}gbJ z>_Y`1Ha0dq<0K42y!YPw*gHDHwPqV}n$9U99^w0mnZ~cB*nm_(*_q2Q3t((DQdi(Cq*$<9>I{k6n3ztGDzjdzcVm4+{P7?C(d%iN7H%gyJG;Z3 zot^Z5fA9O1Yg?&iqwc`b@p4@I`}^+OthQzW)HHr>6*{^yfS%=0=C`_DNA4G~!@PWVQtXhGCv$ z;hvV6K8xYLxV2;Q7m~F|&i&R`Hh=f{Wb#i(Nl`b(`$;-G&wishC*!kEvUeKKO-@#B z%q?{mYB*a?x=}apniIYT#FMy8I!!qs>NZjMG6eQg>7eZv; z{Jr1%TTS=ovw4d6u_6+Kpp=~HlX&a)=+Peh=wcjUIy^&;_d{z8Dd~r7T%UH^^WCy6^UM%q7^iO9nweAUz$VBGMVB+sIkf;@nD0IBEGEtP zbFLTb$)k;0?Xf%UU~6j&X_liNhHwc57t<*#L8z1HuSgj-Yrz`?l7W=$M1Iw$SX%dH$Nw8cud@#*g#vtro^`nI*O!JoM9E z&Kfx`+sDwx_W#j-V!<;TGF4SsqH0P!D*#gX%B@J1U`l~B3Bs$BAj6f6e9T7;kSrC=iHaC~zaE3}?Fw-1yg4I9$;C-AN9>5sue@{`C z=y!WaTupAAX7?)Kkzvd8VSv==PSapb8=Hknwv1(dS`+`-k$2iR)ppj~V1+xIcqO?2Nw#lc9#aHU{MSX5L#@T8#cm7!c&akSD{pn)2 z^)fM{s;U4%hyn#EFgwh{Gl;FVaAwK5&Ry>^rIemnxwpIP!Jz_$G)esHq$He=ris>i z2%?MuUq=B@S+;pAR98R!uoMWZAPb-PV|9Rh4GdF&Hsx9;=RE)u5GDKL!Nk^rT42p@EWOZ=u`2 zwbHG>Kq?@Fv6pJtb&0?`KYAAf=Srg}@{;-_2pgN5SeWm_WvuwcojXuU`7y)c>~%pn zn@ixq>EL*prVjud5y8%q1^|g5Vb(kG@201tmDSOZ>Scjk1z;&WXeTpWlW79w=VFyz zFAM!@Ag(VewSHD-EN!!K8Y5R+%@}dRkC!fg~*fZ3AY%W`pjs*faOj#q!p8E|;Cv7_H54WwKoz z0<=_9^2PH41XCeMCdj!4Z`GpMvv%FR2bLfSW7(H4T#po0=~a>*n_u=SPFa@lG?|_O zI1qwJ8=Ff(3V>j+JvtkI|8K{^^K-hKYYU6gFb2&Q3Tset0XKmUH=Bw`DH$j3gEY>O znhF{W!vKNO6JsBx+Wt(Cz8gp(KUc5xi!c7t$8b>~1IrxMopob|XvM+HD@zNtuf6eA ztgI|yw)%5jG@5l75lOcyct!~)>6zZjPxWDPRsu}uUa2+1j%Z3DH-biM=`om4>dGHC z?kMCCVc^FB=jZ3)bU?hC(ynu(pWOb1vu=0&u#RQ+!M-2?6+{?M#z|Hb2TD+3nNul* zG?q&uZ=mVH{_#&*WpSz>^Tlg0t>v)H&0=<9-mA@5e`hi%sJ&W~jkRDt-x6iXHqOfmBm_jBBLt9> zz-ea=&<5Rfie^>0TjRxPy!ER$(0!~UH#$4>YD+RyoQ=~%w!Cc!Ln)|ctRKLZBxLgU zzx}PR-+JZ7i*z`cD@P;L9vwhFc!2EDp3jWE4vCPS$eHE2KSRR0r3eXI-am2gn=(!V zqK~xY2TG8so=jBbOI#^GJA(XkoVQhKgSkf3l7coj`u*!J$o{5OGJNkRKgDNT4}FYv zczgl|1H>YZ;v0?uG8!l89$RjvhkB>EX_6wXnlcioEe7-QFxr3XYj1H{8DvEX360c& zp|!Sx|8X$zN0C?41k0<-;HvTyUJ$~I7M`8?=`7Fe?aa+V2*A1d=Cv!$jj}9iNt!s1 zTgJxdkt?Ct-~7g3`@>$h-Mfc?4lN^DSANEIskZ>b1{>>)N|z||5_y)fiK5*}p50F> zJ+{o55EK&djv)GZR_LK+o?LP)rM*xBv7ggz^0OEYAY-i^CdEuWEcPo|5@ zi%U2-I>O|9F?LOb>sL1cBE+2ztcp;n5HinvoL7y`addbFNrgq@;hUDh8 z7af69j>aR`Jsa*ONjeIMr=g@oTRVp3LJ-)31el30Sg}hoQhw%AFOgp*_tmL~?X2R7 zF3VT)tPm``mGC2gWqDll_Vny5H4KdvvC>i~!w`uBSJ>`h^U5_?wuqF(ewO*xR;xCf zuy2Wo3lYYlGK_kpqJB|TzGG2T8nFX(ndM$>GtYC(&2|0BH1%fPD{e9vN-4Jk7d6z@ zR3KM3*Ee1wDJHJ(`f@1g&p1oB^L%lgK^R4tsjuI2Le|^6JF`T=tDslb`Z7$-Z7HP( zURhcCKkS`Zk0i%&$N!O;b#?djTsyPegItlKZxgaDAAlYN!+!9K{b;}+!_WQ-e)5O# zs{vbp0n36736NpQ5F|<<#Z${A_n79!OSd|V*bI}s_L$4_DuaI zA|o?0vdEDZX}akl-1KQse=s4*emz$!8(shYc6KDO2vNTx81m8S`|x336vs{gZWhy+>5q3>_Ogqh_~#Wn92E0j&u zmLEIkJ}GBqyR);CuH9In^bLj4R*LdOLr53udk_()xtI#jz=Aaq1Q9v5I8~|GASs|^ z2I~;UU$<{;;K73lKJk;Lv%~{2$T5J1ogHFtcOMUrj&bYm-B5~EFv94=$>9M)sj$HS zt# z3e&Pgqjgw)@Gx{hr?~0ctW8tHItQPQPIF6HHTCP>s{S2wD8u?40zCj27iqi2ISorD z>W;F6J#6*;58nInt(U*{kB<4w0|hujVMA--qyRev*sQZ5Lu7w%KYXTRz*v9*NF~Ebk}3chhzL~~ zDnr5N-UYDV_n9PFnuH{QG*Mv$1k3{g-nPJyfK?FWr;%waNVr_Iyg=b+z%|-BB?SrC zbZ>X}z4f)#$<$8&V*L%NganLcnAS3!c+WL1q+kny92_{P1{+EH(w~iAPkn^C_vQ?1 zw1GQ!?%{BE2er{*{8XhW#;aop3dsW4ys>Gw~osGHDlXLJkS6bN$Ewzsn(0NL}gpauQu-H1sSQ`8>7)|gN(7E(f}V7&s| zf&>yGjJ*2|!9!h#?LT@ z+O%b3cD7cFp>CUW@8jKn5eGw*lja|C57=lk#c(}Mt+v~?aa%_7@4`!Eg4kMm^yUc; z-!0LeIoIAa49Buca!{Iw^&85n41Hutp2N@9g3qN4>oHg{k5=#D=G{Ax%s4*SMKN4~ zRVnt*rcguysKz#60Ida_KUZ7;Ga$1Lrfq}k7fBM@cG_4Ze(y*CG6{>zk&;M+z)DyN zrwG|2kNuDu84Z#G48Rx*6-tq{7!-LX&mp7N-vzRQnpunKN+o z4uF;2qvLzVIFt_Xb$`R4y=>ah9|tM1a^oe8UtJIVXG#E~t+4<22~4Z8a_=Ve93aaO zsS45OARQse5;y=R%u&!#y2i%Y4C~h6ajS7wmZ+IRk|(SK3yGu{hK1uC10+3=3Ffth zLzPU)^MGQAB2Q5_Ep%%FY@(@sa zNg>kHQbQ^7$CP+joT_Dh^5f>8)J-~*tMpgK@D{U^LANh-BARZ$JuxRIs?QGQ8nOULHB-cZx*)(gRm@f8VjjFTfYEaz&cZ zq%ur`?2VOt{b<(c(NJwFA#R-2`mvH?UF^#5dkgk1XZ#8>{v8P3ASDVgx2EJ%uK1yy z(3`^0hx6PzrP94bN~~|)z_hA@OIzLA2D8A~@d3)m55gptx}2h%P7&7HVh%Ok#xLCk z(NoxD67I1w!&wVBs{;(i2}~{EhC{sK%fha1Y~c9G6HI;P@alexmzBb!&&=!LDqOO$) z*c#{RXx0)5h|N*920$~|8q3u%PhWcVwb%ako%-A0*O;l}zp?)cIXOoRh%aB_5vPs|QrEz&d#pFu>h zV7T!Au`n{hFW!1P1QuY%>af75$l;hV&LrlVWd)|jV()NYJ}s(9)2g)qI4*s3N!=Ld za^i_85~(DS^H%L_l&h>UZdVEtqxDVinbNZP$AlVvb#55e*r#y98-S|BB(42?XxgX!e}T0 z7!Go@=A&VW@@O9kG{zpbsx^Q}(9YVhz;K!mxoMii$#k*{U?K=RmO-BuaE!oRLRDi> zYs*Rqds?;=51LvZrkNyGxrawr<<}qX90&#i1jcAOJgaMO`9zV)O#w<8V;*NpB-Vy5 zJ>M}r+dVW62PY@&XGk#tW{~I8R>%Wm+Ei>1bHJUDfs5Wvzd>GDv@-jeWan%rlGF#Daw93oOC| zx6aWhPn36w>9jVs(snY;lCcoToHdOjdD}n073X2{OkTgKdfdx}n0n~in zPV=A(X4sI6*W&2#5I#64wK2bB$N#HM`(ptAMnwNT%F?~lS=01@_CktY)bAoaG4R?R z7pWh}hjmayNY)1Fc$llNG1JHaH?6hCQ}we!mTVnPn+Mi%xP5hy+>(OERcntNGm1pr z^R9U(PsDaA#kYK#&uSvYEfA!(?l=*&O@dUO4UHex`ue0Sw>{8ebC{z@6R6zphjaHj zfRhP)1*R|rRe9hf5iGl~5Qqbz)g}y8dEkSUv&Mwd{p0ZpvZ4qsn4Lp?bO6l`8+Y%a zRhjRDdw??^6}Lv~JWU@?%IfDHxc5hSx|2xy?zFK_HbyBQPur8KXQo)`Vlzy84UsDu#p-f4Z`A7Oo%+#F1 z=6Iz@)*W-(7&jSZ$xUYVx7*E6%XcY32@$3w#6COoW~-Zj_dp#nB5xd$%w`vfEij$OFd+g2QVU=NN@@%Ygr zH=9f=SpCpB`=)oF^Nio`t`zBy2AP_A*zN-;nYo-+jqQUiTDZ$i9TZO|M~m?w$wzs* z_UNcQB?5VCm#r4*E$euCG_8+3fE$jPgrLk?9-fr#0f6~=>Kv4}5Wm@I`|+e|hYSpS zqUWpDVV9YYgdnP0Tg_*?Ys+VOmi|VT=HDKr={IM(-YQ#@-dQVXciN!HM|kz+m&1I7 zM9MJ7HI*WaqqfW-DPeUO2Muo4I)|Bu^K3Hp#|B!@yFpHDYuY-?i$i1Vue8zcByb<3 zN_|L#j}j#(4BkwnSOxh0@vQxLR<#;l5xA=nEjbq5@mM7#fgo+U#(M_JJO=F!FM6D7e8IY}U^T1@?Uw0b-myOxa z(qt{qv%5Y(`SZIQ!{2-N;r`uer3cO!nHM>n)=1`2Y?dNd38r-&5(KPcz}k>#sjb0j zS%XN}JXJFS?KX9F=-BN$=I;w3?n}_eDiLRcM3P}7?6`8wBPFQapVYt7p2%hSZV|Za zsd7%;Yskw3LP`o%WHQsnSz{T@O!E$3DTF-q1gsDJEc{tJ_1B15ZA_L3r40jMB>tk- zIxluoQ0jpg38IV$i4Rm>W z!=aKiOC|BJ7?4Pkdhht8smtn}+7G}BK_4nfKerBzx7w8w;?}e=pZM#IbvTlOZYGK{ zZTXOx51$;*DtJZUo^O?Nv1A^rXpJKT{s3ms+PoV@5X}J;f|PThNv&IE#xPR}5h?QW zmQf1=!OK|3PuGT!iIQrRt2N_5-ZD0G5CtLiaNlsu?B8E68~cz5P7uwn{W>!g0r`Bk znd5NZFNT@?(@MKHi&SoV;D3}z>}!j3ZJ2z`I(%?ewQZ*4iFG_27i!~h+CEsx6rWU@ zy&C7Z(Z(2fMc}SS)C*vLejUx_BU)8!Ypd4qXV(OvgmE0sl^}mSM+gDv&t2z;2p*Kg zF)&|^xonO0wtx;--sNFv;4SS(S z(ZXHN_o1F#ILMv48J0OQdtmRuJsY@yvDqaJs*PXyKj{aNVMX zUBEiW@DDTvfm`D)R6V_~R>x-+F<3wiz_!L3zqLvT=euVbA~YvuQ+duzqwU;!nPDb9 ze7bIpYZ>f(6z#nGt-bqn{zbcgR`Ere7tz8!&+pVfFCrYz7en1SDfKc~7g@R`uu*3d zK#&1odlu&)cqK{@fxuuUBKD`jtpDJmPo=Lk5c?_qX83of?1 z=-PP`QLoT7as=)Rvv$jY>Up1@RS&oGeiuA<0pJK1FBnLDz{NJ))ru~84fkE!f#8DY zVek4T2QApsxxndPMH?{z^TkvD`uklV`T-UT)4BU$BV3VUlzN@&ca40-O$F|Wl6y+k zUzxk32;3Jdx<;4G=Y)-VT;fH=mmOn{z`a1wU7!s|mnbpM&*^98p6I$)abb`zi_hLm=>E^|?^=m%xUWWkB3%F!zJ~8kF=&YJoqO@L7_r7S z+-1awiWcq*vUOa zqD#F_Q3NakcU7VoWJQ-66@iQDh@$0-8NYGFAu3w3sOVCoqN0_Hii(Pgii(Pgii(Pg zii(Pgii(Pgii(Pgii(Pgii(Pgii(Pgii(Pgii(Pgii(Pgilv{fTNLM?M&PbO6emYV z;I2y)XXnQU6Mv2fAt-+L2w!|?i$yQk=W+z^*AqDUG6oG1zAqne^c?S{=Kumn_+Cul z=$h}LYjml6jR0{3?!{hsm%f0XnY-s#3=v1*UJTIaQb1l_d|m**>hRH*7&JQPXVDc~ zK)!_a;wy%aBXHN%=IV!ydY0_Igf+Y@O#Rni5+;toEf-zty5+Ei?ZPWBcY*B}pA#zX zakbp}j+cfm0(S|}daM;a130nd;fgE%nYji9WZd zCDtRn-}7MTl5UjG16ZF0{B?y+y~wb2t=J;amRPv1rE@*N^{B@sE(eVke1~vf4@l#!(JEM4=Bf#X!_e~^o(<{ z2)-q-iLSNldFba|UflJ-wh*w+M$dqTpAqzM&+m@o(nme&fWtlDQIF;8!uIL8Tmpi- z7<56R3u_NtUF+$VK*CEw>bJuDRbOk1`rcn~%zf^&d%kSzz^6WgFUWOZa2I}X-Fucp z#vQ0#=X*R0MA!Y@^nrF+tJeej3kw`wX5`?afOVtP4s6{mJjl%!M>;g(ssC7XE+fAGkIO1THR99v zJICA~v|QZ1cCYOl61)xzsh(Gxtv4F&k3P`>YY}i_fm;pGb=q@t*$!}Bmq_R7wCw;U zIv}|PE|k!Pue){Bana(_`}-r>i$AX}j9*@t+7Fvp09*&Ge)qY|@^xG*I-mvkF5p_Z z3tIQJ4jdiey72Y4BYVDEuSL6r@!;jJU)=hs=WgBl(~Cq0Kq7(wND@E{CT5^x!8*Wo zTx$ooPP@r2g|6Si@gmT=mS_oZZb5{~OJH%AfUFmy%UHt~*}_p@ADbWw06Av%$3jVw zT1J{GF_J z!|XEx2kMDVJ67mY!3Do~F17zJPITWFf&}>;h|)}n^y!6jmJ$YS+}hfFy{+rCZFOrL zszTAKbKErA9*+xIOltkmpKJE&Kqoo@0|7yDK>%T&LZ|`Qj@9bx*mcYHOEz@cdmRWn zATJ-ypq`H7aNg6d0f{&rdiHu`_=&Ma1v?9A)`b587;k?YO7Z?)c9yQ|yo z?y=iF-QyW7+QMTD#vWi~B#=fz7%ZWAKtf^>LIQ~w%=^3`86;jtLSm$0+-3}J58X|# zwRP8i_j_~C*dxxu$)Fmwu5RhlGL%|G=_oSdM&>`P zcdhxf5SS`j)i!5xuIlH`o__Aj&p-DEw(EUoeqrI|ifgauX+-0!=-MV*v`l*5GK@Kc z5}OcksUVN3y%|RyPlt8HUdwBa1NJz0Plz3lDk`3o_#87Yb_{glD165yOGnP1WTWIM zsM^y8?sx!AL*rGtCv`L-He=LM3Z_tsO!uvdea2zeCu!8d_E94;OGSl##)ni2|BvPte0R({YWcbRxdCYNXx8yMZ$H8{e5*j%s z20a#%his9Nful~p}4Km#6KLSAX zW&yAs!^bqNOeu9-V}W@DcKR96rh^{`+A)BYQg=F4o+3m(k^*5@%OS70`YiAb(1O?BbL3{j}B{)6r zgX?qK`};HPn*V#VO_$~~%?Dn^%$35!t^Ls(s|(GGzx?3-AB`q){27G+0T^R+MuV~> zr6NKq+Ud+*?hX6r4AaEM`HhuIt-euntajb6(b@J)^XLEV7fmh&md>vH-9$%@t*h%l z*uK8?UKS?(61I{yDBH1}c6;VKN}&@)(NBiM;SB)l2@Rn66GWiA{)A*j9Mh00pVV{r zMd&VJPoWcpEVU}NL0G7^Z=W?8ZECmx~GdzjUug*4JU0oNc#$tJ@#0x~}{6EX&@JQf8&1 zemVyNcC_|)Y*O*$r8)V;Mf#Hq#pM4B;EuxPSlXv%{JH|1FfI5b;UgWVEvAKx3`V_X z#rld=DhZO}zQ0~cmS1fATh6kpR+G>?6`0(~5FSR5qEt07gQCcb@=b@5(2j<2m_maC z+n~_kz$6rY#UPU?MV92o=~Hv$-qz6EIUF4jz%UrC8U{V32(6eE!<)O*y0G?g>y`Qi z;|gav8*Qbiu{PpKf*=TCSr!%+m%tdqhaY~3Fbq-i>$rFL2({$}6!(W-+i#xgb-M|G z!>O0L74NSFVdR990F_F`2*OYaAvOUBN{Fc$)*@<66DC6`#g!`Iz}e-wl`Cgf{$_6+ zd{}Uyj#-YwF}A6WNjBuktxCry5noVCJz3RU%ODvS*z8B~+Dx?#AYE#@=a^Qwxw*M( z8nmwyk>AO}@!PJ&$in5?KNc)IJvfN4d%u8)6=n*GG%gUv@st$gT%VU5N&?!TVOs>H zLh5q zU4@oD`P^@8U~_v9AKd&H8c>8$1k1M29}VGIHliqo9HdA`A&NAGNo|)>IwPf=7g9bP zjmH1g^D5u;sudb1Nv|mK`x-0>z^0nWv!_r04KCy>S)NV8F#2cOa^KgdjSHt%{<`4e zd!2g4)yF@XUtHEeN+z?!)5or5(3)Wyu4$OmrgFSL82#+w-e8Xlk$(yrql9?2X+|06 zgg`YbHX8(KA(fJ!e`VqLm5-+h9Qi-ZNGn=$(n2T=+pLCep8)30Fu8Z;)Y_R-t4sg# zoeys@LRI1V)ernRjLbG(AMWHxCIzDX0=97T2}!IdO2DN6G6ljI2%%7r@N5FdG2v=ftxmwB2#7>>si{kW$Tl20wtbG_w;U-`|g-2>Vi3>PUSe6>Ki)n#{4wVum#WNI zEXJs{K3lyE1wjbmbo%bF*@7evv%0d`8tQcTlZV!u9pl z>Z#S`Z!9e>y=GAIbv?fqYPFg#6$V+BjWq~_QOZoqY-I(1O6DM8@3m3R#$+ovI`0c6(&~%sH}g=`tCI5vHL6V+MA*17u*P2Jz)86R(S+=vgLR z)i7T>zj5&|EUle>G0pfr23Zq|7R`!r0l-1ku@Gg&v6g#ee6g3dj!sr52&SXue`gpa zP=p96$>~}DTj6*(E($JdewAj0#dVx>==k23cqTwUU82vh$MY=78T*+Ap=w)pgNuU1 zQQRd2zJ^5X^|F9cdI+WZgphH?wORn2AQ7jmrUTQ)g1=}eT4kijxhbHKiyVS;2%!L} zz)T90ni)^ke%MR453Qa{o}$!i)cI;n!+xJ!I*Y$r^*n5CZplYGyZjp`7X4Ze@{z34EzH9!RCi#B`p_fIG3>u!{vkJLbXUR^U^V3qPSxQMmN;OpruS^J~N`+GQ2vIk=r953qdBItB&N!Cn zgo6acR3wTrBtcOsnaG6R6EGN(2o^GB@GAOpUH3aX!3ucuUUr06(u4M#$^amsWucbSQex_-tBG z)n?niW4sg#`AHrLWz+!MH{mbX$oF!$2#fcULR{B?QD(4Dm{OZ2V`r5r)4H)`k_NHa zu(s^2)Yk29xiiL`Qo@J^DY6kq7I7qD0VO3s2`KasW+4JOi3V9O&-j6oNFLY0#sEf2 zVRHAu!^7Wt`NiK&?` z38{k8f?DLzuG!7%l6|4NWNnxY(yDY!mPZBp4OB2CJ=-GK&eetsScomN>=p_GAu!4Rdp|=yW;|yZ~eY-55}UyRa~iJc+S)*n>kAEQ4UIUpMDD;v`u) z94Dy)A-ZsWnNpHVsro{xOvAPxXT^TTWvG-80HCc@0z#%aA!SAm&{Fwn*pcx>3+}mEwi_59Dl}aH6Ov{2)3W!3?*bEzIFQC`!L1%+l zTV25L@Bs2~ACMB^(7>QpgXcKN4GKyI*7=Td&^ngmD}`q%MP(3X{ig3N+g`oXcJTvVWdA{>=q;S zP;vS2)Y8fewMJ!C)>6L^q9&yrCVAdX@+?ntjbTwloby!3^21ziCwyNCEjX9Ic>CS! z?OOE}YL1Z=JmZ{ONs<~xmLg9xI2x4u4>utQL9U_t~a@84@kNRVV;l?uExhC>7ncK6U54$#qn zeX_F;WqV-S$`oaAEekcrMI1$-N@8boTMm1J_jn>w#+dIK#(KS0HJdZ5b{0-*QIssD zX%3SS5Jr_qb7fM9s1Q@ji8H}RIp@PDj&1NFn&tT^=g}dHdZDZeKjJ_Lb|O zJow~iKmSE=@xsR0s%^F;7f>i`cqLGSAO@)$*x0zB;mYvn(I#~9SI46381;K4aIWjZ z@jML26QpU1!C;6aNx&GzNL%Ih&d&7uGTD%jNRtGvWq=Y=)(kDjhUeFy6hHu*5V(Yt zD+(%z5_ft%rBn}QaVcI^O6@C!4?ffGBs|-+GcFAs=XK&-3`|CA`Z%5O?YdUDQC2{l zplDy*_9WY6QdMh!vAAtbP^gX5LC zxzd*GM_VuqR>GE!hUgbLJbfI)5L!_Z0Mcv(`^DjJ$LJ{ajtm-&BO?JA$l(>|r>tagyZI*Z~1A_y3>)^>vSr z;AD;ZuKA-3h2aKOL2itd`GIo#sEI5#i$G6%!sSe_ZyeB3aI3X;AuO%V*_kO}%Lv-k z2&E4MmY0eQAIKp@e*N&|ElxzbjYiM@^|D}rAx=}j$DQ4NE>!U0#r=|DI>r)mYi~}? z=1g)%2O=lRFrszJG%R@DW#FGcIC%d)$RmeUbZ1{MQUkXtaB z^z{vDwy_Zq&FSp?JVV4a6*>FUH8hlME5JM&s?#a1IL0$0+p1%l4P2bNCxXGIwoi)x z6w|u7K?(kpX*c+-F4a`e@`6gT@-g+$uUqgk&$sotS`Fc>`)0T>v$q$CyduQE0Dw7w zC{(xI-C>S0L=B0u+~imICb_ZCiVKuSw)3o{QOg8#%VNM(X&cF#ggc`Q4DR*71(7N- z)rM#`DJxAMVEC!jwyCM0!1+1Dm0K&*@yn%_rP#;)^1ig3siKt?-6# zD_)rkR7`i8m7eQwF?jw_E#`v3xg`a|e79lz41p{LC=QzdhedXIx`G53JG%yE=0-|v zfffVk;D1x7b-aNq6^IkuGX?gc1aL5Hiu7fYIh&}J7+8Wpz(kA4`YmHZb4!n_1hgeP zbP8-&WCA^v3lbuS!aRrQ> zU%^j8T|u|F7pIQb4s5|+`rpTyQiO-{o(EK&egD}t=2 zwJkh6+~%IPg!;cKs9#YeIeI%_oPzqoS9P$Tz|2bzJ({3$@Nxfy3;jusD^!+he39A69?Lohdm(V%ZW2E&wqdBU4OeF5|I&G)--O~FKx>Zr%d zM^O2mS->T#u}LhH=l(jN0O7}T*oJTLtByO%S4@6m5G)Vl(jNI?_}7Ws?JK9-Uupc( z!NGXQwtVUcQ0_Kp-oK*Ne`5$Ii6by9sxXaLK5S(TXd95!WA*O-JL)q9&C7gcj0gdD zb$OgniasCvttv`G`Fv4hI$-7P>}UsV_18mjAX*do*`KKQU%aTFGF$t8HB1k@-w;UF zFj(qd@~A)nJaE_9XAU$zRoS(lkwy`gxe0V~%JboW8e7S6py-DkZFNkKKAW1d+>E4{ z($ght!9~o__7e;G=eYQJl4EWw=i!*-%OPTO6INT`6mk-@=BFzt8v{?HW|e@=mZrHn z%hzMp5(P3B^wGlvduD=!Js?$lk4Ep3ZDN<&@gDg^aDWSYfSFz!=IE|_55|r!R0+Uj z|9F&xc%7F!X1d%rNOq_nx+r*Wd5rWUduN|i@Q%ArB$i##J|9^TVfHhvnF{~B;$JrG z`#@(|x5&s*eM%p)VpuEQTFe>+e1LkmS$F>Z9MW}yw zn}s%G)h|{PY^=s9z7#70(WP%sW7Le=LRiF~iO-Te{XSr~6Q97dxb`S^jb_*a(H(g@T?VZ@i zo)TM+JjF2gBWuN1EyY!XSU-pZV^X=o`xy#3}3tuj3 z<6?n?tryzg&zJ3+V&Mc8z9x)95jVKrtvbxOGboW78X8^+tC6kaC(z25$cMXRiq$SU zlp6>ol{)p-PSeEj_;g!E*p!;sG%%%c1i4g1efwd-hHuH0aJ9?-yJ=^q=Bx^arbUYd zZoo2xB~<+oA1=pScGlf#zHg~PXYX3;O$FN@PRbig{N$Yd^bOq!%m>v2zjHplv8p5K z^>4yEDC0x-%wDhNyK=a)b|OG0r;by~xsLgD{%$q%tq6}&6DLmd(fuRsC)arfSn&4c zLqN%Cv-4%EuLoK82Ufej3AO(SG8SDnb-|c2+!KrhaQ`pAct}80KqQ(NP4H5qb|x_LwQZ z7)?tUAMi8s-xnH~wxuIYA~@nv7kxeR-Ym0k`A z7Z*SfVCP?sfZC%kHd_#yrJ|(ASts^SB1;mA(A$%6@`J4 zjKkbn`1#uy3qb5SefvJ|aP+o+;%bl$g`YA`v5+w*s~$2E-i8o&{dVgqlZ2pOAXQD_ zA)26(){GWTIn??7%EpdfR7WZz{hK6$kKg~bDVjBx1J8^bA}~|^-V5F|3lEAmU@-6SwWfq77^^l*&N|-l~`5N7dw||p>r%}6@HTH$m+|a5s88yD&4o=;7#!>d`EV1y0&0j*> z4Ov8tEU?!@b)x!Yh+|rIu@+xUv1VP<+nx$Hv0OE!!x2rJ;kJnp(>a3~QghfcP zXV~u7Le)bCEe`QL5fjp6s*r0rDp=auFUS8VZr&fYEH~v*I%vDN3C&wtv4ivf&T<$y zB(#5~XVYys*q27=m>jJ&j(ovq-5;nOaF^iUp!#b&QWaO(3% z>h?v`#=CRJof#a_}b_s%=FW#J_KeNY1J@p9bdW6UA}Ts*2NNQavG4iX6~oL1{- zf4c_-0Wj4TBx!!+E!^UIX50i+YSPW!UET`7g**odPT4?d;ttYD($*91uq8I9O$&^j zpKec-I`ggg#b`IexzF7VmN2=r3h10`6>uD*ZSuz2m|Y6;jN3mSIBJF|bG56j0s@Wp z&B~j3aw6Vf#Uu0=Ey`%T;!u_i!&}r$T;N&_7Xt+m6ka80wgkv15NAN_a^)6q%A0G{ z10Mz<{=w!#E-KzABWtaNTwpZJp#M;A86y38Ifq?*US9`xo(8Uu;D|BJ@}m)yLK45S z(E4UxqH2AwXVY_WL1}9HiUU@$`c3wEykpha7skUCw`;R~5F+<`)DQf(arduF05r2U zH{Wv4u)3gQL7_#8U02PJqwt@hhP)^Sf(sP*;Vey%vmFgK$SKAxQ^q}$5jBIJ-(}Vp zhMo}NvI&8)B#nE$*Xwq#-XDGF9rtZ;N)k(^eQXY<`OEm4-b7D;8`*kR<6e031*QGE zT?vSX1He!#*higldCeNtcsXM}+gXVpg z3kH+rGI?G zSq1P(1zv6ax*T*&V!tfyt5B;$qfn4U9CW^F#+`tjQk~urwzN@J`L{Ka$o|gu@QME$M01(j3xNrcAPn#Na*bWLB3dCbpda}x9gE(e| zu@F7#L%mVZ|Fpq#L5rNV!YO6a-*D${IdaDadQOTCbi=9*m}i3Dl0~HtQKG_J&6pP) zqjv3ZUckBCXyZbj((?IWMI^#51QG=s=ZtisrJoTY?;B-l9&!_RKW|^5xcH_A?ucPu zh8)%6Kor`x`if&jIZH`D*_Uh$Vl)b<%}&G>DcW+vL;EV=5sUub6j@BrCu@HlO`W+u z(bo!nL4RirBCs8i{X)Uc=_iO$!Gm9MK1`JMclqKkZa)I3ODOo-_G9lwaP0nOw~8&M z_US#y{pbSJc`531#Xsre@p;L#_1K_>Y%DQ|4O}=nuAsB4MUttYn6GOblJ#8ZgZ7VK z@~76cuxtre%j8LtEc!fj!QkGA`K^YXR5(>(4wC!2;bqLdJ4vikeJ2_=f`Sm*HO(QR zfN8#{IsDeqI>TJpmrJ1#F+uSX6ROEq=W!et?A3r z2&8$`uI1bIx!W#NsF6!MKh`sJfHK2B&HixYCT#4WU9Xn^zUd1EhRUJEKeNGCy>Ai~ z$}bRkehe8u4D)o}HXtg7=({bIB822D0((qkcOZ9!{m*yjeM&UB3Rqa{yWN`O_&6Z| zOO}q2(#Kpb-oqVyl%oLV#YJm^x4sG%C^$gWu>rll9KP+;uCn}@lo>fd0j<9IzcVZ* z$zGHsHuvdjF-#bCDkMTKBR!kj;k`!S%%FkL?bPO-#wvhPQxPdun!Uid1}6UjbnEDE zHkA1x76yEVpB^F@mCqoDNJM4^{woulpoSR%VWdzuISb}5^4vc>6iNEHRB#Otq@+J; z+pVu|twz#el74XNAT+?OARJHV%;M#$Ok<3z@qaU@>Ik*^2Ws(SCl2UzGT>ODp-2 zK%+sfT5j4c4DDa`-&mp1^ir?42#$mm7;cN^2KA8d696G!72O~673sR!`tOF{4HQ*0 z#r>u^nYHXu-yeBtG%|$eDill?p6i)sK{_o=y@fhcWPfun?5uyma*_{}aB z!(Kud6!*4)3&Pci8`D7As$=MHy95n@wV>f%wAhnCkmn_)^x~hoSB#kc)55-6RpMrx zehhUo1=+%?1|s>mF=H*`WwMFDZ|3WUH-?sHcQobFqZ(K9fZQd8Z33deAvt*XmH*7 z{$Q0gCsUG5ZKCo}Nx`00HnXElooeM?8n09u-+->DOQ}(ow}O(~)Nxo9Bx=wY(obSJ zgz?+!6;n#zxE|0bxhn@~-x(D2OKWG)X_8+jgqrDN9DQP>5zUGVi4s3+3zsaHoKDAtN1Ug_K8OiqiSIWI< z%8I3TK#*oOVKiaHh@KvnS+TiaE7wNMQ?(hAC3!Q_j$L`k`wD;zTi#m0T5^PV5n}4c zQ?-WlmR?6UO4l13H){IRi%0UAIz7wcMU(LdeUUYG>j%i)%(hg4PjLVd_FnYaBD^Rl ziswEor_ksp=HKH|*9*GQO1-7}MuVdzPnJ3!Nn{Gxb(|e(Xhz|3N|nG8ZQDfqNQ@3K zX{PeBRQpWo{j~;*6gn^S%7mc2xZdCn?j?}WB_q_9eeOkbyA8X0!`dmtMXhwYSbb%T z8`yIN5KmB{Iq^%ag2Xi_<9~Q`wLaWIO#N?@S6;M`+;+z^@cO(?<_;Rq&yAl=^cze) z3K~sT)&dd<+Br5;EyRbEE>}@B4-#%i#K7}!XfpnOHg5r;mVVs`l$IhfRkp5q3J3O? zAGMT9^sjDafcZt>H_L=`-7SEVamW@5&TtTVXati zG~T*89>w|{c{4%NKmRo|PxPsinJdmwKTCw0hwhFbj6!8D`B{Nk7L22riGF+Y`mI7p z`RjKh;U=hYR}f+3upD&ss-TusHXpXkh1XElIbhw@^1v#lR^v@v#+x};jo^;+syYKC z=g_KJ&72MeWn+l6qHUcqzGZd&HyR*6X9*gXA`p|ftH%BUW7Z#Vfgte^uT^dkdppwA zIbHvfGb4x^l8joEIYLo|zqmwsm7L+0O%#q_v1=_ptPnItnS+A24Ogq(3Gf1d(U9@JvKZmnd%TQ``;9_x3A?gy?g5*@T z9QAFx9EDn>kMkuez8C>q2pBar3HD(t8kAow zsK*j+TkS6&hE0M2ZWt||yd5ErO5VnsZ=q8;UU zN{Y4u3hg)`TBJUup_)c(wB<|s%Ju~-m%oG+r_WP;!fAJGKh1EYsMG5DOg^*l~OYy&AqtR zip!hqIcJ$uj$`t@3|91Y+iA`b%9|8Y%6Py>jT6Q2B@9?t>HE%7QJPy5VM|73t*gEA zutHM&IMLGj(KY@rcquiguWFJcEO_d?uy24t*AUE3P{FB|j?TC%|y7&Bl@N&kNZ8v10v z1kU_Iy*Q+>wMx$WnSD~0-oy`mSU2H01jE1NO6MZx&~lQYrS5cA_@+{)o&Vrlg1c^p z`cHMeL#ajDo`q&YIt`2R@OHdH)UNxY1IJ@u#HQZ!0W!5|dzhg^i8zh^mBw6TyISx8 z8~5=)rHwnq{CvZg<;szFS42T|#WRCZXeA6Cy8H?TYkj1Nf$ton-}?p}1>;*u0@~@- zVU}t9cL|v#MD&^89V+aZ)E2($d(#nTve_hUA8qeObZ1+B-~Ww6S$O@_4e9_fW$chl zzgpY>LWt&q%SmT9MqbtC;3-lGXZbGw3u~)wrzXMlIH3JPNe#2BJ4t{sWHw?oe#;u7 z!xrt{Pb1F8h&k#{nx!qw77`Sp>-c1T2yh_UrKOylRx33q*T+PD%Y+fe&c@V1rW7al z@)2*6TW)KP;3@n&vy{lDt7O8K^_C^)wxa;ZAf}-Day%qQ%XY1e>(o^FX@&PdqhN`tPF5nWK(wk=HIMpP5WiQ0D;)OyGDC zfji_VG&;P&aQ}RWSgVvwF}pf={=A+M+Z*nU3>%keT5Lkk-i7SzWodIG$7-@W5w$*P zDzrA7x)a6Fw{5M^j5naT(|gg^AN;%>5jdLo#v(;07xCANWR5$wI!iyK9L04|-|7iK z!p!^$erWMxh@&)lr#Slmvv?RKS2I`U3jECT*dX144!Jhgy^s|_lW7|dzwgS$?lh9e z^@*29qy`L@ke+e){a!U$nM_>!(>O}ZKfrzb`Uhw2h6ltjbb0%|3%EcG5j$?nw%(q`D_Kc_r$i0Zf+A>22yZbs5?5kj=S>oWhYy6#dCJS}{;O1;wm2bul4})D(B`(=87^A(46i}Bh0-_cn zCEjdQG~`Xt5PnG(E!;*X{<^Xv9P*5T`^e3|?9L^+v5a+|A&#mc*qZx+2)TCk@cO(6 zp^6P@R`}iGpVd~B$W0KyxvE`YV;*js3BxL{U75szh3@{EuL(3i9VPrBTFA@7vtxLf zZBcXy%|QL>y-vygko1Ixtr4`@*re0JQ)qe#$|g%^xWLCZW91C-C9ZM<#?K=8gqwXv%j zkVft1IiDnFU|B|e+MyzoIy3H3390eh4$rJkP5WkC^F*0M5_!3H)tA0>fR$G7Bwh*d z_epIMkC^mBzt$=rKtT^%sJ5$Zsjp3qwNn{z%PrrXQ#h@wUi(BMc!gT zD8#G9dN(eHD$E7w=r4mE_U8e{nPTXy5|s!!TsG6urF_8Hcy5`c&R;~~h5awA-YU0T zm*@H7_x1v3B7n;2#`gD%0TE&5T+SL-Ql&LmR55b19jy_O z!a9{fUU!Hn`ID8tT|qkZbm5Hsn@rkV$PBcthZQ1oh&4b)U^pGF5k9DNeqa09zdrM9 zv*>w3J}YmXa5)E}Ufobmo#l1oI9+p8Up83$a-fptM>ub94B0uJcBiM{Xa9&HWXhx@OeP1lF&5v=PGA8&68ftSIjdEJKh3v9%QS~(-6fS3(3#LhXzGfpXJZ&g|6wFh zA+Ary2b$c9*z^>X97HS>EvclpcM_mYg8c*N%3AKD$t8-?)U#Mg5v6Hf2gAx(d>=9^ z!!ok9vZYYjM2VST*WocoQ;JG}v>O4~Bv%se2=^dK_twxL!B4U}1HvH?_B|Z@dm5Wv z)7L#ecka(Wrj92WP_%}c)K=e59`G4V7$tpe@*aSljXlo=?}&-muR9aeIZt8az?&0H z_T%*O~%|)n-KAMt(Yv3w?bpKJGiOM{Je9@eQbkN@Z&hd%@z2(-{ur7tJtZZdA8a> zyY4l6zuxgXMJT%|V=7cQd~Zp@C)vED+6iLQZjv!6y>zSN*rhcWNp7tO!vgbHq!b%w znmDkPhs!2x2B!7J+^#2}(}0Z>cK*eLk%H~wq9=$qk8WzMRzgB$p10pdR;JLcb^GYi z>mmnyZ4ZbnYLM4Tqy6r0^M>Azb?Joqm1jJ4R5SKt9{*#z9rvsQWPqJdRzQBH zWO~6LI9=XwuO%=Str#7i7Z)E6mqvd3yh;2fiz;Z|Ue@(ELe2Z|`JXwKz`hwdB5>bg zKUFPV?S~5%`2+cXVw zjU0^4)_(nd7^|sBWa+S-_ko{{hOncde_S*34F!cI+=bZZD96(yCMFZgW@K$hG4$S6 zK@=DfP5Q?<`)50E!~g^HAp#U4D1aDVjny2}d5rAwsCDVx+gq3sGD4z1nk*&A#~OcX z;oSH0m-ZCvp_4z6^Gtm8tMk&z3?BSDZU$q%FMDQwa-Fpkadbe*Oq{pu^VdjMPKOgv z^kp#n*7YWJM(>$ykPm_c6FQTH?qB_*B0_BdQgI&j2tkl9C#nJu)-6|mFJ91zK-ij~ zAbHeR);18kh@ksFnc8dnzps~3j}XmTA*3IK;6BPj5vA8Q#)Wm2`63z5f#S-d9I7-( z6r!7}?y?6}DuH&E!-bDzw>`rv(kR_1TgdSMVXMp9q1YsjW0Z6bW8p`J2stB|AOpyKfYv*=(Km>BUO@pUXll|FI8Y1tio?CSFE}(Eo*lyi zR3?N}h>qH$SKhVSZ;BEnw~6FT8Fuyg&InnF*Ws+=bN zt>v&`8|KoJkxmLkM_De`ANB7$FO|fW9rD{DV;^#>#spfy5Z}~hDSKEEMnp`ubmUV4|eed&3&{AERh_mjK|U+WMQPm%vNjW#%In*GD*%$$-VCiREkw zVEiGPHN~C<(;t?P%Hqh7AK#wpB8$MJ=FmQm zwX)6kr$!T&-WG}*mXB z(kU4&?JDC!TVaYrUiL_Yee0Ot&AR)FH+||GsDx6tjDgl~#Z;`FwR?C5S!B*%4s0ij zWRc82Nf%Rq(1)6a*U-Ei(&O>j>-FmXtJ=~hs^Mu1P^a9{(Kzk|T+2!K*e0ybBducc zVa9YX`8&fis;8K1I1a`13F6i3frm+%-6Z}dhQ`zaSJcev0-OiNkuh<6e3#d+cXIsS zU%gyi`&+l?kjQ{Td1*g=4@j;`6`Ts5m&q3DMK&1k8r(ci$qE5dL~w9zOFX^T59}Ua z4OUI*FGL6pFdXVf40Z$S7oacmFCB~nN;EzUI5uSJYg+~+i**JtzNvH7Li&~2Yzm>T zf-H?(D{uwbB2ohl9f>eQ@fO=?Y?jF?SO#2y@ssW+Fus|G@-vTmxKX1sA`tH-#v( z{>t=d+TBj$BjrC4yrCt*%6xwjF_Z7gouvD99&>)WbtC|^4e7g?V2Na>-3x<~MUEM4 zF$$>A5AphGr@|&YVuaIJ^-mvQ0qZe%0w;sG6P2IPz*XbTlD1lRjH|E!O&{YgG=5hU z9vT1#CrN*oX%L{H8WI;jVzN>{uqn7`v8l-~vkou0uekzk_~2!8V^YS!7mtT^qMp@v z&yBW2t5tn6&J*O(8c}~FuNojLgT4DWt%-^EMZiR><}wLrL}!D^Njc_CdD$|U7%tDx zqeu_4fF+HkN}CRM3AYjNLgh*v$%JbBU^Gzi$Dz4Y6Ub6*K3gIeKn8>P9aII3H~Ek2 z?NmKv^*3!O-b}0q)v1T@%4jBBIIN?_G4Ix{)p5cg-u9DfmnQS;HIKV&l4HE(SPw$) zHz=&TpuUsHMNO?J^OHSek|8QLt1ZS2{RkbX5=YI6Xk}%n0RK=WA=3C1o;H(|_&~SV zl0U}HfiV&M7-q6epGH(#qog!}W7pk?STw`9UQ#v7J()$q!Q*jTj_3oWv&SjbeFu?F z!ic0{TY8Sj&@V!KNFgDA~w4vI0guz{H_1(9CXH zdsv99u`_WZ7y=x0QCJ!7cKB`rJ*PlCsK^i&;H+zBEMbFabm*_pSUVp}sc$z+{Pwm( zkp0lu8ohxmFH3z1#hobG?eUq8KbMo3SL{$srt`mjR=;^*hk!3VZBIOWllNLD=n>oZowa44D4prqZM@bHJ8=N)jsiiI8l#f>8 zS3CV6FZMv(ncF^R`O;9O1OvZix!D|C z($nCK893kcnMSd-zI`DT&PM>xqAmO}8_mO)m{=z6k5Y1UIiG`k*Eor&jbhtH7|Y8Y z+fZc&Ls*Kx)K9xklr6*N8QH?_lzVrh7@weNtD@|VP%qSen3S6Y@1!nj8+BC}rGK3U zV+_~M@c;*!pa$Co)=z`kdSb(Af3Tq*IuW#W{iD_DcM!h1%Ebd+ol+>`S+4H(VY^U! zoCFD*L<@~UQ$S}uYVt1JJ@Q71AijV2%fA)ZlbXNn@#MnV?3lRV9U0=Tu}3c~qbqNP zY=gL5^Qd}S`2QOphquuAzfoOkZTbIBU*|jgjKRJOFIk8L;BypYRHYjv&BFc%s`^pt literal 0 HcmV?d00001 diff --git a/cybersyn/graphics/combinator/hr-cybernetic-combinator-shadow.png b/cybersyn/graphics/combinator/hr-cybernetic-combinator-shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..cd5d3e04252ca6a3a183154299a65676969cf218 GIT binary patch literal 7923 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
;1ZK6n5^w$a1$6wIEB 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�F-*7eYi7C0>(mmco>*j~Art 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
* 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 QJ^%m!07*qoM6N<$f&c|K%m4rY literal 0 HcmV?d00001 diff --git a/cybersyn/graphics/icons/combinator.png b/cybersyn/graphics/icons/combinator.png deleted file mode 100644 index 3f4233236012a0c3d07b99909145f407a013676f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2532 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

PzGi*U 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 B^nL&U literal 0 HcmV?d00001 diff --git a/cybersyn/graphics/icons/locked-slots.png b/cybersyn/graphics/icons/locked-slots.png index 3f4233236012a0c3d07b99909145f407a013676f..b8766f81f7bf14091f6e8f48dc440e690732a010 100644 GIT binary patch literal 11063 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 literal 2532 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 yESKYB-5@%wf{EG2uho^PS580000CF delta 1165 zcmV;81akYpTA&G#BYyw{XF*Lt006O%3;baP000D6Nkl(}oGD|GD4o0&In+!=lFfn^Wg@B3kQW`FNJTgzw}#(xhuZ_7ZckncqBaYl0q zpNhOk;2+-8Yt2Ex0R$wduzL8u2r%etWdSt`1k_kHd@ceC$+cQ|2Xa$|0D7;(N&*TH zP|0|!gm)p2Gzg&2xU3*x4gwmPWA^Y~1Gm89xBMk z6Al=c8xJWdDIv+tPJkCjBcvphna@9L^2T4Ltet$~ve}E!NnmT+U@>!{X-rd?W1$6I{tfohx zxf7dGz<+uKlro@SB2z$CW|G=QX?;_LPeba}6q`4w9aF&YoCLK3>Zq2c5U@w8D4t%R zV3hvs%eg?EF`B#Pcp^WZO0*Xcd=2JVdcZP`DyCOwPDE>FR5BVT+(o0)h6iyL) zSGYwBpsc<>1*}CtBxg57D;BVQu}8E3VohWCj>r$u6FxZ5ipYPXYD{oW0y?x!h(shbS#c>Dnu9;NkA6z6M<$1WzK566J(POq`nyhuMcL; fwzZ6wVfzh;W@+mgBaJu!0000 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 diff --git a/cybersyn/graphics/icons/nonempty-train.png b/cybersyn/graphics/icons/nonempty-train.png index 3f4233236012a0c3d07b99909145f407a013676f..7255cad6bf98ec076656de41b4a9ea190ed3a341 100644 GIT binary patch literal 10471 zcmVYm?P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3;eb{sjdWdE^>UP537mV@z}w}W1OUjvd!QbS2q z{U}kHOh#bv!Q9LdGVA~T&o=*uuk1oFA?A{5N)KP5hUyw`%5%T&t9`=x_k4xdulV`f z=jQzj$48!5e}7Etb$#RW_<2Kq-C+Co&rKe$ams5T`yTjwdCjc%^R>v=K;GTY?dxY# zUpKV$Wjn8LH(tZO2LJKrkTJ%LjOU$k%Ul1Pf8CwQ>)ZLpCzgL7AGwfgc<+2ZpU0o= zXWpj!g#q91ez4#3u`>T@jyurDw$cxFdVOx=51;$@H16Aqtb5PwzOKjzeDn3|i~aVV zW$$%&*FrLwE4?=Lv6b_V3mGK%b6et1<~#B}pFfp9Z^K^cw)?v+UgPpe7 zZP$4ncCf_Ymdm_4H=IA86`x&o!^;Ihf9!VI`0A%G_+#KX&&Te)7JKHcnvcl}Gf&Hv z&N7%ip8fL2{P@9t{qt?0y%pxxKILsI*5wpmEJL2tzg&ew?0(ra9{XBzU!EVo3M;O(@+zyYwu!av zci3^Kop;%Fx3||`Uj6R1A3gWaUb8Q+x#K8Zmp{G6$2ortVFf2qKEq=!SUjHP0Rq~| zXHT&qIC)M!d#dxsgIlaJ$~n`@V|Xwu>*BU=-+g-Sw|+BN_uu->{mOGjuKVBcoRRCk zcJdk=%;8rVMeiiZGq@o@!z|VSMwJaGL3)P zX0Spc_4+Ecm^(!aKD(mHWsj)kTkBX*k89$S?m{1!2!0m&ticAiUpHCsBQ?DVQPgR9 zOb(3fl~jcpNVD{?`q}0h<4JC>R>7{BHGZBRKYJi1#n>vJ0dV@Bb@SvL@Y{+Srv5Ib#0)ert9&( z>a(8zH5hq^&oR^U97FN?Eqvb5WBz0K{P-G;;)yqJGcltK^{)fUyd&!WGoZ{50fd=V zABxRD7e2!+P!L==D+Zui?iE$IyH9DI^_P`BNJ#C!E`Q(cKLaH3IOK z0jmuy@RsYM5R3WAwNkS{&ZY|7j?kvCv!^|%&XnxLa?h}RR7dR|pAnk*^*b`nZ$kAQ zvMi|(o?=-Kz(z>y<&xk0?M{VrOAz{D2IxpcpLq7RP#D|He0Ym0Z$7K)DYOT%RaCpj zO&>+oZIjYCRn?9K0a}%d?eLK+HzW_bwIE?ze>=DBshjK$STip6*Z6(%gTR?`<+O)US)>08aIyK(Ewan3jBPR#DZrCw1BIWF1eAj+GEc zh?UmLS~-rHaoZ+}DEy`0`Z~Hl^kVke%?T)Mu6QRy^vB~yE<*SI2X5B)Bm>0Svl-M5-^x0+EEL5A;33)&)a=~0mwTwL5iVEiiY7q^iT2gNB-vdO`#Sz|)3snpB{hrsMu;*^zP zq!(Zj#139X9dlc%vYro7XsxU*{QeS`h5UjX$<75U;T{M;-{W zocnV42HoF)6w({xR4mLGzMqr z=1CI&P4c}&+jq%UP5QlH>tb+e|06KIanW30{G@P76YinHuMIP=!Y-m*f5B^TMz@Lh zV*c$YO&QigZ8h^yUvfL6M}J3tzr056OW?L{kZ##nod`G(+Ld62VKqrx(cDg)5J>6< z)dqy|nMgLDk8hg)>h+^>&DUJ0H&%pObW6#$Vr2bLHTQ@?BcBf z$rJ3ox)V#?GsPD91A0cl#U7&d)UcE>)D7ST?qZ2$Oi1{7cBX;~w-bA?B69~JGlR|x zk7v*`PzS2WYk&!&LrP4FI#Ncc>%%6^LCAh87^8VV5_O^0umeRQlC+l4N{_j15~bIL zAOS=WcIEYeTcjh9eH@X7dQyy35qpTcnwCJ-3R97yL>=Jowe_uFSFF~vUhGJH01^ET z0j2}hsoVrUL4V-Igo3g=119hW1F#dr0>@u=!-v@ej^ z4ndsK?H{$&4+K(k01I|ugWyJi6}*ggB3NZicmzvLYq4W0j3o~6=2CGmpVa}|E~lmv zuDUn3Q-$;KIMxC7s$y9(gryR)=o-IbQiIZnI2K@f5d?|)uK*Z$Tm@*VR>c+-ozYRv zCsY80byH|g(juA2S%K6xSQO&3SRi2nNWc*QaP3=Fq=#|rPQBzRBE4?We+suEm37Ka zk(E}{0nH4QO-$*nZAcW@Cb8nh5J+57qri9&v&0i{9N1hG7EQIpWWgeqY}UEyk=}&? zYaI&*av$LjJtG)Ky9#nz6-)2^DFjTQm1>5;LaA#(U2up$Zn9oiI1SR~wlW~+Ud|{ZI+4V^BttdS0l%W0}a6xoMrp7bD5g8qh7|F~)>QZOA zFHegYVo{E;GVfo_Lqre?ij)U3LVet8tZ zSWs{Sh-EGfIGEr>LIQL!4e&@b4$;+NBG4=lo3w(j0QU$DVBnD3J|*c9-&}$GZRs3E z5RMyciKp}$EFliI9?!)T81)M9<`M}Vs!Gd?(gCC?gb9RGW9{i!061Btfci&Tnc34> z`F>zq8{|jh`7p)D)c6QlCdQq2G-@qu&a%7&^(F0BKEyYJM?$s(Y@;+)7?BzoEt9P2 zu9n0n7YA{G8Ju4y>ob^x)hT;e7!8VixEQ4KDE}V99f)q%HK7dh8;96Cu$EGA;0YXNVtFylyjNrVw z@^o=H;Wr>d9cjX2UBn8b;92-cA%Z!;DzeJM_wr*@e}eBeFgT?{6#ob#xb~9P;aio! zs#Fk!ZPm8cLl_kx#vh(Zf~09wh}2#fw$D=^!bg2zKq;|(sdJyLJk-(YGgS?6VU(SP zD(ZLIWLO(D5VsQjv4TBY3x&dxFohrmBE{WFK0l5rH&;#w^|_KbwYiikV1uB+FUo^$ zvX01XO0NtSqg6xi#Y>s^M~D(YVKFp>GgvJ-JK-ef5H59xOS6s{De1j79FLsXQ9a^) z+kJEK{zvU?ZugdgHhzapBcSH|L5bneAhW|mN3k;MqZAp_uMmS??KhCfZ;gb0RU zA+=6F6%BcNiRmOZvJN5b;F|i?MEI?6K!HRA6p}-bHV}>QCv8Qn9<}YfXj7Av)qxZI z;q-Ty&!&@YK&?!RI;OxRR&0d~qz~9CRt;#uj1MSvO+)CpeKx;h&$rvqk1F0sh}4p! zG*L8x3jQu^;g8hMQWt2#Q_yQatvf3Q?$oUIkg0=DRi(=G!(k>08=;@2AidNFxO0e> zKb#Fk;2$4g-FiBOnsp;eD4gz9OG!&!qPI5{qQKP%WBJXkUkC=|IBRi-{vRz^T!Fii&Bqsvfx0AfE$M;#}?&R?a zBzLTVn8qNYCKEHPXvG#LmR+gAK?8{uxxQ>|G-I+ZskofMWC$TG8- zkrXGse9aaFNW?Ue3FA8@QdJJGq1=cQ)Do0_y^1zuO{_$*zY~lNV4I>0>OGt;ygCEu zIw{%WDRcRN_0SPmLvw~lP6peHfQDc@fGMs%P{Pd}|J zesqWFWyR`?=YecG>NaXz;k|P3^tDWo6kU&v*08KyXPGa-o95|~d7Uy8xOpA{021#| z|LAfWgvU`-T9G*5P&(jh2-(Vq{3jLQ*ju~u3OuRO)Dn^_zsM-XP%Z4(NY2x!6Cq%k zSFeiO)VExHe5+k#=(Jdb;?wqXnxzj&d|?GCw_~+Fj(hU4)E_=NYu-bODxd`a%56Vs zd=&x;q-{llNfy+J0I>?L!H7fGp~DrX;X{~47keai)M(1O8keLNNzHC{*Hbv|5FMN` z-!w-(iKv`VvsqYvIS$j*!Z!OEo@k+9wLTt1O*p)T9ct39XFk?*yTGTP2;skR4CafY z`O^#47@txC(rdiBToZ^T2yQP_NOU_VU4^;y$|^}fS715nNWynoXI!-+)G7;@%9e93-zV$yx=-NJ2NY6knW5Cj@3*LiOdul!(-M={YXV zQXB8XQnhKqml1$A-F|L1Nln#V^T&Cs_=`XHtEfwwfXelB)J53{HizR$AixkRzjdt9 zajl6ZkW4eFWdSnPdxQw?R#0^RbpA`(qXa%PIN_nRfR4xTv}uQCkqqH1Y@D(+PVaiX zs2;+FdS}l;Js$ml!EF&F#iz0Y)ynjBTe(SNOogCuMNRIjzluuvm5$XnH9o~Ck6kZd z9lv%-2)4?mww|?Om?{2KrLmky(>a|`N4%;;suzb1F9kW^lP>Q9k5taoM5UOJ?M^vf zLPcZMC#v_}*KLz}6{LIgaWtTcSQ79=*%yA)4v^nYgH9dM>I`75Xw+pTr>V>x`Si5Z zzp<2BU}3@Ugd)>Fr^mKNYpEyaDz-s^QDk(k3%6aCq9lo@X0Gt~9Mlrwhog}i`0#WJ zPGkH#N2NwuiGJ}j$n@n~dM;YT!K)J}xqPS?0nikwu+_Pu5&516&Du)h(o$iF^>Dli zn-(D^dIe&(ja3WEO_~dwYU&Mk)HvZ_<{@IRo`&$%sZ&+epw6jL_nfMOSdf-y*Hm^X z?mRxu31823E#fF0!6vB>z3pn}p7x@~+w-*j*p|9vUHL~Gwj6BMqpLb(L_%6*T7If= zICm8I%cGLHlfBeGzHnp3x;{Y;evCcsJ1Wp?uq$Fug!0q-A)q4@l+~<2?{z15l6FX;^uB%fVjZbpPGB%Rg6z>r`@3*`N1`q{)F>T z++mL}eYdCxK##X-o)0#6&;bKL;niYr-H2sPPfDF< zgOM&^OXZgS6rz$4CnPufy-yp#S|C7M{hyoUtGWO}C2zJ49Si6WDWojI-jNE#U_CeT zeuRZ6_3En1KNuNu9G@~Q{AWRynjm}`xb{I8riZi)c?5Ez_~=aTSqE&J!$Dzy3bAN;V)7o zs!ya-TI|!*+!F&``PluPw_nIfvQ}Dn>~XgqmbxZ=8gPIJUOKuXW&QMOk*Ry&ha(O< z2&_YYRo|iRYL9Wna0p8-0C{8p5CrN61EbnLuBzLET>Rs)pAzo#aVyn-J#J+Vd${$# zeyjhl$F1HBfLxDXAJ}@nKeA=s&uqOJ$a#?9%|QAq(^arfrU%7{7(`H_Lk|g~Ub;C9 z!s-DNJ^JuZ3G>gd=DczGpAZ=7`A<*6o>%jdt>xqCI>o}*r|WwE{&XE-{C>JFd_1S9u-3H=PzoQg+`%yOTwgBHrz*paC9_hdxW zjq*+GLH*Bz5a(RPi@e?rpq|)^B<@lN16FnZ3l1cLx*?`$UkUML68ozu9vx<>I$zoW z9vp$Yi_v5Gq+k@zVgX$%VNNvap?Fn3;08oF5v3CrC9R_Bb05G(r{}CM>gnHa z8!><9lRvw}{GCt!>=N^LKKZjt%-{J$k3YXKB*!l(EX>4Tx0C=2zkv&MmKpe$iQ;VWi2P=p;WT;LSL`58>ibb$c+6t{Ym|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfbaGO3krMxx6k5c1aNLh~_a1le0HI!Hs@X9CsG4OY zV^J}aT@^cD(Src`FbKcIOg)}jOv7`0-NVP%y9m$nKKJM7RdOZ+d?N82(+!JwgLr1s z(mC%FhgeZkh|h_~4Z0xlBiCh@-#8Z>7Ik9zor9e;vcGPz1% z00006VoOIv0RI3v057vlpS1u0010qNS#tmY zE+YT{E+YYWr9XB6000McNliru<_iT58xe)yRek^f4S-2RK~#9!?VEXUR9AM!f3KI< z-I7{rZLM9c4O$3cVX-3+7!V3Jwn>V z1+LV@v4@F`H+e$_jcK73Fd!k&uGX%Wy46yvTfM#f@mdyUmMxlsPcPj&4T%b00o&zKcyMfKu(R6dD?cEask_83u z1(1K8w_E@Ugd&1>3j@A85AcNAe&?=WfMh`fTn5tTd21K2R48KdPG`XW8vsw}p8a<; z10)LupaqD%!7he@9H9uuoyvfxZvs4_d!N1|86a6m0$KsxO?EXAVnN@X#(-Ze0C<3~ zP+xZr10)Mefkr^NKwSoaJfVoocM1buSU~WE`g-AxV1Q&{El_=n9J@{^qULsIz^xQB zzr3wOK(g>Bz}8#nfcrwvxcx#PBzR5(1-E!_@da|_D0sUu;ITyjPv{ws-Bt{cEQr7* zAY+l#Wd~puityep4EUQx1W)K$Ke#OzAX(4@Z9v?d?Iig#C_yj?fgtz;;PruLQvO|~ z0<{V>YWY*C1cd;?)kkRqvV+25kIwVP;zfsGqBl9ZH4` z1_;;6A)?^_IUzhWhW(4q0LelQaPewRl%(kxpYXu&C|vA-|9m%&^`-mSwfiAHC@Z6` zs*3v`c!1bw11&AhtlqE@+pvx56Mx|Q`}Y&@d1dTNHc;yOb&?|Oz?$-Qp;j9 z;NZ;rC21PG*9T)0&^HVx&%$pj^7!{(A7XgS$uEBXbHa5x9^SKu@rend;*G41Hxdj6 zP%4y&`fy%2aDcRw6iRo0hl&b`?aIR_l@Kn-Ot@m^)Suu6CD01g}skI ziFw6x{^gZd=xlDLc*_=Y@41JBw?vH}54-wCgFd zl>(EJzWPm*aFr!KjOiCv2n7-Q9vvsZJ#FC`1dgd(K*R9ddvo_~UUzz?sy=J$ik z4J}sq?dK)z{`OPk7Zfl&WWzo>%9`S0P{8u#dE}VQ)YYHi^^lX)fg$26Lremi4jkgb zf-@i_cn*H0SQnIcbjQ!HqJPj!UvCd}wYB(MF1GL5g-@x(XEG6=nugmwK}@uP{w@Va zkNhvK7cP)nP{6wT?&G~9N2ot@29;8YXL6FFO`A|C6r8TBBO)T4mdnlLnsZ5tHxLzc z%oV!_B@1s|uTYwg82UbtJ@aBP6#qRoEh#A}w6wRga_t%(-nWmkvNDdp|2|r8fSOZP z3^_(=zSxMh`4YXI9o)NZ8|k@be)sz8)SW(!TBSmx(U6dkfXD43LLW{b7-V?ZMvf^B zg+j@R$_qq_XHd<~-R#g<7fuaI79v7BuB&HK0am1U&}Xv|6&XolLL&E-l<>@R&(hd% zp0?&@veVLV4i3?L@e4c)kGT%q^713Z!pks_6$8;R+i;1 zCEsi&!@LZ&0tWgA@c80p6G&g~P(x#0Fa|sc7_VBd4$AAkWO#_A#6*@XHPhQ=(_Jq*fE^FJtQS0;Pv_N1LfuA^!4?j7DbFkBasFJqr*0wqYg~znIy)= zvvJibI=Z^~_>)g?x!vfr8jLY|RC5Qf5%}4HI6$%x4?H#7?_MAD*tgI-szVe-!gM;U zU7b{Xc8u2#9|qu|y?b#woqY1?F&^Bzmz?~3E;KeWJUmQvY%E*1?_k2^Vq$a@kTAr? zva__5h{$l>eCr*WJ3C2FPbVYIM6r2T9di(AaM}2|fq2(@BzGd@_u#_AcxW zJ5llRXcY>&TAGn0iPv8{jHuC~kB`S%S4XAA0t&DV4WZL$PzegMao6-hF)oVZLW4f3Bpt@ zOpMt%Qr?eFBa*j#IYUE(^mq3HQ1;Qsbar$g2?838h-bn>VnPBz3A)-^5k)o8u`#44 zCbD|ndQP6I;$&qdv2k%^q@S-Myph^ zy?8SpeC$F2JWx_fM`tJRm6b6vI7miX8U}-b*49=WE*FJDf8NCnqC{B3eCwRXk-MJ$HS!G&$!dcsn0*BWO+Z)`e{jgo%X0? zVdae^!GD;KRtN+^6}Oq8zCQetM7_mAXKM>-85tB77IL!UIHuHOmYU6UcXgrBXh=y- zWoU4Sw~oF|OFMCl_C5KybtWMyX)rqdA!1~4Y2 zurw>1$?*v~tyVHkCZhCu`uh9v2ZM}zz5K-vCz;6*p_?v`&BIDVn6B-Bs|lVt-7Cmp z5T=3rOf{SD2`4Evh0OdsGM6spLPHa^XKH!u@ozCaJW5-0D?3U{85$hqt)oYYG8o7) znF#p2^!N5+jE$wfriR+uS_TFOal2gXTqCgeK8b`F(1>$ZiszK@A#8K60?$9ep94R> zeu+4(2(`*ChNb)2u&J27?rus-cF@+|%81=gcTX>)Halz9t-)@$({TPg!62yoKI+e& z#o>07o}NyaR*N4P8z0AJv+-xeetz@_EJ*`Hgq&|~&;iLn!+}FI&bkUn7F5jh9A3I2 z2N3lG%mt~u`m0y5JI1hfTPZGDOIDVd7ytQX{1Xl|IuU(L95F^CVWJwVwVNTQ6TR1q zORvZ0_hYl!aEy%b!#{;bc0gt_MC)$~{FME@WZ`Y02>)!$;P-%>c^63}xd7VM>%p|l zgefxzZEpu#x2!`Y#&W)~0l(Lc$0t!)Q-@M5qE@R=0r7f0g28|%7^LCyWdtB4M&e(8 z3~TcsIbJS}i)uO4gqu6cSrWqdA77~mNfyFGHU7l;^f*-w8nnZIm9NJYzK+3x0W^*` zP{!<{XGBGHZ9PBP(@TU7{-^R@+O31A)oO%b5Rbz_n8Con&=8YeFOTgAvTvtER->?`q+ zmnqM7v|e83G~)D;n81R9pE4(qEfis!a==T#Pj2{-M4HY6{($_qAWVN(DdqU}`e9@g zM#kh)npPuc_6Qw_qU^M(wt{d=fM01Z2}S%&kSv&xUuG6>w#T3}eF}V~W2S&ek{kwJ zzr1`X6!J`~l+y>GivavfB@ZlB0naW3_^Etwt2k=4Q8HG?p1+X2cg6Tc-U2Ogk*6kte df3$D3{U2GfFY2@tPCx(v002ovPDHLkV1jW;_p$&0 delta 2523 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 diff --git a/cybersyn/graphics/icons/priority.png b/cybersyn/graphics/icons/priority.png index 3f4233236012a0c3d07b99909145f407a013676f..e8099ccc145244bed793058c9c5e8494ab4cce6b 100644 GIT binary patch literal 8243 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 Y0$uHABszVwaR2}S07*qoM6N<$f-!DgdjJ3c literal 2532 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 literal 2532 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 literal 0 HcmV?d00001 diff --git a/dev/provide-threshold.xcf b/dev/provide-threshold.xcf new file mode 100644 index 0000000000000000000000000000000000000000..d2ef6d1def1976a72283733ffdec3be9ed74a71b GIT binary patch literal 48314 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 literal 0 HcmV?d00001 diff --git a/dev/signal_X.png b/dev/signal_X.png new file mode 100644 index 0000000000000000000000000000000000000000..b2571840beae0458682e959b0a3d388dfb4611dd GIT binary patch literal 4222 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 literal 0 HcmV?d00001 diff --git a/dev/signal_yellow.png b/dev/signal_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..97ff46b4b4bc61c15627d11a9dad673035200e95 GIT binary patch literal 2121 zcmV-P2)6f$P); z1nO%mq^J_2ph}h6R01K90&20S6nKJy5Pbk4-ue(lrJ^NDXp1p86em#IvEyCa`_H>G zbMHCKoLv_%Uav78ul9O=(zPGf$7j!c`<;95+>x!2AuvM$mprA;nsoDA@`1BfDKP$x z>FJk7#>dC86XxgVaq{E?MZ`49ub#A=`%)MhrO|`{lHWdjwCs=Pq4ddHK1+cYf-d zvr|_dnmGCK$5Cp)^E48yB^(+W#O(AHec%OVW);J?UjOP~{@7=~=H-UZdD_nt5uo-e z383|1ynF0eSiE&Pk&~SOSf;@YBjI`JT$RU(C1ali+tb(zIyed&w!a5IPvwdvGFHMg;kJkh^4{U zGsrRkA4kW=pPDP5Pz;<;axWL4YSr7p(0Y}FV4YPAwt<_UNQp^e!=QvPD0pEIKq=+5 zeg&U%!Gu9oE2ULB z?}@lvv{a9Cy*P;r51xLc`uIZtlT~c%ifkRaLvCUMTdk59l!1C97I=LLVLgIU8+m|G z=j64v0x$jj*PQD+3NJSZt@Ax8u;&k8W%0Kh1uHmq{BsIzxN!~Y`1`NtNALNhFPDd{ zLQ0TX_g4$o^GQy$u(lD8by!me@1R9nt0X4DBn}P>HVll_3IZ`~nDb%UIrzrZ_t^6f z!OstaDMA#i$_48sl=2dRr3SJMsp;PPzOtoo1-Wp{U?Ue$r6e!2e34uQG^tboW9qPG zUB-?CHWDMT#p0T}_xNE?v-R}9EbJ-rL=yU_(ia#9n)2;U_CAHB!)u&_qir;+t=m{(GDB$5+10 ze*OrQ?vqb`&%+V4*N?a+sqOwxJin!UmIy>q>JBE-l52ws0Am_3Q8gY7XstR3%rL%9 zVkGuk-+yZwz!PYLk4@A{GZ-E@g}#ANfC(d=f99yj9GuL} z@d$t&2uy|~1{G|kv%srs6&$JDz@ftva0MoVdgX@Hwj~9CiNGAmcc_#FCX$?@ZyB&r zJy~RlXw01jw$@5arMwG)iDU&T!y%*|B5X-fRNOP zAuuxoG6;?-EVZ?R$i^d#2d6Bsl3bhbf<$4RO%$+Fy5n9t@~p({Lf}n8A0VVfQxp$Q zw?&hZTpP^X0xz$W``$ZoR4GMDE5>#Zvl<+TFcrrnvAb*0AV7vYFsN;dE^Mr#Qx{kq zEV&A3GB=0S-~E2!nQ}RJ)YCa4kYa{vcOasn*;1tc^q1muN$hS~w6k+CGq+Wkt%*kf zvPiW>vz1(uvjso zLr6W;-oPLtu#LoFNZG+8Ejk@Kw`jQvBrVM?NNQ^bTLN*Ex`T`AWXXkM*IIOI4u*SkFjHH7!Di#IL8W}7gC$q7 zdo7wK$ZJq}crMwTOX0Gnf!Qg{3 z2Mc7eZw@AlF0Z1=!8=)Gw_QbBVvy7gFj7JUZstNa_rVNyuSH8u+npvsfg@N8XpJ-w zadUn%S63YjcJHtOOl=)J=8f{3e_jheaH3>}?>M5vAc9hA&sXvN{W&g|)@+h1$vJ~% z{J&&}4IK^}9)9?vxr-OC){Y)OHu=hHb5DNwLlK6DhxT?ZW?v9S^+p(0YBU+= zx&VMBcXu3Y+;gzh#BKZqyzs(O4Z!&`XGWi?&)?F%H^SIMj4i5x@lm>bdAYhd_wLD_ z$=cB~SuNC4XW%_80;F_$_trAv9RAanVF7U07FhxlT%$wV<@mxIq|Bxq*&-{2MV9+? zZqe+ZzziZ}0=LeB9ccbS6)aj%1`Xdn}~c__{VcJPS~ zYqJ(DFdTd)3!FK4!@*1rKC(kLbMOYl?#)@m$Q=C6KA0SQ@SU=Ku)xf)K2 z3B=-J=NwFgY#-ceWzOEJWXc@;j)R#wb8xnb&Q{Sq+oHjgwP>eXG_n@$YK!h^2WNj4 zY6rI}i5#4R3E8d6gyL$1>FZUDj@(AR|6Oo_uu+hj;@UESOXUc&|6q*WRlcWMpJy>^uJia Date: Sat, 5 Nov 2022 12:07:14 -0400 Subject: [PATCH 23/66] moved todo --- cybersyn/TODO => TODO | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename cybersyn/TODO => TODO (77%) diff --git a/cybersyn/TODO b/TODO similarity index 77% rename from cybersyn/TODO rename to TODO index cc65589..13befd8 100644 --- a/cybersyn/TODO +++ b/TODO @@ -1,6 +1,7 @@ close gui when the combinator is destroyed do not play close sound when a different gui is opened -support space elevator do hardcore testing models & art move signal subgroup +add miniloader compat +space elevator compat From 2dff20756e5c3501885aa8759abc41ffb35d8c8f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 6 Nov 2022 00:54:36 -0400 Subject: [PATCH 24/66] added miniloader compat --- TODO | 3 +-- cybersyn/info.json | 3 ++- cybersyn/scripts/layout.lua | 21 +++++++++++++++------ cybersyn/scripts/main.lua | 13 +++++-------- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/TODO b/TODO index 13befd8..cc20c47 100644 --- a/TODO +++ b/TODO @@ -2,6 +2,5 @@ close gui when the combinator is destroyed do not play close sound when a different gui is opened do hardcore testing models & art -move signal subgroup -add miniloader compat space elevator compat +railloader compat diff --git a/cybersyn/info.json b/cybersyn/info.json index f74152d..e61ffda 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -6,6 +6,7 @@ "factorio_version": "1.1", "dependencies": [ "base", - "flib >= 0.6.0" + "flib >= 0.6.0", + "? miniloader" ] } diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 51b070e..d9f3dba 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -507,12 +507,21 @@ end function update_station_from_inserter(map_data, inserter, forbidden_entity) local surface = inserter.surface - local rail = surface.find_entity("straight-rail", inserter.pickup_position) - if rail then - update_station_from_rail(map_data, rail, forbidden_entity) + --NOTE: we don't use find_entity solely for miniloader compat + local rails = surface.find_entities_filtered({ + type = "straight-rail", + position = inserter.pickup_position, + radius = 1, + }) + if rails[1] then + update_station_from_rail(map_data, rails[1], forbidden_entity) end - rail = surface.find_entity("straight-rail", inserter.drop_position) - if rail then - update_station_from_rail(map_data, rail, forbidden_entity) + rails = surface.find_entities_filtered({ + type = "straight-rail", + position = inserter.drop_position, + radius = 1, + }) + if rails[1] then + update_station_from_rail(map_data, rails[1], forbidden_entity) end end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index df6d9c9..0a0c9f8 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -195,7 +195,7 @@ local function on_combinator_built(map_data, comb) if cur_entity.name == "train-stop" then --NOTE: if there are multiple stops we take the later one stop = cur_entity - elseif cur_entity.name == "straight-rail" then + elseif cur_entity.type == "straight-rail" then rail = cur_entity end end @@ -719,23 +719,20 @@ end local filter_built = { - {filter = "type", type = "train-stop"}, - {filter = "type", type = "arithmetic-combinator"}, + {filter = "name", name = "train-stop"}, + {filter = "name", name = COMBINATOR_NAME}, {filter = "type", type = "inserter"}, {filter = "type", type = "pump"}, {filter = "type", type = "straight-rail"}, } local filter_broken = { - {filter = "type", type = "train-stop"}, - {filter = "type", type = "arithmetic-combinator"}, + {filter = "name", name = "train-stop"}, + {filter = "name", name = COMBINATOR_NAME}, {filter = "type", type = "inserter"}, {filter = "type", type = "pump"}, {filter = "type", type = "straight-rail"}, {filter = "rolling-stock"}, } -local filter_comb = { - {filter = "type", type = "arithmetic-combinator"}, -} local function main() mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] From 09c34521ef4e44e2be7efa8c1948f832a6ddf17c Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 6 Nov 2022 07:22:34 -0500 Subject: [PATCH 25/66] updated algorithm --- cybersyn/scripts/central-planning.lua | 128 ++++++++++++++------------ 1 file changed, 70 insertions(+), 58 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index d84d696..3b2c802 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -8,6 +8,7 @@ local INF = math.huge local btest = bit32.btest local band = bit32.band local table_remove = table.remove +local table_sort = table.sort local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ---@param stop LuaEntity @@ -471,81 +472,92 @@ end ---@param map_data MapData ---@param mod_settings CybersynModSettings local function tick_dispatch(map_data, mod_settings) - --we do not dispatch more than one train per station per tick + --we do not dispatch more than one train per tick --psuedo-randomize what item (and what station) to check first so if trains available is low they choose orders psuedo-randomly --NOTE: It may be better for performance to update stations one tick at a time rather than all at once, however this does mean more redundant data will be generated and discarded each tick. Once we have a performance test-bed it will probably be worth checking. + --NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic local tick_data = map_data.tick_data local all_r_stations = map_data.economy.all_r_stations local all_p_stations = map_data.economy.all_p_stations local all_names = map_data.economy.all_names local stations = map_data.stations - local size = #all_names - if size > 0 then - if tick_data.start_i == nil then + + local r_stations = tick_data.r_stations + local p_stations = tick_data.p_stations + if not (p_stations and #r_stations > 0 and #p_stations > 0) then + if size == 0 then + map_data.tick_state = STATE_INIT + return true + elseif tick_data.start_i == nil then --semi-randomized starting item tick_data.start_i = 2*(map_data.total_ticks%(size/2)) + 1 tick_data.offset_i = 0 - elseif tick_data.offset_i >= size then - tick_data.start_i = nil - tick_data.offset_i = nil - map_data.tick_state = STATE_INIT - return true end - else - map_data.tick_state = STATE_INIT - return true - end - local name_i = tick_data.start_i + tick_data.offset_i - tick_data.offset_i = tick_data.offset_i + 2 - - local item_network_name = all_names[(name_i - 1)%size + 1] - local signal = all_names[(name_i)%size + 1] - local item_name = signal.name - local item_type = signal.type - local r_stations = all_r_stations[item_network_name] - local p_stations = all_p_stations[item_network_name] - - --NOTE: this is an approximation algorithm for solving the assignment problem (bipartite graph weighted matching), the true solution would be to implement the simplex algorithm but I strongly believe most factorio players would prefer run-time efficiency over perfect train routing logic - if p_stations and #r_stations > 0 and #p_stations > 0 then - table.sort(r_stations, function(a_id, b_id) - local a = stations[a_id] - local b = stations[b_id] - if a.priority ~= b.priority then - return a.priority < b.priority - else - return a.last_delivery_tick > b.last_delivery_tick + while true do + if tick_data.offset_i >= size then + tick_data.start_i = nil + tick_data.offset_i = nil + tick_data.r_stations = nil + tick_data.p_stations = nil + tick_data.item_name = nil + tick_data.item_type = nil + map_data.tick_state = STATE_INIT + return true end - end) - repeat - local r_station_id = table_remove(r_stations) + local name_i = tick_data.start_i + tick_data.offset_i + tick_data.offset_i = tick_data.offset_i + 2 - local best = 0 - local best_depot = nil - local best_dist = INF - local highest_prior = -INF - local could_have_been_serviced = false - for j, p_station_id in ipairs(p_stations) do - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type) - local prior = stations[p_station_id].priority - if prior > highest_prior or (prior == highest_prior and d < best_dist) then - if depot then - best = j - best_dist = d - best_depot = depot - highest_prior = prior - elseif d < INF then - could_have_been_serviced = true - best = j + local item_network_name = all_names[(name_i - 1)%size + 1] + local signal = all_names[(name_i)%size + 1] + + r_stations = all_r_stations[item_network_name] + p_stations = all_p_stations[item_network_name] + if p_stations and #r_stations > 0 and #p_stations > 0 then + tick_data.r_stations = r_stations + tick_data.p_stations = p_stations + tick_data.item_name = signal.name + tick_data.item_type = signal.type + table_sort(r_stations, function(a_id, b_id) + local a = stations[a_id] + local b = stations[b_id] + if a.priority ~= b.priority then + return a.priority < b.priority + else + return a.last_delivery_tick > b.last_delivery_tick end - end + end) + break end - if best_depot then - send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) - elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) + end + end + + local r_station_id = table_remove(r_stations--[[@as uint[] ]]) + + local best = 0 + local best_depot = nil + local best_dist = INF + local highest_prior = -INF + local could_have_been_serviced = false + for j, p_station_id in ipairs(p_stations) do + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, tick_data.item_type) + local prior = stations[p_station_id].priority + if prior > highest_prior or (prior == highest_prior and d < best_dist) then + if depot then + best = j + best_dist = d + best_depot = depot + highest_prior = prior + elseif d < INF then + could_have_been_serviced = true + best = j end - until #r_stations == 0 + end + end + if best_depot then + send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, tick_data.item_name) + elseif could_have_been_serviced then + send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) end return false end From e111e865d68087d61260d3e7a814870cfcdfdca3 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 6 Nov 2022 15:27:22 -0500 Subject: [PATCH 26/66] added filtering --- .vscode/launch.json | 14 ++++++ TODO | 3 ++ cybersyn/scripts/central-planning.lua | 32 ++++++------ cybersyn/scripts/constants.lua | 1 + cybersyn/scripts/global.lua | 1 + cybersyn/scripts/layout.lua | 71 ++++++++++++++++----------- cybersyn/scripts/main.lua | 46 +++++++++++------ 7 files changed, 106 insertions(+), 62 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index cb7c79b..ddec48b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -18,6 +18,20 @@ }, "disableExtraMods": true }, + { + "type": "factoriomod", + "request": "launch", + "name": "Factorio Mod Debug (Modded)", + "modsPath": "C:\\Users\\mmoni\\AppData\\Roaming\\Factorio\\mods", + "manageMod": true, + "adjustMods": { + "debugadapter": true, + "flib": true, + "cybersyn": true, + "creative-mod": true, + }, + "disableExtraMods": false + }, { "type": "factoriomod", "request": "launch", diff --git a/TODO b/TODO index cc20c47..ec7f13b 100644 --- a/TODO +++ b/TODO @@ -4,3 +4,6 @@ do hardcore testing models & art space elevator compat railloader compat +add missing items alert +lost train can be repurposed and rescheduled while alert is active +display when train is coming diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 3b2c802..0bd6991 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -9,6 +9,7 @@ local btest = bit32.btest local band = bit32.band local table_remove = table.remove local table_sort = table.sort +local random = math.random local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ---@param stop LuaEntity @@ -99,8 +100,8 @@ local function set_comb2(map_data, station) local signals = {} for item_name, count in pairs(deliveries) do local i = #signals + 1 - local item_type = game.item_prototypes[item_name].type--NOTE: this is expensive - signals[i] = {index = i, signal = {type = item_type, name = item_name}, count = -count} + local is_fluid = game.item_prototypes[item_name] == nil--NOTE: this is expensive + signals[i] = {index = i, signal = {type = is_fluid and "fluid" or "item", name = item_name}, count = -count} end set_combinator_output(map_data, station.entity_comb2, signals) end @@ -481,23 +482,13 @@ local function tick_dispatch(map_data, mod_settings) local all_p_stations = map_data.economy.all_p_stations local all_names = map_data.economy.all_names local stations = map_data.stations - local size = #all_names local r_stations = tick_data.r_stations local p_stations = tick_data.p_stations if not (p_stations and #r_stations > 0 and #p_stations > 0) then - if size == 0 then - map_data.tick_state = STATE_INIT - return true - elseif tick_data.start_i == nil then - --semi-randomized starting item - tick_data.start_i = 2*(map_data.total_ticks%(size/2)) + 1 - tick_data.offset_i = 0 - end while true do - if tick_data.offset_i >= size then - tick_data.start_i = nil - tick_data.offset_i = nil + local size = #all_names + if size == 0 then tick_data.r_stations = nil tick_data.p_stations = nil tick_data.item_name = nil @@ -505,11 +496,16 @@ local function tick_dispatch(map_data, mod_settings) map_data.tick_state = STATE_INIT return true end - local name_i = tick_data.start_i + tick_data.offset_i - tick_data.offset_i = tick_data.offset_i + 2 - local item_network_name = all_names[(name_i - 1)%size + 1] - local signal = all_names[(name_i)%size + 1] + --randomizing the ordering should only matter if we run out of available trains + local name_i = size <= 2 and 2 or 2*random(size/2) + local item_network_name = all_names[name_i - 1] + local signal = all_names[name_i] + --swap remove + all_names[name_i - 1] = all_names[size - 1] + all_names[name_i] = all_names[size] + all_names[size] = nil + all_names[size - 1] = nil r_stations = all_r_stations[item_network_name] p_stations = all_p_stations[item_network_name] diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 4e15cdd..bdce530 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -15,6 +15,7 @@ COMBINATOR_CLOSE_SOUND = "entity-close/cybersyn-combinator" OPERATION_DEFAULT = "*" OPERATION_PRIMARY_IO = "/" +OPERATION_PRIMARY_IO_ACTIVE = "^" OPERATION_SECONDARY_IO = "%" OPERATION_DEPOT = "+" OPERATION_WAGON_MANIFEST = "-" diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 961980c..6f8ba23 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -53,6 +53,7 @@ ---@field public p_station_id uint ---@field public r_station_id uint ---@field public manifest Manifest +---@field public has_filtered_wagon boolean ---@alias Manifest {}[] ---@alias TrainClass {[uint]: boolean} diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index d9f3dba..543742e 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -2,6 +2,7 @@ local area = require("__flib__.area") local abs = math.abs local floor = math.floor +local ceil = math.ceil local function iterr(a, i) i = i + 1 @@ -123,6 +124,7 @@ function set_p_wagon_combs(map_data, station, train) local ivpairs = is_reversed and irpairs or ipairs for carriage_i, carriage in ivpairs(carriages) do + --NOTE: we are not checking valid ---@type LuaEntity? local comb = station.wagon_combs[carriage_i] if comb and not comb.valid then @@ -137,42 +139,53 @@ function set_p_wagon_combs(map_data, station, train) local signals = {} local inv = carriage.get_inventory(defines.inventory.cargo_wagon) - local item_slots_capacity = #inv - station.locked_slots - while item_slots_capacity > 0 do - local do_inc = false - if item.type == "item" then - local stack_size = game.item_prototypes[item.name].stack_size - local item_slots = math.ceil(item_count/stack_size) - local i = #signals + 1 - if item_slots > item_slots_capacity then - if comb then - signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_slots_capacity*stack_size} + if inv then + local inv_filter_i = 1 + local item_slots_capacity = #inv - station.locked_slots + while item_slots_capacity > 0 do + local do_inc = false + if item.type == "item" then + local stack_size = game.item_prototypes[item.name].stack_size + local item_slots = ceil(item_count/stack_size) + local i = #signals + 1 + local slots_to_filter + if item_slots > item_slots_capacity then + if comb then + signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_slots_capacity*stack_size} + end + item_slots_capacity = 0 + item_count = item_count - item_slots_capacity*stack_size + slots_to_filter = item_slots_capacity + else + if comb then + signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_count} + end + item_slots_capacity = item_slots_capacity - item_slots + do_inc = true + slots_to_filter = item_slots end - item_slots_capacity = 0 - item_count = item_count - item_slots_capacity*stack_size + for j = 1, slots_to_filter do + inv.set_filter(inv_filter_i, item.name) + inv_filter_i = inv_filter_i + 1 + end + train.has_filtered_wagon = true else - if comb then - signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_count} - end - item_slots_capacity = item_slots_capacity - item_slots do_inc = true end - else - do_inc = true - end - if do_inc then - item_i = item_i + 1 - if item_i <= #manifest then - item = manifest[item_i] - item_count = item.count - else - break + if do_inc then + item_i = item_i + 1 + if item_i <= #manifest then + item = manifest[item_i] + item_count = item.count + else + break + end end end - end - if comb then - set_combinator_output(map_data, comb, signals) + if comb then + set_combinator_output(map_data, comb, signals) + end end elseif carriage.type == "fluid-wagon" and fluid_i <= #manifest then local fluid_capacity = carriage.prototype.fluid_capacity diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 0a0c9f8..d29050d 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -562,6 +562,20 @@ end local function on_train_leaves_station(map_data, train) if train.manifest then if train.status == STATUS_P then + if train.has_filtered_wagon then + train.has_filtered_wagon = false + for carriage_i, carriage in ipairs(train.entity.carriages) do + if carriage.type == "cargo-wagon" then + local inv = carriage.get_inventory(defines.inventory.cargo_wagon) + if inv and inv.is_filtered() then + ---@type uint + for i = 1, #inv do + inv.set_filter(i, nil) + end + end + end + end + end train.status = STATUS_P_TO_R local station = map_data.stations[train.p_station_id] remove_manifest(map_data, station, train.manifest, 1) @@ -663,21 +677,23 @@ end local function on_train_changed(event) local train_e = event.train local train = global.trains[train_e.id] - if train_e.state == defines.train_state.wait_station then - local stop = train_e.station - if stop and stop.valid and stop.name == "train-stop" then - if global.stations[stop.unit_number] then - on_train_arrives_buffer(global, stop, train) - else - local depot = global.depots[stop.unit_number] - if depot then - on_train_arrives_depot(global, depot, train_e) + if train_e.valid then + if train_e.state == defines.train_state.wait_station then + local stop = train_e.station + if stop and stop.valid and stop.name == "train-stop" then + if global.stations[stop.unit_number] then + on_train_arrives_buffer(global, stop, train) + else + local depot = global.depots[stop.unit_number] + if depot then + on_train_arrives_depot(global, depot, train_e) + end end end - end - elseif event.old_state == defines.train_state.wait_station then - if train then - on_train_leaves_station(global, train) + elseif event.old_state == defines.train_state.wait_station then + if train then + on_train_leaves_station(global, train) + end end end end @@ -739,7 +755,7 @@ local function main() mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value--[[@as int]] mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] - --NOTE: I have no idea if this correctly registers all events once in all situations + --NOTE: There is a concern that it is possible to build or destroy important entities without one of these events being triggered, in which case the mod will have undefined behavior flib_event.register(defines.events.on_built_entity, on_built, filter_built) flib_event.register(defines.events.on_robot_built_entity, on_built, filter_built) flib_event.register({defines.events.script_raised_built, defines.events.script_raised_revive, defines.events.on_entity_cloned}, on_built) @@ -754,7 +770,7 @@ local function main() flib_event.register(defines.events.on_entity_settings_pasted, on_paste) local nth_tick = math.ceil(60/mod_settings.tps); - flib_event.on_nth_tick(nth_tick, function(event) + flib_event.on_nth_tick(nth_tick, function() tick(global, mod_settings) end) From 729f21a5bb345cd44df9a7ca6ec860af73c6925e Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 6 Nov 2022 21:11:45 -0500 Subject: [PATCH 27/66] added active delivery icon --- cybersyn/scripts/central-planning.lua | 22 ++++++-- cybersyn/scripts/gui.lua | 20 ++++--- cybersyn/scripts/main.lua | 75 +++++++++++++++++---------- 3 files changed, 73 insertions(+), 44 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 0bd6991..cf376f6 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -84,13 +84,19 @@ end ---@param comb LuaEntity ---@param signals ConstantCombinatorParameters[]? function set_combinator_output(map_data, comb, signals) - if comb.valid then - local out = map_data.to_output[comb.unit_number] - if out.valid then - out.get_or_create_control_behavior().parameters = signals - end + local out = map_data.to_output[comb.unit_number] + if out.valid then + out.get_or_create_control_behavior().parameters = signals end end +---@param comb LuaEntity +---@param op string +function set_combinator_operation(comb, op) + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = a.parameters + control.operation = op + a.parameters = control +end ---@param map_data MapData ---@param station Station @@ -333,6 +339,12 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p train.entity.schedule = create_manifest_schedule(train.depot_name, p_station.entity_stop, r_station.entity_stop, manifest) set_comb2(map_data, p_station) set_comb2(map_data, r_station) + if p_station.entity_comb1.valid then + set_combinator_operation(p_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + end + if r_station.entity_comb1.valid then + set_combinator_operation(r_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + end end diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index a31938b..1749b68 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -32,13 +32,14 @@ function gui_opened(comb, player) local rootgui = player.gui.screen local selected_index = 0 local control = comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - if control.operation == OPERATION_PRIMARY_IO then + local op = control.operation + if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE then selected_index = 1 - elseif control.operation == OPERATION_SECONDARY_IO then + elseif op == OPERATION_SECONDARY_IO then selected_index = 2 - elseif control.operation == OPERATION_DEPOT then + elseif op == OPERATION_DEPOT then selected_index = 3 - elseif control.operation == OPERATION_WAGON_MANIFEST then + elseif op == OPERATION_WAGON_MANIFEST then selected_index = 4 end @@ -142,28 +143,26 @@ function register_gui_actions() if not comb or not comb.valid then return end local parent = element.parent.bottom - local a = comb.get_or_create_control_behavior() --[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters if element.selected_index == 1 then - control.operation = OPERATION_PRIMARY_IO + set_combinator_operation(comb, OPERATION_PRIMARY_IO) element.parent["network_label"].visible = true parent["network"].visible = true parent["radiobutton"].visible = true parent["radiolabel"].visible = true elseif element.selected_index == 2 then - control.operation = OPERATION_SECONDARY_IO + set_combinator_operation(comb, OPERATION_SECONDARY_IO) element.parent["network_label"].visible = false parent["network"].visible = false parent["radiobutton"].visible = false parent["radiolabel"].visible = false elseif element.selected_index == 3 then - control.operation = OPERATION_DEPOT + set_combinator_operation(comb, OPERATION_DEPOT) element.parent["network_label"].visible = true parent["network"].visible = true parent["radiobutton"].visible = false parent["radiolabel"].visible = false elseif element.selected_index == 4 then - control.operation = OPERATION_WAGON_MANIFEST + set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) element.parent["network_label"].visible = false parent["network"].visible = false parent["radiobutton"].visible = false @@ -172,7 +171,6 @@ function register_gui_actions() return end - a.parameters = control on_combinator_updated(global, comb) elseif msg[1] == "choose-elem-button" then local element = event.element diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index d29050d..01602d6 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -1,6 +1,28 @@ --By Mami local flib_event = require("__flib__.event") + +---@param map_data MapData +---@param station Station +---@param sign int? +local function set_comb1(map_data, station, manifest, sign) + local comb = station.entity_comb1 + if comb.valid then + if manifest then + local signals = {} + for i, item in ipairs(manifest) do + signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = sign*item.count} + end + set_combinator_output(map_data, comb, signals) + else + if station.deliveries_total == 0 then + set_combinator_operation(comb, OPERATION_PRIMARY_IO) + end + set_combinator_output(map_data, comb, nil) + end + end +end + ---@param map_data MapData ---@param train Train local function on_failed_delivery(map_data, train) @@ -10,7 +32,7 @@ local function on_failed_delivery(map_data, train) local station = map_data.stations[train.p_station_id] remove_manifest(map_data, station, train.manifest, 1) if train.status == STATUS_P then - set_combinator_output(map_data, station.entity_comb1, nil) + set_comb1(map_data, station, nil) unset_wagon_combs(map_data, station) end end @@ -19,7 +41,7 @@ local function on_failed_delivery(map_data, train) local station = map_data.stations[train.r_station_id] remove_manifest(map_data, station, train.manifest, -1) if train.status == STATUS_R then - set_combinator_output(map_data, station.entity_comb1, nil) + set_comb1(map_data, station, nil) unset_wagon_combs(map_data, station) end end @@ -224,16 +246,22 @@ local function on_combinator_built(map_data, comb) local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local control = a.parameters - if control.operation == OPERATION_DEFAULT then - control.operation = OPERATION_PRIMARY_IO + local op = control.operation + if op == OPERATION_DEFAULT then + op = OPERATION_PRIMARY_IO + control.operation = op control.first_signal = NETWORK_SIGNAL_DEFAULT a.parameters = control + elseif op == OPERATION_PRIMARY_IO_ACTIVE then + op = OPERATION_PRIMARY_IO + control.operation = op + a.parameters = control end - if control.operation == OPERATION_WAGON_MANIFEST then + if op == OPERATION_WAGON_MANIFEST then if rail then force_update_station_from_rail(map_data, rail, nil) end - elseif control.operation == OPERATION_DEPOT then + elseif op == OPERATION_DEPOT then if stop then local station = map_data.stations[stop.unit_number] ---@type Depot @@ -244,7 +272,7 @@ local function on_combinator_built(map_data, comb) on_depot_built(map_data, stop, comb, control) end end - elseif control.operation == OPERATION_SECONDARY_IO then + elseif op == OPERATION_SECONDARY_IO then if stop then local station = map_data.stations[stop.unit_number] if station and not station.entity_comb2 then @@ -375,11 +403,12 @@ local function on_stop_built(map_data, stop) if entity.valid and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then map_data.to_stop[entity.unit_number] = stop local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - if control.operation == OPERATION_PRIMARY_IO then + local op = control.operation + if op == OPERATION_PRIMARY_IO then comb1 = entity - elseif control.operation == OPERATION_SECONDARY_IO then + elseif op == OPERATION_SECONDARY_IO then comb2 = entity - elseif control.operation == OPERATION_DEPOT then + elseif op == OPERATION_DEPOT then depot_comb = entity end end @@ -525,25 +554,15 @@ local function on_train_arrives_buffer(map_data, stop, train) if train.status == STATUS_D_TO_P then if train.p_station_id == station_id then train.status = STATUS_P - --change circuit outputs local station = map_data.stations[station_id] - local signals = {} - for i, item in ipairs(train.manifest) do - signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item.count} - end - set_combinator_output(map_data, station.entity_comb1, signals) + set_comb1(map_data, station, train.manifest, 1) set_p_wagon_combs(map_data, station, train) end elseif train.status == STATUS_P_TO_R then if train.r_station_id == station_id then train.status = STATUS_R - --change circuit outputs local station = map_data.stations[station_id] - local signals = {} - for i, item in ipairs(train.manifest) do - signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = -item.count} - end - set_combinator_output(map_data, station.entity_comb1, signals) + set_comb1(map_data, station, train.manifest, -1) set_r_wagon_combs(map_data, station, train) end else @@ -562,6 +581,11 @@ end local function on_train_leaves_station(map_data, train) if train.manifest then if train.status == STATUS_P then + train.status = STATUS_P_TO_R + local station = map_data.stations[train.p_station_id] + remove_manifest(map_data, station, train.manifest, 1) + set_comb1(map_data, station, nil) + unset_wagon_combs(map_data, station) if train.has_filtered_wagon then train.has_filtered_wagon = false for carriage_i, carriage in ipairs(train.entity.carriages) do @@ -576,16 +600,11 @@ local function on_train_leaves_station(map_data, train) end end end - train.status = STATUS_P_TO_R - local station = map_data.stations[train.p_station_id] - remove_manifest(map_data, station, train.manifest, 1) - set_combinator_output(map_data, station.entity_comb1, nil) - unset_wagon_combs(map_data, station) elseif train.status == STATUS_R then train.status = STATUS_R_TO_D local station = map_data.stations[train.r_station_id] remove_manifest(map_data, station, train.manifest, -1) - set_combinator_output(map_data, station.entity_comb1, nil) + set_comb1(map_data, station, nil) unset_wagon_combs(map_data, station) end elseif train.depot then From 5f53c53749e8981986b2e72a326ac4a3e48fec83 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Mon, 7 Nov 2022 08:03:47 -0500 Subject: [PATCH 28/66] rebalanced --- cybersyn/prototypes/tech.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybersyn/prototypes/tech.lua b/cybersyn/prototypes/tech.lua index e787109..5c27fa1 100644 --- a/cybersyn/prototypes/tech.lua +++ b/cybersyn/prototypes/tech.lua @@ -28,7 +28,7 @@ cybersyn_tech = { {"automation-science-pack", 1}, {"logistic-science-pack", 1} }, - count = 400, + count = 250, time = 30 }, order = "c-g-c" From 1a0c9cf52a5945c223ceda3277add3b0a63fe558 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 8 Nov 2022 10:22:34 -0500 Subject: [PATCH 29/66] fixed a crash --- cybersyn/scripts/main.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 01602d6..eacbf12 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -695,13 +695,15 @@ local function on_train_built(event) end local function on_train_changed(event) local train_e = event.train - local train = global.trains[train_e.id] if train_e.valid then + local train = global.trains[train_e.id] if train_e.state == defines.train_state.wait_station then local stop = train_e.station if stop and stop.valid and stop.name == "train-stop" then if global.stations[stop.unit_number] then - on_train_arrives_buffer(global, stop, train) + if train then + on_train_arrives_buffer(global, stop, train) + end else local depot = global.depots[stop.unit_number] if depot then From e6a8d72f85ab2765db47195b2559881c515ece39 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 16:16:47 -0500 Subject: [PATCH 30/66] added capacity sorting --- .vscode/launch.json | 1 - cybersyn/scripts/central-planning.lua | 7 +++++-- cybersyn/scripts/layout.lua | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ddec48b..5b06ae2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,7 +28,6 @@ "debugadapter": true, "flib": true, "cybersyn": true, - "creative-mod": true, }, "disableExtraMods": false }, diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index cf376f6..087e8b3 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -170,6 +170,7 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type) ---@type Depot|nil local best_depot = nil + local best_capacity = 0 local best_dist = INF local valid_train_exists = false @@ -182,9 +183,10 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type) local layout_id = train.layout_id --check cargo capabilities --check layout validity for both stations + local capacity = (is_fluid and train.fluid_capacity) or train.item_slot_capacity if + capacity > 0 and btest(netand, depot.network_flag) and - ((is_fluid and train.fluid_capacity > 0) or (not is_fluid and train.item_slot_capacity > 0)) and (r_station.is_all or r_station.accepted_layouts[layout_id]) and (p_station.is_all or p_station.accepted_layouts[layout_id]) then @@ -194,7 +196,8 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type) local d_to_p_dist = get_stop_dist(depot.entity_stop, p_station.entity_stop) - DEPOT_PRIORITY_MULT*depot.priority local dist = d_to_p_dist - if dist < best_dist then + if capacity > best_capacity or (capacity == best_capacity and dist < best_dist) then + best_capacity = capacity best_dist = dist best_depot = depot end diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 543742e..9a8238e 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -3,6 +3,8 @@ local area = require("__flib__.area") local abs = math.abs local floor = math.floor local ceil = math.ceil +local string_find = string.find +local string_sub = string.sub local function iterr(a, i) i = i + 1 @@ -431,11 +433,11 @@ local function reset_station_layout(map_data, station, forbidden_entity) search_area = area.move(search_area, area_delta) end end - layout_pattern = string.sub(layout_pattern, 1, pattern_length)..STATION_LAYOUT_NA.."*$" + layout_pattern = string_sub(layout_pattern, 1, pattern_length)..STATION_LAYOUT_NA.."*$" station.layout_pattern = layout_pattern local accepted_layouts = station.accepted_layouts for id, layout in pairs(map_data.layouts) do - if string.find(layout, layout_pattern) ~= nil then + if string_find(layout, layout_pattern) ~= nil then accepted_layouts[id] = true else accepted_layouts[id] = nil From 0b370407fa7506cdcf4d16cb7e18edca1e9afc49 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 19:20:29 -0500 Subject: [PATCH 31/66] removed provide threshold --- cybersyn/changelog.txt | 6 + cybersyn/control.lua | 1 + cybersyn/data.lua | 1 - cybersyn/graphics/icons/provide-threshold.png | Bin 16446 -> 0 bytes cybersyn/locale/en/base.cfg | 13 +- cybersyn/prototypes/signal.lua | 9 - cybersyn/scripts/central-planning.lua | 169 ++++++---------- cybersyn/scripts/combinator.lua | 100 ++++++++++ cybersyn/scripts/constants.lua | 2 +- cybersyn/scripts/global.lua | 14 +- cybersyn/scripts/gui.lua | 180 +++++++++++------- cybersyn/scripts/layout.lua | 12 +- cybersyn/scripts/main.lua | 82 ++++---- cybersyn/settings.lua | 11 +- 14 files changed, 335 insertions(+), 265 deletions(-) delete mode 100644 cybersyn/graphics/icons/provide-threshold.png create mode 100644 cybersyn/scripts/combinator.lua diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 37bbf6a..01df86f 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -3,3 +3,9 @@ Version: 0.1.0 Date: 2022-11-04 Features: - Pre-Alpha release +--------------------------------------------------------------------------------------------------- +Version: 0.2.0 +Date: 2022-11-04 + Features: + - Removed provide-threshold + - Added ability to specify station type on a cybernetic combinator diff --git a/cybersyn/control.lua b/cybersyn/control.lua index de63f60..a1bce50 100644 --- a/cybersyn/control.lua +++ b/cybersyn/control.lua @@ -2,6 +2,7 @@ require("scripts.constants") require("scripts.global") +require("scripts.combinator") require("scripts.central-planning") require("scripts.layout") require("scripts.gui") diff --git a/cybersyn/data.lua b/cybersyn/data.lua index 7b95676..5d2a820 100644 --- a/cybersyn/data.lua +++ b/cybersyn/data.lua @@ -16,7 +16,6 @@ data:extend({ cybersyn_tech, subgroup_signal, priority_signal, - p_threshold_signal, r_threshold_signal, locked_slots_signal, missing_train_icon, diff --git a/cybersyn/graphics/icons/provide-threshold.png b/cybersyn/graphics/icons/provide-threshold.png deleted file mode 100644 index d5d937a4e3fd1b22862c56a30751204cf561c69b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16446 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 Y0$uHABszVwaR2}S07*qoM6N<$f-!DgdjJ3c diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 4a39218..4507c8f 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -1,20 +1,18 @@ [mod-setting-name] cybersyn-ticks-per-second=Dispatcher updates per second cybersyn-request-threshold=Default requester threshold -cybersyn-provide-threshold=Default provider threshold cybersyn-network-flag=Default network flags [mod-setting-description] cybersyn-ticks-per-second=How many times per second the dispather should check for new deliveries. Deliveries are made one at a time per update. This value will be rounded up to a divisor of 60. cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. Huge values will prevent stations from taking requests from the network unless an explicit threshold is set. -cybersyn-provide-threshold=The default provide threshold when a provide threshold signal is not given to a station. Huge values will prevent stations from providing to the network unless an explicit threshold is set. cybersyn-network-flag=The default set of networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible network flags to choose from. [item-name] cybersyn-combinator=Cybernetic combinator [item-description] -cybersyn-combinator=Place next to a train stop to add it to the cybersyn train network. This stop can now request or provide items using the circuit network. Be sure to set a threshold signal. +cybersyn-combinator=Place next to a train stop to add it to the cybersyn train network. This stop can now request or provide items that are reported to it by the circuit network. [entity-name] cybersyn-combinator=Cybernetic combinator @@ -32,7 +30,6 @@ cybersyn-train-network=Train stop controllers capable of coordinating the inputs [virtual-signal-name] cybersyn-priority=Station priority -cybersyn-provide-threshold=Provide threshold cybersyn-request-threshold=Request threshold cybersyn-locked-slots=Locked slots per cargo wagon @@ -49,4 +46,10 @@ comb2=Optional station control depot=Depot control wagon-manifest=Wagon control network=Network -auto-description=Station automatically decides which trains in the network it can service (all trains are allowed by default) +network-tooltip=A signal is used to identify which network this combinator is a member of. Trains will only be dispatched from depots to provide and request stations if they are all identified with the same signal. +auto-tooltip=When checked trains in the network are automatically blacklisted if they are not able to interact with all of the loading and unloading machines present along the station. When unchecked the blacklist is empty and all trains are allowed. +auto-description=Allow automatic train blacklist +switch-provide=Provide only +switch-request=Request only +switch-provide-tooltip=Lock this station to only provide items to the network. By default it both requests and provides. +switch-request-tooltip=Lock this station to only request items from the network. By default it both requests and provides. diff --git a/cybersyn/prototypes/signal.lua b/cybersyn/prototypes/signal.lua index 0390528..b8819f6 100644 --- a/cybersyn/prototypes/signal.lua +++ b/cybersyn/prototypes/signal.lua @@ -14,15 +14,6 @@ priority_signal = { subgroup = "cybersyn-signal", order = "a" } -p_threshold_signal = { - type = "virtual-signal", - name = PROVIDE_THRESHOLD, - icon = "__cybersyn__/graphics/icons/provide-threshold.png", - icon_size = 64, - icon_mipmaps = 4, - subgroup = "cybersyn-signal", - order = "b" -} r_threshold_signal = { type = "virtual-signal", name = REQUEST_THRESHOLD, diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 087e8b3..89bac8e 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -70,49 +70,6 @@ function create_manifest_schedule(depot_name, p_stop, r_stop, manifest) }} end ----@param station Station -local function get_signals(station) - local comb = station.entity_comb1 - if comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then - return comb.get_merged_signals(defines.circuit_connector_id.combinator_input) - else - return nil - end -end - ----@param map_data MapData ----@param comb LuaEntity ----@param signals ConstantCombinatorParameters[]? -function set_combinator_output(map_data, comb, signals) - local out = map_data.to_output[comb.unit_number] - if out.valid then - out.get_or_create_control_behavior().parameters = signals - end -end ----@param comb LuaEntity ----@param op string -function set_combinator_operation(comb, op) - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - control.operation = op - a.parameters = control -end - ----@param map_data MapData ----@param station Station -local function set_comb2(map_data, station) - if station.entity_comb2 then - local deliveries = station.deliveries - local signals = {} - for item_name, count in pairs(deliveries) do - local i = #signals + 1 - local is_fluid = game.item_prototypes[item_name] == nil--NOTE: this is expensive - signals[i] = {index = i, signal = {type = is_fluid and "fluid" or "item", name = item_name}, count = -count} - end - set_combinator_output(map_data, station.entity_comb2, signals) - end -end - ---@param map_data MapData ---@param station Station ---@param manifest Manifest @@ -128,20 +85,6 @@ function remove_manifest(map_data, station, manifest, sign) station.deliveries_total = station.deliveries_total - 1 end ----@param map_data MapData ----@param signal SignalID -local function get_thresholds(map_data, station, signal) - local comb2 = station.entity_comb2 - if comb2 and comb2.valid then - local count = comb2.get_merged_signal(signal, defines.circuit_connector_id.combinator_input) - if count > 0 then - return station.r_threshold, count - elseif count < 0 then - return -count, station.p_threshold - end - end - return station.r_threshold, station.p_threshold -end ---@param stop0 LuaEntity ---@param stop1 LuaEntity @@ -150,12 +93,12 @@ local function get_stop_dist(stop0, stop1) end - ---@param map_data MapData ---@param r_station_id uint ---@param p_station_id uint ---@param item_type string -local function get_valid_train(map_data, r_station_id, p_station_id, item_type) +---@param min_amount_to_move int +local function get_valid_train(map_data, r_station_id, p_station_id, item_type, min_amount_to_move) --NOTE: this code is the critical section for run-time optimization local r_station = map_data.stations[r_station_id] local p_station = map_data.stations[p_station_id] @@ -185,10 +128,10 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type) --check layout validity for both stations local capacity = (is_fluid and train.fluid_capacity) or train.item_slot_capacity if - capacity > 0 and + capacity > min_amount_to_move and btest(netand, depot.network_flag) and - (r_station.is_all or r_station.accepted_layouts[layout_id]) and - (p_station.is_all or p_station.accepted_layouts[layout_id]) + (r_station.allows_all_trains or r_station.accepted_layouts[layout_id]) and + (p_station.allows_all_trains or p_station.accepted_layouts[layout_id]) then valid_train_exists = true --check if exists valid path @@ -227,48 +170,35 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p ---@type string local network_name = depot.network_name - local requests = {} local manifest = {} - local r_signals = r_station.tick_signals - if r_signals then - for k, v in pairs(r_signals) do - ---@type string - local item_name = v.signal.name - local item_count = v.count - local effective_item_count = item_count + (r_station.deliveries[item_name] or 0) - if effective_item_count < 0 and item_count < 0 then - requests[item_name] = -effective_item_count - end - end - end - - local p_signals = p_station.tick_signals - if p_signals then - for k, v in pairs(p_signals) do - local item_name = v.signal.name - local item_count = v.count - local item_type = v.signal.type - local effective_item_count = item_count + (p_station.deliveries[item_name] or 0) - if effective_item_count > 0 and item_count > 0 then - local r = requests[item_name] - if r then - local item = {name = item_name, type = item_type, count = min(r, effective_item_count)} - if item_name == primary_item_name then - manifest[#manifest + 1] = manifest[1] - manifest[1] = item - else - manifest[#manifest + 1] = item - end + for k, v in pairs(r_station.tick_signals) do + ---@type string + local item_name = v.signal.name + local item_type = v.signal.type + local r_item_count = v.count + local r_effective_item_count = r_item_count + (r_station.deliveries[item_name] or 0) + if r_effective_item_count < 0 and r_item_count < 0 then + local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] + local p_effective_item_count = p_station.p_count_or_r_threshold_per_item[item_name] + if p_effective_item_count and p_effective_item_count >= r_threshold then + local item = {name = item_name, type = item_type, count = min(-r_effective_item_count, p_effective_item_count)} + if item_name == primary_item_name then + manifest[#manifest + 1] = manifest[1] + manifest[1] = item + else + manifest[#manifest + 1] = item end end end end - local locked_slots = max(p_station.locked_slots, r_station.locked_slots) + --locked slots is only taken into account after the train is already approved for dispatch + local locked_slots = p_station.locked_slots local total_slots_left = train.item_slot_capacity if locked_slots > 0 then - total_slots_left = max(total_slots_left - #train.entity.cargo_wagons*locked_slots, min(total_slots_left, #train.entity.cargo_wagons)) + local total_cw = #train.entity.cargo_wagons + total_slots_left = min(total_slots_left, max(total_slots_left - total_cw*locked_slots, total_cw)) end local total_liquid_left = train.fluid_capacity @@ -307,7 +237,6 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p r_station.deliveries_total = r_station.deliveries_total + 1 p_station.deliveries_total = p_station.deliveries_total + 1 - assert(manifest[1].name == primary_item_name) for item_i, item in ipairs(manifest) do assert(item.count > 0, "main.lua error, transfer amount was not positive") @@ -420,12 +349,12 @@ local function tick_poll_station(map_data, mod_settings) if station.network_name and station.deliveries_total < station.entity_stop.trains_limit then station.r_threshold = mod_settings.r_threshold - station.p_threshold = mod_settings.p_threshold station.priority = 0 station.locked_slots = 0 station.network_flag = mod_settings.network_flag local signals = get_signals(station) station.tick_signals = signals + table_clear(station.p_count_or_r_threshold_per_item) if signals then for k, v in pairs(signals) do local item_name = v.signal.name @@ -436,10 +365,8 @@ local function tick_poll_station(map_data, mod_settings) if item_name == SIGNAL_PRIORITY then station.priority = item_count elseif item_name == REQUEST_THRESHOLD and item_count ~= 0 then - --NOTE: thresholds must be >0 or they will cause a crash + --NOTE: thresholds must be >0 or they can cause a crash station.r_threshold = abs(item_count) - elseif item_name == PROVIDE_THRESHOLD and item_count ~= 0 then - station.p_threshold = abs(item_count) elseif item_name == LOCKED_SLOTS then station.locked_slots = max(item_count, 0) end @@ -453,12 +380,13 @@ local function tick_poll_station(map_data, mod_settings) end end for k, v in pairs(signals) do + ---@type string local item_name = v.signal.name local item_count = v.count local effective_item_count = item_count + (station.deliveries[item_name] or 0) - local r_threshold, p_threshold = get_thresholds(map_data, station, v.signal) + local r_threshold = get_threshold(map_data, station, v.signal) - if -effective_item_count >= r_threshold and -item_count >= r_threshold then + if station.is_r and -effective_item_count >= r_threshold and -item_count >= r_threshold then local item_network_name = station.network_name..":"..item_name local stations = all_r_stations[item_network_name] if stations == nil then @@ -468,7 +396,8 @@ local function tick_poll_station(map_data, mod_settings) all_names[#all_names + 1] = v.signal end stations[#stations + 1] = station_id - elseif effective_item_count >= p_threshold and item_count >= p_threshold then + station.p_count_or_r_threshold_per_item[item_name] = r_threshold + elseif station.is_p and effective_item_count > 0 and item_count > 0 then local item_network_name = station.network_name..":"..item_name local stations = all_p_stations[item_network_name] if stations == nil then @@ -476,6 +405,7 @@ local function tick_poll_station(map_data, mod_settings) all_p_stations[item_network_name] = stations end stations[#stations + 1] = station_id + station.p_count_or_r_threshold_per_item[item_name] = effective_item_count else signals[k] = nil end @@ -544,6 +474,10 @@ local function tick_dispatch(map_data, mod_settings) end local r_station_id = table_remove(r_stations--[[@as uint[] ]]) + local r_station = stations[r_station_id] + local item_name = tick_data.item_name + local item_type = tick_data.item_type + local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] local best = 0 local best_depot = nil @@ -551,24 +485,27 @@ local function tick_dispatch(map_data, mod_settings) local highest_prior = -INF local could_have_been_serviced = false for j, p_station_id in ipairs(p_stations) do - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, tick_data.item_type) - local prior = stations[p_station_id].priority - if prior > highest_prior or (prior == highest_prior and d < best_dist) then - if depot then - best = j - best_dist = d - best_depot = depot - highest_prior = prior - elseif d < INF then - could_have_been_serviced = true - best = j + local p_station = stations[p_station_id] + if p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold then + local prior = p_station.priority + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, r_threshold) + if prior > highest_prior or (prior == highest_prior and d < best_dist) then + if depot then + best = j + best_dist = d + best_depot = depot + highest_prior = prior + elseif d < INF then + could_have_been_serviced = true + best = j + end end end end if best_depot then - send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, tick_data.item_name) + send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) elseif could_have_been_serviced then - send_missing_train_alert_for_stops(stations[r_station_id].entity_stop, stations[p_stations[best]].entity_stop) + send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) end return false end diff --git a/cybersyn/scripts/combinator.lua b/cybersyn/scripts/combinator.lua new file mode 100644 index 0000000..265a194 --- /dev/null +++ b/cybersyn/scripts/combinator.lua @@ -0,0 +1,100 @@ +--By Mami +local abs = math.abs +local floor = math.floor + +---@param param ArithmeticCombinatorParameters +function get_comb_secondary_state(param) + local bits = param.second_constant or 0 + return bits%2 == 1, floor(bits/2)%3 +end +---@param depot Depot +function set_depot_from_comb_state(depot) + local param = depot.entity_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local signal = param.first_signal + depot.network_name = signal and signal.name or nil +end +---@param station Station +function set_station_from_comb_state(station) + --NOTE: this does nothing to update currently active deliveries + local param = station.entity_comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local bits = param.second_constant or 0 + local is_pr_state = floor(bits/2)%3 + local signal = param.first_signal + station.network_name = signal and signal.name or nil + station.allows_all_trains = bits%2 == 1 + station.is_p = is_pr_state == 0 or is_pr_state == 1 + station.is_r = is_pr_state == 0 or is_pr_state == 2 +end +---@param control LuaArithmeticCombinatorControlBehavior +function set_comb_allows_all_trains(control, allows_all_trains) + local param = control.parameters + local bits = param.second_constant or 0 + param.second_constant = (bits - bits%2) + (allows_all_trains and 1 or 0) + control.parameters = param +end +---@param control LuaArithmeticCombinatorControlBehavior +function set_comb_is_pr_state(control, is_pr_state) + local param = control.parameters + local bits = param.second_constant or 0 + param.second_constant = (bits%2) + (2*is_pr_state) + control.parameters = param +end + + +---@param map_data MapData +---@param comb LuaEntity +---@param signals ConstantCombinatorParameters[]? +function set_combinator_output(map_data, comb, signals) + local out = map_data.to_output[comb.unit_number] + if out.valid then + out.get_or_create_control_behavior().parameters = signals + end +end +---@param comb LuaEntity +---@param op string +function set_combinator_operation(comb, op) + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = a.parameters + control.operation = op + a.parameters = control +end + + +---@param station Station +function get_signals(station) + local comb = station.entity_comb1 + if comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then + return comb.get_merged_signals(defines.circuit_connector_id.combinator_input) + else + return nil + end +end + +---@param map_data MapData +---@param station Station +function set_comb2(map_data, station) + if station.entity_comb2 then + local deliveries = station.deliveries + local signals = {} + for item_name, count in pairs(deliveries) do + local i = #signals + 1 + local is_fluid = game.item_prototypes[item_name] == nil--NOTE: this is expensive + signals[i] = {index = i, signal = {type = is_fluid and "fluid" or "item", name = item_name}, count = -count} + end + set_combinator_output(map_data, station.entity_comb2, signals) + end +end + +---@param map_data MapData +---@param station Station +---@param signal SignalID +function get_threshold(map_data, station, signal) + local comb2 = station.entity_comb2 + if comb2 and comb2.valid then + local count = comb2.get_merged_signal(signal, defines.circuit_connector_id.combinator_input) + if count ~= 0 then + return abs(count) + end + end + return station.r_threshold +end diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index bdce530..cf75555 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -6,7 +6,6 @@ NONEMPTY_TRAIN_NAME = "cybersyn-nonempty-train" SIGNAL_PRIORITY = "cybersyn-priority" REQUEST_THRESHOLD = "cybersyn-request-threshold" -PROVIDE_THRESHOLD = "cybersyn-provide-threshold" LOCKED_SLOTS = "cybersyn-locked-slots" COMBINATOR_NAME = "cybersyn-combinator" @@ -16,6 +15,7 @@ COMBINATOR_CLOSE_SOUND = "entity-close/cybersyn-combinator" OPERATION_DEFAULT = "*" OPERATION_PRIMARY_IO = "/" OPERATION_PRIMARY_IO_ACTIVE = "^" +OPERATION_PRIMARY_IO_NOT_FOUND = "<<" OPERATION_SECONDARY_IO = "%" OPERATION_DEPOT = "+" OPERATION_WAGON_MANIFEST = "-" diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 6f8ba23..bb0fdfe 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -16,11 +16,12 @@ ---@field public economy Economy ---@class Station +---@field public is_p boolean +---@field public is_r boolean ---@field public deliveries_total int ---@field public last_delivery_tick int ---@field public priority int --transient ---@field public r_threshold int >= 0 --transient ----@field public p_threshold int >= 0 --transient ---@field public locked_slots int >= 0 --transient ---@field public entity_stop LuaEntity ---@field public entity_comb1 LuaEntity @@ -29,10 +30,11 @@ ---@field public deliveries {[string]: int} ---@field public network_name string? ---@field public network_flag int --transient ----@field public is_all boolean +---@field public allows_all_trains boolean ---@field public accepted_layouts TrainClass ---@field public layout_pattern string? ---@field public tick_signals {[uint]: Signal}? --transient +---@field public p_count_or_r_threshold_per_item {[string]: int}? --transient ---@class Depot ---@field public priority int --transient @@ -67,12 +69,18 @@ ---@class CybersynModSettings ---@field public tps int ---@field public r_threshold int ----@field public p_threshold int ---@field public network_flag int ---@type CybersynModSettings mod_settings = {} +local pairs = pairs +function table_clear(tab) + for k, _ in pairs(tab) do + tab[k] = nil + end +end + function init_global() global.total_ticks = 0 global.tick_state = STATE_INIT diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 1749b68..7945bfe 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -30,10 +30,21 @@ STATUS_NAMES_DEFAULT = "entity-status.disabled" ---@param player LuaPlayer function gui_opened(comb, player) local rootgui = player.gui.screen - local selected_index = 0 local control = comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] local op = control.operation - if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE then + + local selected_index = 0 + local switch_state = "none" + local allows_all_trains, is_pr_state = get_comb_secondary_state(control) + if is_pr_state == 0 then + switch_state = "none" + elseif is_pr_state == 1 then + switch_state = "left" + elseif is_pr_state == 2 then + switch_state = "right" + end + + if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_NOT_FOUND then selected_index = 1 elseif op == OPERATION_SECONDARY_IO then selected_index = 2 @@ -43,61 +54,67 @@ function gui_opened(comb, player) selected_index = 4 end -local window = flib_gui.build(rootgui, { - {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={ - --title bar - {type="flow", ref={"titlebar"}, children={ - {type="label", style="frame_title", caption={"cybersyn-gui.combinator-title"}, elem_mods={ignored_by_interaction=true}}, - {type="empty-widget", style="flib_titlebar_drag_handle", elem_mods={ignored_by_interaction=true}}, - {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}, sprite="utility/close_white", hovered_sprite="utility/close_black", name=COMBINATOR_NAME, actions={ - on_click = {"close", comb.unit_number} - }} - }}, - {type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=12}, children={ - {type="flow", direction="vertical", style_mods={horizontal_align="left"}, children={ - --status - {type="flow", style="status_flow", direction="horizontal", style_mods={vertical_align="center", horizontally_stretchable=true, bottom_padding=4}, children={ - {type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}}, - {type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}} - }}, - --preview - {type="frame", style="deep_frame_in_shallow_frame", style_mods={minimal_width=0, horizontally_stretchable=true, padding=0}, children={ - {type="entity-preview", style="wide_entity_button", ref={"preview"}}, - }}, - --drop down - {type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=8}}, - {type="drop-down", style_mods={top_padding=3}, ref={"operation"}, actions={ - on_selection_state_changed={"drop-down", comb.unit_number} - }, selected_index=selected_index, items={ - {"cybersyn-gui.comb1"}, - {"cybersyn-gui.comb2"}, - {"cybersyn-gui.depot"}, - {"cybersyn-gui.wagon-manifest"}, - }}, - ---choose-elem-button - {type="line", style_mods={top_padding=10}}, - {type="label", name="network_label", ref={"network_label"}, style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=7}}, - {type="flow", name="bottom", direction="horizontal", children={ - {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", signal=control.first_signal, style_mods={bottom_margin=2, right_margin=6}, actions={ - on_elem_changed={"choose-elem-button", comb.unit_number} + local window = flib_gui.build(rootgui, { + {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={ + --title bar + {type="flow", ref={"titlebar"}, children={ + {type="label", style="frame_title", caption={"cybersyn-gui.combinator-title"}, elem_mods={ignored_by_interaction=true}}, + {type="empty-widget", style="flib_titlebar_drag_handle", elem_mods={ignored_by_interaction=true}}, + {type="sprite-button", style="frame_action_button", mouse_button_filter={"left"}, sprite="utility/close_white", hovered_sprite="utility/close_black", name=COMBINATOR_NAME, actions={ + on_click = {"close", comb.unit_number} + }} + }}, + {type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=12}, children={ + {type="flow", direction="vertical", style_mods={horizontal_align="left"}, children={ + --status + {type="flow", style="status_flow", direction="horizontal", style_mods={vertical_align="center", horizontally_stretchable=true, bottom_padding=4}, children={ + {type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}}, + {type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}} }}, - {type="checkbox", name="radiobutton", ref={"radiobutton"}, state=control.second_constant ~= 1, style_mods={top_margin=4}, actions={ - on_checked_state_changed={"radiobutton", comb.unit_number} + --preview + {type="frame", style="deep_frame_in_shallow_frame", style_mods={minimal_width=0, horizontally_stretchable=true, padding=0}, children={ + {type="entity-preview", style="wide_entity_button", ref={"preview"}}, }}, - {type="label", name="radiolabel", style_mods={single_line=false, maximal_width=330, left_padding=3}, ref={"radiolabel"}, caption={"cybersyn-gui.auto-description"}}, + --drop down + {type="label", style="heading_3_label", caption={"cybersyn-gui.operation"}, style_mods={top_padding=8}}, + {type="flow", name="top", direction="horizontal", style_mods={vertical_align="center"}, children={ + {type="drop-down", style_mods={top_padding=3, right_margin=8}, ref={"operation"}, actions={ + on_selection_state_changed={"drop-down", comb.unit_number} + }, selected_index=selected_index, items={ + {"cybersyn-gui.comb1"}, + {"cybersyn-gui.comb2"}, + {"cybersyn-gui.depot"}, + {"cybersyn-gui.wagon-manifest"}, + }}, + {type="switch", name="switch", ref={"switch"}, allow_none_state=true, switch_state=switch_state, left_label_caption={"cybersyn-gui.switch-provide"}, right_label_caption={"cybersyn-gui.switch-request"}, left_label_tooltip={"cybersyn-gui.switch-provide-tooltip"}, right_label_tooltip={"cybersyn-gui.switch-request-tooltip"}, actions={ + on_switch_state_changed={"switch", comb.unit_number} + }} + }}, + ---choose-elem-button + {type="line", style_mods={top_padding=10}}, + {type="label", name="network_label", ref={"network_label"}, style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=8}}, + {type="flow", name="bottom", direction="horizontal", style_mods={vertical_align="center"}, children={ + {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=control.first_signal, style_mods={bottom_margin=1, right_margin=6}, actions={ + on_elem_changed={"choose-elem-button", comb.unit_number} + }}, + {type="checkbox", name="radio_button", ref={"radio_button"}, state=not allows_all_trains, tooltip={"cybersyn-gui.auto-tooltip"}, actions={ + on_checked_state_changed={"radio_button", comb.unit_number} + }}, + {type="label", name="radio_label", style_mods={left_padding=3}, ref={"radio_label"}, caption={"cybersyn-gui.auto-description"}}, + }} }} }} }} - }} -}) + }) window.preview.entity = comb window.titlebar.drag_target = window.main_window window.main_window.force_auto_center() window.network.visible = selected_index == 1 or selected_index == 3 window.network_label.visible = selected_index == 1 or selected_index == 3 - window.radiobutton.visible = selected_index == 1 - window.radiolabel.visible = selected_index == 1 + window.radio_button.visible = selected_index == 1 + window.radio_label.visible = selected_index == 1 + window.switch.visible = selected_index == 1 player.opened = window.main_window end @@ -142,31 +159,37 @@ function register_gui_actions() local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end - local parent = element.parent.bottom + local top_flow = element.parent + local all_flow = top_flow.parent + local bottom_flow = all_flow.bottom if element.selected_index == 1 then set_combinator_operation(comb, OPERATION_PRIMARY_IO) - element.parent["network_label"].visible = true - parent["network"].visible = true - parent["radiobutton"].visible = true - parent["radiolabel"].visible = true + top_flow["switch"].visible = true + all_flow["network_label"].visible = true + bottom_flow["network"].visible = true + bottom_flow["radio_button"].visible = true + bottom_flow["radio_label"].visible = true elseif element.selected_index == 2 then set_combinator_operation(comb, OPERATION_SECONDARY_IO) - element.parent["network_label"].visible = false - parent["network"].visible = false - parent["radiobutton"].visible = false - parent["radiolabel"].visible = false + top_flow["switch"].visible = false + all_flow["network_label"].visible = false + bottom_flow["network"].visible = false + bottom_flow["radio_button"].visible = false + bottom_flow["radio_label"].visible = false elseif element.selected_index == 3 then set_combinator_operation(comb, OPERATION_DEPOT) - element.parent["network_label"].visible = true - parent["network"].visible = true - parent["radiobutton"].visible = false - parent["radiolabel"].visible = false + top_flow["switch"].visible = false + all_flow["network_label"].visible = true + bottom_flow["network"].visible = true + bottom_flow["radio_button"].visible = false + bottom_flow["radio_label"].visible = false elseif element.selected_index == 4 then set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) - element.parent["network_label"].visible = false - parent["network"].visible = false - parent["radiobutton"].visible = false - parent["radiolabel"].visible = false + top_flow["switch"].visible = false + all_flow["network_label"].visible = false + bottom_flow["network"].visible = false + bottom_flow["radio_button"].visible = false + bottom_flow["radio_label"].visible = false else return end @@ -188,28 +211,43 @@ function register_gui_actions() else control.first_signal = signal end - a.parameters = control + on_combinator_network_updated(global, comb, signal and signal.name or nil) - elseif msg[1] == "radiobutton" then + elseif msg[1] == "radio_button" then local element = event.element if not element then return end local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - local is_auto = element.state - control.second_constant = is_auto and 0 or 1 - - a.parameters = control + local allows_all_trains = element.state + set_comb_allows_all_trains(a, allows_all_trains) local stop = global.to_stop[comb.unit_number] if stop then local station = global.stations[stop.unit_number] if station then - set_station_train_class(global, station, not is_auto) + set_station_train_class(global, station, allows_all_trains) + end + end + elseif msg[1] == "switch" then + local element = event.element + if not element then return end + local comb = global.to_comb[msg[2]] + if not comb or not comb.valid then return end + + local is_pr_state = (element.switch_state == "none" and 0) or (element.switch_state == "left" and 1) or 2 + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + set_comb_is_pr_state(a, is_pr_state) + + local stop = global.to_stop[comb.unit_number] + if stop then + local station = global.stations[stop.unit_number] + if station then + station.is_p = is_pr_state == 0 or is_pr_state == 1 + station.is_r = is_pr_state == 0 or is_pr_state == 2 end end end diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 9a8238e..9b71a35 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -447,11 +447,11 @@ end ---@param map_data MapData ---@param station Station ----@param is_all boolean -function set_station_train_class(map_data, station, is_all) - if station.is_all ~= is_all then - station.is_all = is_all - if not is_all then +---@param allows_all_trains boolean +function set_station_train_class(map_data, station, allows_all_trains) + if station.allows_all_trains ~= allows_all_trains then + station.allows_all_trains = allows_all_trains + if not allows_all_trains then reset_station_layout(map_data, station, nil) end end @@ -461,7 +461,7 @@ end ---@param station Station ---@param forbidden_entity LuaEntity? function update_station_if_auto(map_data, station, forbidden_entity) - if not station.is_all then + if not station.allows_all_trains then reset_station_layout(map_data, station, forbidden_entity) end end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index eacbf12..05db3ed 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -91,15 +91,16 @@ end ---@param map_data MapData ---@param stop LuaEntity ---@param comb LuaEntity -local function on_depot_built(map_data, stop, comb, control) +local function on_depot_built(map_data, stop, comb) local depot = { entity_stop = stop, entity_comb = comb, - network_name = control.first_signal and control.first_signal.name or nil, + --network_name = nil, priority = 0, network_flag = 0, } map_data.depots[stop.unit_number] = depot + set_depot_from_comb_state(depot) end local function on_depot_broken(map_data, depot) @@ -117,8 +118,7 @@ end ---@param stop LuaEntity ---@param comb1 LuaEntity ---@param comb2 LuaEntity ----@param control ArithmeticCombinatorParameters -local function on_station_built(map_data, stop, comb1, comb2, control) +local function on_station_built(map_data, stop, comb1, comb2) local station = { entity_stop = stop, entity_comb1 = comb1, @@ -130,13 +130,14 @@ local function on_station_built(map_data, stop, comb1, comb2, control) r_threshold = 0, p_threshold = 0, locked_slots = 0, - network_name = control.first_signal and control.first_signal.name or nil, + --network_name = param.first_signal and param.first_signal.name or nil, network_flag = 0, deliveries = {}, - is_all = control.second_constant == 1, + --allows_all_trains = param.second_constant == 1, accepted_layouts = {}, layout_pattern = nil, } + set_station_from_comb_state(station) map_data.stations[stop.unit_number] = station update_station_if_auto(map_data, station, nil) @@ -244,18 +245,18 @@ local function on_combinator_built(map_data, comb) map_data.to_output[comb.unit_number] = out map_data.to_stop[comb.unit_number] = stop - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - local op = control.operation + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local param = control.parameters + local op = param.operation if op == OPERATION_DEFAULT then op = OPERATION_PRIMARY_IO - control.operation = op - control.first_signal = NETWORK_SIGNAL_DEFAULT - a.parameters = control - elseif op == OPERATION_PRIMARY_IO_ACTIVE then + param.operation = op + param.first_signal = NETWORK_SIGNAL_DEFAULT + control.parameters = param + elseif op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_NOT_FOUND then op = OPERATION_PRIMARY_IO - control.operation = op - a.parameters = control + param.operation = op + control.parameters = param end if op == OPERATION_WAGON_MANIFEST then if rail then @@ -269,7 +270,7 @@ local function on_combinator_built(map_data, comb) if depot or station then --NOTE: repeated combinators are ignored else - on_depot_built(map_data, stop, comb, control) + on_depot_built(map_data, stop, comb) end end elseif op == OPERATION_SECONDARY_IO then @@ -279,23 +280,23 @@ local function on_combinator_built(map_data, comb) station.entity_comb2 = comb end end - elseif stop then - control.operation = OPERATION_PRIMARY_IO - local station = map_data.stations[stop.unit_number] - local depot = map_data.depots[stop.unit_number] - if station then - --NOTE: repeated combinators are ignored - else - if depot then - --NOTE: this will disrupt deliveries in progress that where dispatched from this station in a minor way - map_data.depots[stop.unit_number] = nil + elseif op == OPERATION_PRIMARY_IO then + if stop then + local station = map_data.stations[stop.unit_number] + if station then + --NOTE: repeated combinators are ignored + else + local depot = map_data.depots[stop.unit_number] + if depot then + on_depot_broken(map_data, depot) + end + --no station or depot + --add station + + local comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) + + on_station_built(map_data, stop, comb, comb2) end - --no station or depot - --add station - - local comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) - - on_station_built(map_data, stop, comb, comb2, control) end end end @@ -341,14 +342,12 @@ local function on_combinator_broken(map_data, comb) local comb1 = search_for_station_combinator(map_data, stop, OPERATION_PRIMARY_IO, comb) if comb1 then station.entity_comb1 = comb1 - local control = comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - station.network_name = control.first_signal and control.first_signal.name + set_station_from_comb_state(station) else on_station_broken(map_data, stop.unit_number, station) local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) if depot_comb then - local control = depot_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - on_depot_built(map_data, stop, depot_comb, control.first_signal) + on_depot_built(map_data, stop, depot_comb) end end elseif station.entity_comb2 == comb then @@ -360,9 +359,8 @@ local function on_combinator_broken(map_data, comb) --NOTE: this will disrupt deliveries in progress that where dispatched from this station in a minor way local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) if depot_comb then - local control = depot_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] depot.entity_comb = depot_comb - depot.network_name = control.first_signal and control.first_signal.name + set_depot_from_comb_state(depot) else on_depot_broken(map_data, depot) end @@ -404,7 +402,7 @@ local function on_stop_built(map_data, stop) map_data.to_stop[entity.unit_number] = stop local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] local op = control.operation - if op == OPERATION_PRIMARY_IO then + if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_NOT_FOUND then comb1 = entity elseif op == OPERATION_SECONDARY_IO then comb2 = entity @@ -414,9 +412,9 @@ local function on_stop_built(map_data, stop) end end if comb1 then - on_station_built(map_data, stop, comb1, comb2, comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]]) + on_station_built(map_data, stop, comb1, comb2) elseif depot_comb then - on_depot_built(map_data, stop, depot_comb, depot_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]]) + on_depot_built(map_data, stop, depot_comb) end end ---@param map_data MapData @@ -743,7 +741,6 @@ end local function on_settings_changed(event) mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] - mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value--[[@as int]] mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] if event.setting == "cybersyn-ticks-per-second" then local nth_tick = math.ceil(60/mod_settings.tps); @@ -773,7 +770,6 @@ local filter_broken = { local function main() mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] - mod_settings.p_threshold = settings.global["cybersyn-provide-threshold"].value--[[@as int]] mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] --NOTE: There is a concern that it is possible to build or destroy important entities without one of these events being triggered, in which case the mod will have undefined behavior diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index b31e060..f25ed54 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -14,16 +14,7 @@ data:extend({ name = "cybersyn-request-threshold", order = "ab", setting_type = "runtime-global", - default_value = 2000000000, - minimum_value = 1, - maximum_value = 2147483647, - }, - { - type = "int-setting", - name = "cybersyn-provide-threshold", - order = "ac", - setting_type = "runtime-global", - default_value = 2000000000, + default_value = 2000, minimum_value = 1, maximum_value = 2147483647, }, From 9fa190aea674946a8315192663fb156b634e4260 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 19:33:27 -0500 Subject: [PATCH 32/66] added not found notification --- cybersyn/scripts/central-planning.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 89bac8e..9d0617c 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -504,8 +504,13 @@ local function tick_dispatch(map_data, mod_settings) end if best_depot then send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) - elseif could_have_been_serviced then - send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) + else + if could_have_been_serviced then + send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) + end + if r_station.entity_comb1.valid then + set_combinator_operation(r_station.entity_comb1, OPERATION_PRIMARY_IO_NOT_FOUND) + end end return false end From db2d96ccd560549fb3630e5bf35f792dfa027c28 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 19:34:41 -0500 Subject: [PATCH 33/66] changelog mistake --- cybersyn/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 01df86f..4395c35 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -5,7 +5,7 @@ Date: 2022-11-04 - Pre-Alpha release --------------------------------------------------------------------------------------------------- Version: 0.2.0 -Date: 2022-11-04 +Date: 2022-11-10 Features: - Removed provide-threshold - Added ability to specify station type on a cybernetic combinator From cca2b377f5fa230721cff5c00f92dc513e3875b5 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 19:52:50 -0500 Subject: [PATCH 34/66] fixed info --- cybersyn/info.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybersyn/info.json b/cybersyn/info.json index e61ffda..dc78c1f 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.0.1", + "version": "0.2.0", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", From 8d15fd49817c90a612a6b4cda2da760925f23652 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 20:02:57 -0500 Subject: [PATCH 35/66] fixed crash --- cybersyn/scripts/global.lua | 3 ++- cybersyn/scripts/main.lua | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index bb0fdfe..c90b8b7 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -34,7 +34,7 @@ ---@field public accepted_layouts TrainClass ---@field public layout_pattern string? ---@field public tick_signals {[uint]: Signal}? --transient ----@field public p_count_or_r_threshold_per_item {[string]: int}? --transient +---@field public p_count_or_r_threshold_per_item {[string]: int} --transient ---@class Depot ---@field public priority int --transient @@ -75,6 +75,7 @@ mod_settings = {} local pairs = pairs +---@param tab {} function table_clear(tab) for k, _ in pairs(tab) do tab[k] = nil diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 05db3ed..a59f686 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -136,6 +136,7 @@ local function on_station_built(map_data, stop, comb1, comb2) --allows_all_trains = param.second_constant == 1, accepted_layouts = {}, layout_pattern = nil, + p_count_or_r_threshold_per_item = {}, } set_station_from_comb_state(station) map_data.stations[stop.unit_number] = station From dca69194a1a07746eb0ce7469392e824e06ac588 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 20:44:52 -0500 Subject: [PATCH 36/66] fixed dispatch bug --- cybersyn/locale/en/base.cfg | 4 ++-- cybersyn/scripts/central-planning.lua | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 4507c8f..055c857 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -12,7 +12,7 @@ cybersyn-network-flag=The default set of networks a station will service when no cybersyn-combinator=Cybernetic combinator [item-description] -cybersyn-combinator=Place next to a train stop to add it to the cybersyn train network. This stop can now request or provide items that are reported to it by the circuit network. +cybersyn-combinator=Place next to a train stop to add it to the Cybersyn train network. This stop can now request or provide items that are reported to it by the circuit network. [entity-name] cybersyn-combinator=Cybernetic combinator @@ -48,7 +48,7 @@ wagon-manifest=Wagon control network=Network network-tooltip=A signal is used to identify which network this combinator is a member of. Trains will only be dispatched from depots to provide and request stations if they are all identified with the same signal. auto-tooltip=When checked trains in the network are automatically blacklisted if they are not able to interact with all of the loading and unloading machines present along the station. When unchecked the blacklist is empty and all trains are allowed. -auto-description=Allow automatic train blacklist +auto-description=Automatic train blacklist switch-provide=Provide only switch-request=Request only switch-provide-tooltip=Lock this station to only provide items to the network. By default it both requests and provides. diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 9d0617c..1218afb 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -97,8 +97,8 @@ end ---@param r_station_id uint ---@param p_station_id uint ---@param item_type string ----@param min_amount_to_move int -local function get_valid_train(map_data, r_station_id, p_station_id, item_type, min_amount_to_move) +---@param min_slots_to_move int +local function get_valid_train(map_data, r_station_id, p_station_id, item_type, min_slots_to_move) --NOTE: this code is the critical section for run-time optimization local r_station = map_data.stations[r_station_id] local p_station = map_data.stations[p_station_id] @@ -128,7 +128,7 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type, --check layout validity for both stations local capacity = (is_fluid and train.fluid_capacity) or train.item_slot_capacity if - capacity > min_amount_to_move and + capacity >= min_slots_to_move and btest(netand, depot.network_flag) and (r_station.allows_all_trains or r_station.accepted_layouts[layout_id]) and (p_station.allows_all_trains or p_station.accepted_layouts[layout_id]) @@ -488,7 +488,8 @@ local function tick_dispatch(map_data, mod_settings) local p_station = stations[p_station_id] if p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold then local prior = p_station.priority - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, r_threshold) + local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/game.item_prototypes[item_name].stack_size) + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold) if prior > highest_prior or (prior == highest_prior and d < best_dist) then if depot then best = j From 6e80a913391db1dcb83c5fa0edec9f1944226dd7 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 22:15:55 -0500 Subject: [PATCH 37/66] refactored --- cybersyn/control.lua | 4 +- cybersyn/scripts/alerts.lua | 48 ------ cybersyn/scripts/central-planning.lua | 80 +-------- cybersyn/scripts/combinator.lua | 100 ----------- cybersyn/scripts/factorio-api.lua | 232 ++++++++++++++++++++++++++ cybersyn/scripts/main.lua | 2 + cybersyn/scripts/migrations.lua | 22 +++ 7 files changed, 261 insertions(+), 227 deletions(-) delete mode 100644 cybersyn/scripts/alerts.lua delete mode 100644 cybersyn/scripts/combinator.lua create mode 100644 cybersyn/scripts/factorio-api.lua create mode 100644 cybersyn/scripts/migrations.lua diff --git a/cybersyn/control.lua b/cybersyn/control.lua index a1bce50..7b5da94 100644 --- a/cybersyn/control.lua +++ b/cybersyn/control.lua @@ -2,9 +2,9 @@ require("scripts.constants") require("scripts.global") -require("scripts.combinator") +require("scripts.factorio-api") require("scripts.central-planning") require("scripts.layout") require("scripts.gui") -require("scripts.alerts") +require("scripts.migrations") require("scripts.main") diff --git a/cybersyn/scripts/alerts.lua b/cybersyn/scripts/alerts.lua deleted file mode 100644 index 81cca79..0000000 --- a/cybersyn/scripts/alerts.lua +++ /dev/null @@ -1,48 +0,0 @@ ---By Mami - -local send_missing_train_alert_for_stop_icon = {name = MISSING_TRAIN_NAME, type = "fluid"} ----@param r_stop LuaEntity ----@param p_stop LuaEntity -function send_missing_train_alert_for_stops(r_stop, p_stop) - for _, player in pairs(r_stop.force.players) do - player.add_custom_alert( - r_stop, - send_missing_train_alert_for_stop_icon, - {"cybersyn-messages.missing-trains", r_stop.backer_name, p_stop.backer_name}, - true - ) - end -end - -local send_lost_train_alert_icon = {name = LOST_TRAIN_NAME, type = "fluid"} ----@param train LuaTrain -function send_lost_train_alert(train) - local loco = train.front_stock or train.back_stock - if loco then - for _, player in pairs(loco.force.players) do - player.add_custom_alert( - loco, - send_lost_train_alert_icon, - {"cybersyn-messages.lost-train"}, - true - ) - end - end -end - - -local send_nonempty_train_in_depot_alert_icon = {name = NONEMPTY_TRAIN_NAME, type = "fluid"} ----@param train LuaTrain -function send_nonempty_train_in_depot_alert(train) - local loco = train.front_stock or train.back_stock - if loco then - for _, player in pairs(loco.force.players) do - player.add_custom_alert( - loco, - send_nonempty_train_in_depot_alert_icon, - {"cybersyn-messages.nonempty-train"}, - true - ) - end - end -end diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 1218afb..d28224e 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -11,80 +11,6 @@ local table_remove = table.remove local table_sort = table.sort local random = math.random -local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ----@param stop LuaEntity ----@param manifest Manifest -function create_loading_order(stop, manifest) - local condition = {} - for _, item in ipairs(manifest) do - local cond_type - if item.type == "fluid" then - cond_type = "fluid_count" - else - cond_type = "item_count" - end - - condition[#condition + 1] = { - type = cond_type, - compare_type = "and", - condition = {comparator = "≥", first_signal = {type = item.type, name = item.name}, constant = item.count} - } - end - condition[#condition + 1] = create_loading_order_condition - return {station = stop.backer_name, wait_conditions = condition} -end - -local create_unloading_order_condition = {{type = "empty", compare_type = "and"}} ----@param stop LuaEntity -function create_unloading_order(stop) - return {station = stop.backer_name, wait_conditions = create_unloading_order_condition} -end - -local create_inactivity_order_condition = {{type = "inactivity", compare_type = "and", ticks = 120}} ----@param depot_name string -function create_inactivity_order(depot_name) - return {station = depot_name, wait_conditions = create_inactivity_order_condition} -end - ----@param stop LuaEntity -local function create_direct_to_station_order(stop) - return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction} -end - ----@param depot_name string -function create_depot_schedule(depot_name) - return {current = 1, records = {create_inactivity_order(depot_name)}} -end - ----@param depot_name string ----@param p_stop LuaEntity ----@param r_stop LuaEntity ----@param manifest Manifest -function create_manifest_schedule(depot_name, p_stop, r_stop, manifest) - return {current = 1, records = { - create_inactivity_order(depot_name), - create_direct_to_station_order(p_stop), - create_loading_order(p_stop, manifest), - create_direct_to_station_order(r_stop), - create_unloading_order(r_stop), - }} -end - ----@param map_data MapData ----@param station Station ----@param manifest Manifest -function remove_manifest(map_data, station, manifest, sign) - local deliveries = station.deliveries - for i, item in ipairs(manifest) do - deliveries[item.name] = deliveries[item.name] + sign*item.count - if deliveries[item.name] == 0 then - deliveries[item.name] = nil - end - end - set_comb2(map_data, station) - station.deliveries_total = station.deliveries_total - 1 -end - ---@param stop0 LuaEntity ---@param stop1 LuaEntity @@ -215,7 +141,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p keep_item = true end elseif total_slots_left > 0 then - local stack_size = game.item_prototypes[item.name].stack_size + local stack_size = get_stack_size(map_data, item.name) local slots = ceil(item.count/stack_size) if slots > total_slots_left then item.count = total_slots_left*stack_size @@ -488,7 +414,7 @@ local function tick_dispatch(map_data, mod_settings) local p_station = stations[p_station_id] if p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold then local prior = p_station.priority - local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/game.item_prototypes[item_name].stack_size) + local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name)) local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold) if prior > highest_prior or (prior == highest_prior and d < best_dist) then if depot then @@ -518,8 +444,8 @@ end ---@param map_data MapData ---@param mod_settings CybersynModSettings function tick(map_data, mod_settings) + map_data.total_ticks = map_data.total_ticks + 1 if map_data.tick_state == STATE_INIT then - map_data.total_ticks = map_data.total_ticks + 1 map_data.economy.all_p_stations = {} map_data.economy.all_r_stations = {} map_data.economy.all_names = {} diff --git a/cybersyn/scripts/combinator.lua b/cybersyn/scripts/combinator.lua deleted file mode 100644 index 265a194..0000000 --- a/cybersyn/scripts/combinator.lua +++ /dev/null @@ -1,100 +0,0 @@ ---By Mami -local abs = math.abs -local floor = math.floor - ----@param param ArithmeticCombinatorParameters -function get_comb_secondary_state(param) - local bits = param.second_constant or 0 - return bits%2 == 1, floor(bits/2)%3 -end ----@param depot Depot -function set_depot_from_comb_state(depot) - local param = depot.entity_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - local signal = param.first_signal - depot.network_name = signal and signal.name or nil -end ----@param station Station -function set_station_from_comb_state(station) - --NOTE: this does nothing to update currently active deliveries - local param = station.entity_comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - local bits = param.second_constant or 0 - local is_pr_state = floor(bits/2)%3 - local signal = param.first_signal - station.network_name = signal and signal.name or nil - station.allows_all_trains = bits%2 == 1 - station.is_p = is_pr_state == 0 or is_pr_state == 1 - station.is_r = is_pr_state == 0 or is_pr_state == 2 -end ----@param control LuaArithmeticCombinatorControlBehavior -function set_comb_allows_all_trains(control, allows_all_trains) - local param = control.parameters - local bits = param.second_constant or 0 - param.second_constant = (bits - bits%2) + (allows_all_trains and 1 or 0) - control.parameters = param -end ----@param control LuaArithmeticCombinatorControlBehavior -function set_comb_is_pr_state(control, is_pr_state) - local param = control.parameters - local bits = param.second_constant or 0 - param.second_constant = (bits%2) + (2*is_pr_state) - control.parameters = param -end - - ----@param map_data MapData ----@param comb LuaEntity ----@param signals ConstantCombinatorParameters[]? -function set_combinator_output(map_data, comb, signals) - local out = map_data.to_output[comb.unit_number] - if out.valid then - out.get_or_create_control_behavior().parameters = signals - end -end ----@param comb LuaEntity ----@param op string -function set_combinator_operation(comb, op) - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - control.operation = op - a.parameters = control -end - - ----@param station Station -function get_signals(station) - local comb = station.entity_comb1 - if comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then - return comb.get_merged_signals(defines.circuit_connector_id.combinator_input) - else - return nil - end -end - ----@param map_data MapData ----@param station Station -function set_comb2(map_data, station) - if station.entity_comb2 then - local deliveries = station.deliveries - local signals = {} - for item_name, count in pairs(deliveries) do - local i = #signals + 1 - local is_fluid = game.item_prototypes[item_name] == nil--NOTE: this is expensive - signals[i] = {index = i, signal = {type = is_fluid and "fluid" or "item", name = item_name}, count = -count} - end - set_combinator_output(map_data, station.entity_comb2, signals) - end -end - ----@param map_data MapData ----@param station Station ----@param signal SignalID -function get_threshold(map_data, station, signal) - local comb2 = station.entity_comb2 - if comb2 and comb2.valid then - local count = comb2.get_merged_signal(signal, defines.circuit_connector_id.combinator_input) - if count ~= 0 then - return abs(count) - end - end - return station.r_threshold -end diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua new file mode 100644 index 0000000..f6f33e8 --- /dev/null +++ b/cybersyn/scripts/factorio-api.lua @@ -0,0 +1,232 @@ +--By Mami +local abs = math.abs +local floor = math.floor + + +---@param map_data MapData +---@param item_name string +function get_stack_size(map_data, item_name) + return game.item_prototypes[item_name].stack_size +end + + +local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} +---@param stop LuaEntity +---@param manifest Manifest +function create_loading_order(stop, manifest) + local condition = {} + for _, item in ipairs(manifest) do + local cond_type + if item.type == "fluid" then + cond_type = "fluid_count" + else + cond_type = "item_count" + end + + condition[#condition + 1] = { + type = cond_type, + compare_type = "and", + condition = {comparator = "≥", first_signal = {type = item.type, name = item.name}, constant = item.count} + } + end + condition[#condition + 1] = create_loading_order_condition + return {station = stop.backer_name, wait_conditions = condition} +end + +local create_unloading_order_condition = {{type = "empty", compare_type = "and"}} +---@param stop LuaEntity +function create_unloading_order(stop) + return {station = stop.backer_name, wait_conditions = create_unloading_order_condition} +end + +local create_inactivity_order_condition = {{type = "inactivity", compare_type = "and", ticks = 120}} +---@param depot_name string +function create_inactivity_order(depot_name) + return {station = depot_name, wait_conditions = create_inactivity_order_condition} +end + +---@param stop LuaEntity +local function create_direct_to_station_order(stop) + return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction} +end + +---@param depot_name string +function create_depot_schedule(depot_name) + return {current = 1, records = {create_inactivity_order(depot_name)}} +end + +---@param depot_name string +---@param p_stop LuaEntity +---@param r_stop LuaEntity +---@param manifest Manifest +function create_manifest_schedule(depot_name, p_stop, r_stop, manifest) + return {current = 1, records = { + create_inactivity_order(depot_name), + create_direct_to_station_order(p_stop), + create_loading_order(p_stop, manifest), + create_direct_to_station_order(r_stop), + create_unloading_order(r_stop), + }} +end + +---@param map_data MapData +---@param station Station +---@param manifest Manifest +function remove_manifest(map_data, station, manifest, sign) + local deliveries = station.deliveries + for i, item in ipairs(manifest) do + deliveries[item.name] = deliveries[item.name] + sign*item.count + if deliveries[item.name] == 0 then + deliveries[item.name] = nil + end + end + set_comb2(map_data, station) + station.deliveries_total = station.deliveries_total - 1 +end + + + +---@param param ArithmeticCombinatorParameters +function get_comb_secondary_state(param) + local bits = param.second_constant or 0 + return bits%2 == 1, floor(bits/2)%3 +end +---@param depot Depot +function set_depot_from_comb_state(depot) + local param = depot.entity_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local signal = param.first_signal + depot.network_name = signal and signal.name or nil +end +---@param station Station +function set_station_from_comb_state(station) + --NOTE: this does nothing to update currently active deliveries + local param = station.entity_comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local bits = param.second_constant or 0 + local is_pr_state = floor(bits/2)%3 + local signal = param.first_signal + station.network_name = signal and signal.name or nil + station.allows_all_trains = bits%2 == 1 + station.is_p = is_pr_state == 0 or is_pr_state == 1 + station.is_r = is_pr_state == 0 or is_pr_state == 2 +end +---@param control LuaArithmeticCombinatorControlBehavior +function set_comb_allows_all_trains(control, allows_all_trains) + local param = control.parameters + local bits = param.second_constant or 0 + param.second_constant = (bits - bits%2) + (allows_all_trains and 1 or 0) + control.parameters = param +end +---@param control LuaArithmeticCombinatorControlBehavior +function set_comb_is_pr_state(control, is_pr_state) + local param = control.parameters + local bits = param.second_constant or 0 + param.second_constant = (bits%2) + (2*is_pr_state) + control.parameters = param +end + + +---@param map_data MapData +---@param comb LuaEntity +---@param signals ConstantCombinatorParameters[]? +function set_combinator_output(map_data, comb, signals) + local out = map_data.to_output[comb.unit_number] + if out.valid then + out.get_or_create_control_behavior().parameters = signals + end +end +---@param comb LuaEntity +---@param op string +function set_combinator_operation(comb, op) + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = a.parameters + control.operation = op + a.parameters = control +end + + +---@param station Station +function get_signals(station) + local comb = station.entity_comb1 + if comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then + return comb.get_merged_signals(defines.circuit_connector_id.combinator_input) + else + return nil + end +end + +---@param map_data MapData +---@param station Station +function set_comb2(map_data, station) + if station.entity_comb2 then + local deliveries = station.deliveries + local signals = {} + for item_name, count in pairs(deliveries) do + local i = #signals + 1 + local is_fluid = game.item_prototypes[item_name] == nil--NOTE: this is expensive + signals[i] = {index = i, signal = {type = is_fluid and "fluid" or "item", name = item_name}, count = -count} + end + set_combinator_output(map_data, station.entity_comb2, signals) + end +end + + +---@param map_data MapData +---@param station Station +---@param signal SignalID +function get_threshold(map_data, station, signal) + local comb2 = station.entity_comb2 + if comb2 and comb2.valid then + local count = comb2.get_merged_signal(signal, defines.circuit_connector_id.combinator_input) + if count ~= 0 then + return abs(count) + end + end + return station.r_threshold +end + +local send_missing_train_alert_for_stop_icon = {name = MISSING_TRAIN_NAME, type = "fluid"} +---@param r_stop LuaEntity +---@param p_stop LuaEntity +function send_missing_train_alert_for_stops(r_stop, p_stop) + for _, player in pairs(r_stop.force.players) do + player.add_custom_alert( + r_stop, + send_missing_train_alert_for_stop_icon, + {"cybersyn-messages.missing-trains", r_stop.backer_name, p_stop.backer_name}, + true + ) + end +end + +local send_lost_train_alert_icon = {name = LOST_TRAIN_NAME, type = "fluid"} +---@param train LuaTrain +function send_lost_train_alert(train) + local loco = train.front_stock or train.back_stock + if loco then + for _, player in pairs(loco.force.players) do + player.add_custom_alert( + loco, + send_lost_train_alert_icon, + {"cybersyn-messages.lost-train"}, + true + ) + end + end +end + + +local send_nonempty_train_in_depot_alert_icon = {name = NONEMPTY_TRAIN_NAME, type = "fluid"} +---@param train LuaTrain +function send_nonempty_train_in_depot_alert(train) + local loco = train.front_stock or train.back_stock + if loco then + for _, player in pairs(loco.force.players) do + player.add_custom_alert( + loco, + send_nonempty_train_in_depot_alert_icon, + {"cybersyn-messages.nonempty-train"}, + true + ) + end + end +end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index a59f686..37abd59 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -802,6 +802,8 @@ local function main() register_gui_actions() flib_event.on_init(init_global) + + flib_event.on_configuration_changed(on_config_changed) end diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua new file mode 100644 index 0000000..33d20e3 --- /dev/null +++ b/cybersyn/scripts/migrations.lua @@ -0,0 +1,22 @@ +local flib_migration = require("__flib__.migration") + + +local migrations_table = { + ["0.0.1"] = function() + ---@type MapData + local map_data = global + for k, station in pairs(map_data.stations) do + station.p_count_or_r_threshold_per_item = {} + station.p_threshold = nil + station.is_all = nil + station.is_auto = nil + set_station_from_comb_state(station) + end + map_data.tick_state = STATE_INIT + end, +} + +---@param data ConfigurationChangedData +function on_config_changed(data) + flib_migration.on_config_changed(data, migrations_table) +end From 0e9df22034448ee91ba746c3bb0d334068d7add2 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 23:11:02 -0500 Subject: [PATCH 38/66] added missing items display --- cybersyn/scripts/central-planning.lua | 34 ++++++++++++-- cybersyn/scripts/constants.lua | 4 +- cybersyn/scripts/factorio-api.lua | 68 +++++++++++++-------------- cybersyn/scripts/global.lua | 6 ++- cybersyn/scripts/gui.lua | 2 +- cybersyn/scripts/main.lua | 5 +- cybersyn/scripts/migrations.lua | 4 +- 7 files changed, 74 insertions(+), 49 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index d28224e..4253663 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -19,6 +19,21 @@ local function get_stop_dist(stop0, stop1) end +---@param map_data MapData +---@param station Station +---@param manifest Manifest +function remove_manifest(map_data, station, manifest, sign) + local deliveries = station.deliveries + for i, item in ipairs(manifest) do + deliveries[item.name] = deliveries[item.name] + sign*item.count + if deliveries[item.name] == 0 then + deliveries[item.name] = nil + end + end + set_comb2(map_data, station) + station.deliveries_total = station.deliveries_total - 1 +end + ---@param map_data MapData ---@param r_station_id uint ---@param p_station_id uint @@ -206,7 +221,6 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p end - ---@param map_data MapData local function tick_poll_depot(map_data) local depot_id @@ -272,6 +286,11 @@ local function tick_poll_station(map_data, mod_settings) map_data.tick_state = STATE_DISPATCH return true end + if station.display_update then + update_combinator_display(station.entity_comb1, station.display_failed_request) + station.display_update = station.display_failed_request + station.display_failed_request = nil + end if station.network_name and station.deliveries_total < station.entity_stop.trains_limit then station.r_threshold = mod_settings.r_threshold @@ -380,7 +399,7 @@ local function tick_dispatch(map_data, mod_settings) r_stations = all_r_stations[item_network_name] p_stations = all_p_stations[item_network_name] - if p_stations and #r_stations > 0 and #p_stations > 0 then + if p_stations then tick_data.r_stations = r_stations tick_data.p_stations = p_stations tick_data.item_name = signal.name @@ -395,6 +414,12 @@ local function tick_dispatch(map_data, mod_settings) end end) break + else + for i, id in ipairs(r_stations) do + local station = stations[id] + station.display_failed_request = true + station.display_update = true + end end end end @@ -435,9 +460,8 @@ local function tick_dispatch(map_data, mod_settings) if could_have_been_serviced then send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) end - if r_station.entity_comb1.valid then - set_combinator_operation(r_station.entity_comb1, OPERATION_PRIMARY_IO_NOT_FOUND) - end + r_station.display_failed_request = true + r_station.display_update = true end return false end diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index cf75555..7a832c2 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -14,8 +14,8 @@ COMBINATOR_CLOSE_SOUND = "entity-close/cybersyn-combinator" OPERATION_DEFAULT = "*" OPERATION_PRIMARY_IO = "/" -OPERATION_PRIMARY_IO_ACTIVE = "^" -OPERATION_PRIMARY_IO_NOT_FOUND = "<<" +OPERATION_PRIMARY_IO_REQUEST_FAILED = "^" +OPERATION_PRIMARY_IO_ACTIVE = "<<" OPERATION_SECONDARY_IO = "%" OPERATION_DEPOT = "+" OPERATION_WAGON_MANIFEST = "-" diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index f6f33e8..cfe3d8e 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -69,22 +69,6 @@ function create_manifest_schedule(depot_name, p_stop, r_stop, manifest) }} end ----@param map_data MapData ----@param station Station ----@param manifest Manifest -function remove_manifest(map_data, station, manifest, sign) - local deliveries = station.deliveries - for i, item in ipairs(manifest) do - deliveries[item.name] = deliveries[item.name] + sign*item.count - if deliveries[item.name] == 0 then - deliveries[item.name] = nil - end - end - set_comb2(map_data, station) - station.deliveries_total = station.deliveries_total - 1 -end - - ---@param param ArithmeticCombinatorParameters function get_comb_secondary_state(param) @@ -142,6 +126,21 @@ function set_combinator_operation(comb, op) control.operation = op a.parameters = control end +---@param comb LuaEntity +---@param is_failed boolean +function update_combinator_display(comb, is_failed) + local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = a.parameters + if is_failed then + if control.operation == OPERATION_PRIMARY_IO then + control.operation = OPERATION_PRIMARY_IO_REQUEST_FAILED + a.parameters = control + end + elseif control.operation == OPERATION_PRIMARY_IO_REQUEST_FAILED then + control.operation = OPERATION_PRIMARY_IO + a.parameters = control + end +end ---@param station Station @@ -190,12 +189,12 @@ local send_missing_train_alert_for_stop_icon = {name = MISSING_TRAIN_NAME, type function send_missing_train_alert_for_stops(r_stop, p_stop) for _, player in pairs(r_stop.force.players) do player.add_custom_alert( - r_stop, - send_missing_train_alert_for_stop_icon, - {"cybersyn-messages.missing-trains", r_stop.backer_name, p_stop.backer_name}, - true - ) - end + r_stop, + send_missing_train_alert_for_stop_icon, + {"cybersyn-messages.missing-trains", r_stop.backer_name, p_stop.backer_name}, + true + ) +end end local send_lost_train_alert_icon = {name = LOST_TRAIN_NAME, type = "fluid"} @@ -205,15 +204,14 @@ function send_lost_train_alert(train) if loco then for _, player in pairs(loco.force.players) do player.add_custom_alert( - loco, - send_lost_train_alert_icon, - {"cybersyn-messages.lost-train"}, - true - ) - end + loco, + send_lost_train_alert_icon, + {"cybersyn-messages.lost-train"}, + true + ) end end - +end local send_nonempty_train_in_depot_alert_icon = {name = NONEMPTY_TRAIN_NAME, type = "fluid"} ---@param train LuaTrain @@ -222,11 +220,11 @@ function send_nonempty_train_in_depot_alert(train) if loco then for _, player in pairs(loco.force.players) do player.add_custom_alert( - loco, - send_nonempty_train_in_depot_alert_icon, - {"cybersyn-messages.nonempty-train"}, - true - ) - end + loco, + send_nonempty_train_in_depot_alert_icon, + {"cybersyn-messages.nonempty-train"}, + true + ) end end +end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index c90b8b7..9211bd6 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -18,6 +18,7 @@ ---@class Station ---@field public is_p boolean ---@field public is_r boolean +---@field public allows_all_trains boolean ---@field public deliveries_total int ---@field public last_delivery_tick int ---@field public priority int --transient @@ -30,11 +31,12 @@ ---@field public deliveries {[string]: int} ---@field public network_name string? ---@field public network_flag int --transient ----@field public allows_all_trains boolean ---@field public accepted_layouts TrainClass ---@field public layout_pattern string? ---@field public tick_signals {[uint]: Signal}? --transient ---@field public p_count_or_r_threshold_per_item {[string]: int} --transient +---@field public display_failed_request true? +---@field public display_update true? ---@class Depot ---@field public priority int --transient @@ -58,7 +60,7 @@ ---@field public has_filtered_wagon boolean ---@alias Manifest {}[] ----@alias TrainClass {[uint]: boolean} +---@alias TrainClass {[uint]: true} ---@alias cybersyn.global MapData ---@class Economy diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 7945bfe..d3df6e3 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -44,7 +44,7 @@ function gui_opened(comb, player) switch_state = "right" end - if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_NOT_FOUND then + if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then selected_index = 1 elseif op == OPERATION_SECONDARY_IO then selected_index = 2 diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 37abd59..cbfe0cf 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -4,6 +4,7 @@ local flib_event = require("__flib__.event") ---@param map_data MapData ---@param station Station +---@param manifest Manifest ---@param sign int? local function set_comb1(map_data, station, manifest, sign) local comb = station.entity_comb1 @@ -254,7 +255,7 @@ local function on_combinator_built(map_data, comb) param.operation = op param.first_signal = NETWORK_SIGNAL_DEFAULT control.parameters = param - elseif op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_NOT_FOUND then + elseif op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then op = OPERATION_PRIMARY_IO param.operation = op control.parameters = param @@ -403,7 +404,7 @@ local function on_stop_built(map_data, stop) map_data.to_stop[entity.unit_number] = stop local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] local op = control.operation - if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_NOT_FOUND then + if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then comb1 = entity elseif op == OPERATION_SECONDARY_IO then comb2 = entity diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 33d20e3..042ccd1 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -2,15 +2,15 @@ local flib_migration = require("__flib__.migration") local migrations_table = { - ["0.0.1"] = function() + ["0.2.0"] = function() ---@type MapData local map_data = global for k, station in pairs(map_data.stations) do station.p_count_or_r_threshold_per_item = {} station.p_threshold = nil station.is_all = nil - station.is_auto = nil set_station_from_comb_state(station) + set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) end map_data.tick_state = STATE_INIT end, From baf373035aa016813cf42525fc4515d14814e850 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 10 Nov 2022 23:49:22 -0500 Subject: [PATCH 39/66] fixed potential crash --- .vscode/launch.json | 2 - cybersyn/scripts/central-planning.lua | 100 ++++++++++++++------------ cybersyn/scripts/global.lua | 1 + cybersyn/scripts/main.lua | 12 ++-- 4 files changed, 63 insertions(+), 52 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5b06ae2..19be37f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -53,9 +53,7 @@ "debugadapter": true, "flib": true, "cybersyn": true, - "creative-mod": true, }, - "disableExtraMods": true } ] } diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 4253663..7e2c3d1 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -188,16 +188,20 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p local item_network_name = network_name..":"..item.name local r_stations = economy.all_r_stations[item_network_name] local p_stations = economy.all_p_stations[item_network_name] - for j, id in ipairs(r_stations) do - if id == r_station_id then - table_remove(r_stations, j) - break + if r_stations then + for j, id in ipairs(r_stations) do + if id == r_station_id then + table_remove(r_stations, j) + break + end end end - for j, id in ipairs(p_stations) do - if id == p_station_id then - table_remove(p_stations, j) - break + if p_stations then + for j, id in ipairs(p_stations) do + if id == p_station_id then + table_remove(p_stations, j) + break + end end end end @@ -407,18 +411,24 @@ local function tick_dispatch(map_data, mod_settings) table_sort(r_stations, function(a_id, b_id) local a = stations[a_id] local b = stations[b_id] - if a.priority ~= b.priority then - return a.priority < b.priority + if a and b then + if a.priority ~= b.priority then + return a.priority < b.priority + else + return a.last_delivery_tick > b.last_delivery_tick + end else - return a.last_delivery_tick > b.last_delivery_tick + return a == nil end end) break else for i, id in ipairs(r_stations) do local station = stations[id] - station.display_failed_request = true - station.display_update = true + if station then + station.display_failed_request = true + station.display_update = true + end end end end @@ -426,42 +436,44 @@ local function tick_dispatch(map_data, mod_settings) local r_station_id = table_remove(r_stations--[[@as uint[] ]]) local r_station = stations[r_station_id] - local item_name = tick_data.item_name - local item_type = tick_data.item_type - local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] + if r_station then + local item_name = tick_data.item_name + local item_type = tick_data.item_type + local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] - local best = 0 - local best_depot = nil - local best_dist = INF - local highest_prior = -INF - local could_have_been_serviced = false - for j, p_station_id in ipairs(p_stations) do - local p_station = stations[p_station_id] - if p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold then - local prior = p_station.priority - local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name)) - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold) - if prior > highest_prior or (prior == highest_prior and d < best_dist) then - if depot then - best = j - best_dist = d - best_depot = depot - highest_prior = prior - elseif d < INF then - could_have_been_serviced = true - best = j + local best = 0 + local best_depot = nil + local best_dist = INF + local highest_prior = -INF + local could_have_been_serviced = false + for j, p_station_id in ipairs(p_stations) do + local p_station = stations[p_station_id] + if p_station and p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold then + local prior = p_station.priority + local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name)) + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold) + if prior > highest_prior or (prior == highest_prior and d < best_dist) then + if depot then + best = j + best_dist = d + best_depot = depot + highest_prior = prior + elseif d < INF then + could_have_been_serviced = true + best = j + end end end end - end - if best_depot then - send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) - else - if could_have_been_serviced then - send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) + if best_depot then + send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) + else + if could_have_been_serviced then + send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) + end + r_station.display_failed_request = true + r_station.display_update = true end - r_station.display_failed_request = true - r_station.display_update = true end return false end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 9211bd6..99ec00d 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -64,6 +64,7 @@ ---@alias cybersyn.global MapData ---@class Economy +---could contain invalid stations ---@field public all_r_stations {[string]: uint[]} --{[network_name:item_name]: count} ---@field public all_p_stations {[string]: uint[]} --{[network_name:item_name]: count} ---@field public all_names {[string]: uint[]} --{[network_name:item_name]: count} diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index cbfe0cf..ad7ba37 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -661,12 +661,7 @@ local function on_broken(event) local entity = event.entity if not entity or not entity.valid then return end - if entity.train then - local train = global.trains[entity.train.id] - if train then - on_train_broken(global, train) - end - elseif entity.name == "train-stop" then + if entity.name == "train-stop" then on_stop_broken(global, entity) elseif entity.name == COMBINATOR_NAME then on_combinator_broken(global, entity) @@ -676,6 +671,11 @@ local function on_broken(event) update_station_from_pump(global, entity, entity) elseif entity.type == "straight-rail" then update_station_from_rail(global, entity, nil) + elseif entity.train then + local train = global.trains[entity.train.id] + if train then + on_train_broken(global, train) + end end end local function on_rename(event) From 3d71275330ac8255fe90cf1beaa2302f4a84163f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 11 Nov 2022 09:45:45 -0500 Subject: [PATCH 40/66] made depot polling event based --- cybersyn/scripts/central-planning.lua | 70 ++++++++++----------------- cybersyn/scripts/constants.lua | 5 +- cybersyn/scripts/global.lua | 10 +--- cybersyn/scripts/main.lua | 7 ++- cybersyn/scripts/migrations.lua | 6 ++- 5 files changed, 40 insertions(+), 58 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 7e2c3d1..0481b79 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -226,32 +226,9 @@ end ---@param map_data MapData -local function tick_poll_depot(map_data) - local depot_id - do--get next depot id - local tick_data = map_data.tick_data - while true do - if tick_data.network == nil then - tick_data.network_name, tick_data.network = next(map_data.trains_available, tick_data.network_name) - if tick_data.network == nil then - tick_data.train_id = nil - map_data.tick_state = STATE_POLL_STATIONS - return true - end - end - - tick_data.train_id, depot_id = next(tick_data.network, tick_data.train_id) - if depot_id then - break - else - tick_data.network = nil - end - end - end - - local depot = map_data.depots[depot_id] +function poll_depot(map_data, depot) local comb = depot.entity_comb - if depot.network_name and comb.valid and (comb.status == defines.entity_status.working or comb.status == defines.entity_status.low_power) then + if depot.network_name then depot.priority = 0 depot.network_flag = 1 local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) @@ -269,12 +246,9 @@ local function tick_poll_depot(map_data) end end end - else - depot.priority = 0 - depot.network_flag = 0 end - return false end + ---@param map_data MapData ---@param mod_settings CybersynModSettings local function tick_poll_station(map_data, mod_settings) @@ -284,12 +258,19 @@ local function tick_poll_station(map_data, mod_settings) local all_names = map_data.economy.all_names while true do - local station_id, station = next(map_data.stations, tick_data.station_id) - tick_data.station_id = station_id - if station == nil then + tick_data.i = (tick_data.i or 0) + 1 + if tick_data.i > #map_data.all_station_ids then + tick_data.i = nil map_data.tick_state = STATE_DISPATCH return true end + local station_id = map_data.all_station_ids[tick_data.i] + local station = map_data.stations[station_id] + if station == nil then + table_remove(map_data.all_station_ids, tick_data.i) + tick_data.i = tick_data.i - 1 + return false + end if station.display_update then update_combinator_display(station.entity_comb1, station.display_failed_request) station.display_update = station.display_failed_request @@ -303,7 +284,7 @@ local function tick_poll_station(map_data, mod_settings) station.network_flag = mod_settings.network_flag local signals = get_signals(station) station.tick_signals = signals - table_clear(station.p_count_or_r_threshold_per_item) + station.p_count_or_r_threshold_per_item = {} if signals then for k, v in pairs(signals) do local item_name = v.signal.name @@ -379,7 +360,7 @@ local function tick_dispatch(map_data, mod_settings) local r_stations = tick_data.r_stations local p_stations = tick_data.p_stations - if not (p_stations and #r_stations > 0 and #p_stations > 0) then + if not p_stations then while true do local size = #all_names if size == 0 then @@ -445,7 +426,7 @@ local function tick_dispatch(map_data, mod_settings) local best_depot = nil local best_dist = INF local highest_prior = -INF - local could_have_been_serviced = false + local can_be_serviced = false for j, p_station_id in ipairs(p_stations) do local p_station = stations[p_station_id] if p_station and p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold then @@ -458,17 +439,22 @@ local function tick_dispatch(map_data, mod_settings) best_dist = d best_depot = depot highest_prior = prior + can_be_serviced = true elseif d < INF then - could_have_been_serviced = true + can_be_serviced = true best = j end end end end - if best_depot then + if + best_depot and ( + best_depot.entity_comb.status == defines.entity_status.working or + best_depot.entity_comb.status == defines.entity_status.low_power) + then send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) else - if could_have_been_serviced then + if can_be_serviced then send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) end r_station.display_failed_request = true @@ -485,14 +471,10 @@ function tick(map_data, mod_settings) map_data.economy.all_p_stations = {} map_data.economy.all_r_stations = {} map_data.economy.all_names = {} - map_data.tick_state = STATE_POLL_DEPOTS + map_data.tick_state = STATE_POLL_STATIONS end - if map_data.tick_state == STATE_POLL_DEPOTS then - for i = 1, 3 do - if tick_poll_depot(map_data) then break end - end - elseif map_data.tick_state == STATE_POLL_STATIONS then + if map_data.tick_state == STATE_POLL_STATIONS then for i = 1, 2 do if tick_poll_station(map_data, mod_settings) then break end end diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 7a832c2..cab734f 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -45,6 +45,5 @@ STATION_LAYOUT_NOT_CARGO = "[NF]" LONGEST_INSERTER_REACH = 2 STATE_INIT = 0 -STATE_POLL_DEPOTS = 1 -STATE_POLL_STATIONS = 2 -STATE_DISPATCH = 3 +STATE_POLL_STATIONS = 1 +STATE_DISPATCH = 2 diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 99ec00d..5ac47dd 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -6,6 +6,7 @@ ---@field public to_output {[uint]: LuaEntity} ---@field public to_stop {[uint]: LuaEntity} ---@field public stations {[uint]: Station} +---@field public all_station_ids uint[] ---@field public depots {[uint]: Depot} ---@field public trains {[uint]: Train} ---@field public trains_available {[string]: {[uint]: uint}} --{[network_name]: {[train_id]: depot_id}} @@ -77,14 +78,6 @@ ---@type CybersynModSettings mod_settings = {} -local pairs = pairs ----@param tab {} -function table_clear(tab) - for k, _ in pairs(tab) do - tab[k] = nil - end -end - function init_global() global.total_ticks = 0 global.tick_state = STATE_INIT @@ -98,6 +91,7 @@ function init_global() global.to_output = {} global.to_stop = {} global.stations = {} + global.all_station_ids = {} global.depots = {} global.trains = {} global.trains_available = {} diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index ad7ba37..3a1ec3a 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -68,6 +68,7 @@ local function add_available_train(map_data, depot, train_id) local train = map_data.trains[train_id] train.depot_name = depot.entity_stop.backer_name train.depot = depot + poll_depot(map_data, depot) end ---@param map_data MapData ---@param depot Depot @@ -102,6 +103,7 @@ local function on_depot_built(map_data, stop, comb) } map_data.depots[stop.unit_number] = depot set_depot_from_comb_state(depot) + poll_depot(map_data, depot) end local function on_depot_broken(map_data, depot) @@ -140,7 +142,9 @@ local function on_station_built(map_data, stop, comb1, comb2) p_count_or_r_threshold_per_item = {}, } set_station_from_comb_state(station) - map_data.stations[stop.unit_number] = station + local id = stop.unit_number--[[@as uint]] + map_data.stations[id] = station + map_data.all_station_ids[#map_data.all_station_ids + 1] = id update_station_if_auto(map_data, station, nil) end @@ -363,6 +367,7 @@ local function on_combinator_broken(map_data, comb) if depot_comb then depot.entity_comb = depot_comb set_depot_from_comb_state(depot) + poll_depot(map_data, depot) else on_depot_broken(map_data, depot) end diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 042ccd1..ac79f7e 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -5,14 +5,16 @@ local migrations_table = { ["0.2.0"] = function() ---@type MapData local map_data = global - for k, station in pairs(map_data.stations) do + map_data.tick_state = STATE_INIT + map_data.all_station_ids = {} + for id, station in pairs(map_data.stations) do station.p_count_or_r_threshold_per_item = {} station.p_threshold = nil station.is_all = nil set_station_from_comb_state(station) set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) + map_data.all_station_ids[#map_data.all_station_ids + 1] = id end - map_data.tick_state = STATE_INIT end, } From a0ac3d4d246f19e69c53cb1816fc1dfeafab2894 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 11 Nov 2022 10:46:55 -0500 Subject: [PATCH 41/66] minor improvements --- cybersyn/changelog.txt | 6 + cybersyn/scripts/central-planning.lua | 157 +++++++++++++------------- 2 files changed, 86 insertions(+), 77 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 4395c35..a563926 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -9,3 +9,9 @@ Date: 2022-11-10 Features: - Removed provide-threshold - Added ability to specify station type on a cybernetic combinator + +--------------------------------------------------------------------------------------------------- +Version: 0.2.1 +Date: 2022-11-11 + Features: + - Minor bugfixes and performance improvements diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 0481b79..6845e4a 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -257,93 +257,96 @@ local function tick_poll_station(map_data, mod_settings) local all_p_stations = map_data.economy.all_p_stations local all_names = map_data.economy.all_names - while true do + local station_id + local station + while true do--choose a station tick_data.i = (tick_data.i or 0) + 1 if tick_data.i > #map_data.all_station_ids then tick_data.i = nil map_data.tick_state = STATE_DISPATCH return true end - local station_id = map_data.all_station_ids[tick_data.i] - local station = map_data.stations[station_id] - if station == nil then + station_id = map_data.all_station_ids[tick_data.i] + station = map_data.stations[station_id] + if station then + if station.display_update then + update_combinator_display(station.entity_comb1, station.display_failed_request) + station.display_update = station.display_failed_request + station.display_failed_request = nil + end + if station.network_name and station.deliveries_total < station.entity_stop.trains_limit then + break + end + else + --lazy delete removed stations table_remove(map_data.all_station_ids, tick_data.i) tick_data.i = tick_data.i - 1 - return false - end - if station.display_update then - update_combinator_display(station.entity_comb1, station.display_failed_request) - station.display_update = station.display_failed_request - station.display_failed_request = nil - end - - if station.network_name and station.deliveries_total < station.entity_stop.trains_limit then - station.r_threshold = mod_settings.r_threshold - station.priority = 0 - station.locked_slots = 0 - station.network_flag = mod_settings.network_flag - local signals = get_signals(station) - station.tick_signals = signals - station.p_count_or_r_threshold_per_item = {} - if signals then - for k, v in pairs(signals) do - local item_name = v.signal.name - local item_count = v.count - local item_type = v.signal.type - if item_name then - if item_type == "virtual" then - if item_name == SIGNAL_PRIORITY then - station.priority = item_count - elseif item_name == REQUEST_THRESHOLD and item_count ~= 0 then - --NOTE: thresholds must be >0 or they can cause a crash - station.r_threshold = abs(item_count) - elseif item_name == LOCKED_SLOTS then - station.locked_slots = max(item_count, 0) - end - signals[k] = nil - end - if item_name == station.network_name then - station.network_flag = item_count - end - else - signals[k] = nil - end - end - for k, v in pairs(signals) do - ---@type string - local item_name = v.signal.name - local item_count = v.count - local effective_item_count = item_count + (station.deliveries[item_name] or 0) - local r_threshold = get_threshold(map_data, station, v.signal) - - if station.is_r and -effective_item_count >= r_threshold and -item_count >= r_threshold then - local item_network_name = station.network_name..":"..item_name - local stations = all_r_stations[item_network_name] - if stations == nil then - stations = {} - all_r_stations[item_network_name] = stations - all_names[#all_names + 1] = item_network_name - all_names[#all_names + 1] = v.signal - end - stations[#stations + 1] = station_id - station.p_count_or_r_threshold_per_item[item_name] = r_threshold - elseif station.is_p and effective_item_count > 0 and item_count > 0 then - local item_network_name = station.network_name..":"..item_name - local stations = all_p_stations[item_network_name] - if stations == nil then - stations = {} - all_p_stations[item_network_name] = stations - end - stations[#stations + 1] = station_id - station.p_count_or_r_threshold_per_item[item_name] = effective_item_count - else - signals[k] = nil - end - end - end - return false end end + station.r_threshold = mod_settings.r_threshold + station.priority = 0 + station.locked_slots = 0 + station.network_flag = mod_settings.network_flag + local signals = get_signals(station) + station.tick_signals = signals + station.p_count_or_r_threshold_per_item = {} + if signals then + for k, v in pairs(signals) do + local item_name = v.signal.name + local item_count = v.count + local item_type = v.signal.type + if item_name then + if item_type == "virtual" then + if item_name == SIGNAL_PRIORITY then + station.priority = item_count + elseif item_name == REQUEST_THRESHOLD and item_count ~= 0 then + --NOTE: thresholds must be >0 or they can cause a crash + station.r_threshold = abs(item_count) + elseif item_name == LOCKED_SLOTS then + station.locked_slots = max(item_count, 0) + end + signals[k] = nil + end + if item_name == station.network_name then + station.network_flag = item_count + end + else + signals[k] = nil + end + end + for k, v in pairs(signals) do + ---@type string + local item_name = v.signal.name + local item_count = v.count + local effective_item_count = item_count + (station.deliveries[item_name] or 0) + local r_threshold = get_threshold(map_data, station, v.signal) + + if station.is_r and -effective_item_count >= r_threshold and -item_count >= r_threshold then + local item_network_name = station.network_name..":"..item_name + local stations = all_r_stations[item_network_name] + if stations == nil then + stations = {} + all_r_stations[item_network_name] = stations + all_names[#all_names + 1] = item_network_name + all_names[#all_names + 1] = v.signal + end + stations[#stations + 1] = station_id + station.p_count_or_r_threshold_per_item[item_name] = r_threshold + elseif station.is_p and effective_item_count > 0 and item_count > 0 then + local item_network_name = station.network_name..":"..item_name + local stations = all_p_stations[item_network_name] + if stations == nil then + stations = {} + all_p_stations[item_network_name] = stations + end + stations[#stations + 1] = station_id + station.p_count_or_r_threshold_per_item[item_name] = effective_item_count + else + signals[k] = nil + end + end + end + return false end ---@param map_data MapData ---@param mod_settings CybersynModSettings From e68f6553a908fa33803e8ad19eab772fa9e2a0da Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 11 Nov 2022 11:21:23 -0500 Subject: [PATCH 42/66] fixed bug --- cybersyn/scripts/central-planning.lua | 4 +++- cybersyn/scripts/gui.lua | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 6845e4a..195ff54 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -246,6 +246,8 @@ function poll_depot(map_data, depot) end end end + else + depot.network_flag = 0 end end @@ -363,7 +365,7 @@ local function tick_dispatch(map_data, mod_settings) local r_stations = tick_data.r_stations local p_stations = tick_data.p_stations - if not p_stations then + if p_stations == nil or #p_stations == 0 or #r_stations == 0 then while true do local size = #all_names if size == 0 then diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index d3df6e3..e6afbb4 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -222,7 +222,7 @@ function register_gui_actions() local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local allows_all_trains = element.state + local allows_all_trains = not element.state set_comb_allows_all_trains(a, allows_all_trains) local stop = global.to_stop[comb.unit_number] From 52f6088904efd297e757515f4c6e8700a1cf270f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 11 Nov 2022 11:24:06 -0500 Subject: [PATCH 43/66] fixed typechecking --- cybersyn/scripts/central-planning.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 195ff54..1e047b3 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -363,7 +363,9 @@ local function tick_dispatch(map_data, mod_settings) local all_names = map_data.economy.all_names local stations = map_data.stations + ---@type {} local r_stations = tick_data.r_stations + ---@type {} local p_stations = tick_data.p_stations if p_stations == nil or #p_stations == 0 or #r_stations == 0 then while true do @@ -457,10 +459,10 @@ local function tick_dispatch(map_data, mod_settings) best_depot.entity_comb.status == defines.entity_status.working or best_depot.entity_comb.status == defines.entity_status.low_power) then - send_train_between(map_data, r_station_id, table_remove(p_stations, best), best_depot, item_name) + send_train_between(map_data, r_station_id, table_remove(p_stations--[[@as {}]], best), best_depot, item_name) else if can_be_serviced then - send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations[best]].entity_stop) + send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations--[[@as {}]][best]].entity_stop) end r_station.display_failed_request = true r_station.display_update = true From 7f44dae0ec4ca8fdf7d0a38c818f89b8c6b162ff Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 11 Nov 2022 22:13:33 -0500 Subject: [PATCH 44/66] added fixes --- cybersyn/info.json | 2 +- cybersyn/prototypes/signal.lua | 22 +++++++++++----------- cybersyn/scripts/central-planning.lua | 5 ++++- cybersyn/scripts/factorio-api.lua | 3 ++- cybersyn/scripts/gui.lua | 4 ++-- cybersyn/scripts/main.lua | 6 ++---- cybersyn/scripts/migrations.lua | 7 +++++++ 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/cybersyn/info.json b/cybersyn/info.json index dc78c1f..719e2ed 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.2.0", + "version": "0.2.1", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/prototypes/signal.lua b/cybersyn/prototypes/signal.lua index b8819f6..a29220d 100644 --- a/cybersyn/prototypes/signal.lua +++ b/cybersyn/prototypes/signal.lua @@ -5,15 +5,6 @@ subgroup_signal = { group = "signals", order = "f" } -priority_signal = { - type = "virtual-signal", - name = SIGNAL_PRIORITY, - icon = "__cybersyn__/graphics/icons/priority.png", - icon_size = 64, - icon_mipmaps = 4, - subgroup = "cybersyn-signal", - order = "a" -} r_threshold_signal = { type = "virtual-signal", name = REQUEST_THRESHOLD, @@ -21,7 +12,16 @@ r_threshold_signal = { icon_size = 64, icon_mipmaps = 4, subgroup = "cybersyn-signal", - order = "c" + order = "a" +} +priority_signal = { + type = "virtual-signal", + name = SIGNAL_PRIORITY, + icon = "__cybersyn__/graphics/icons/priority.png", + icon_size = 64, + icon_mipmaps = 4, + subgroup = "cybersyn-signal", + order = "b" } locked_slots_signal = { type = "virtual-signal", @@ -30,5 +30,5 @@ locked_slots_signal = { icon_size = 64, icon_mipmaps = 4, subgroup = "cybersyn-signal", - order = "d" + order = "c" } diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 1e047b3..0994233 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -32,6 +32,9 @@ function remove_manifest(map_data, station, manifest, sign) end set_comb2(map_data, station) station.deliveries_total = station.deliveries_total - 1 + if station.deliveries_total == 0 and station.entity_comb1.valid then + set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) + end end ---@param map_data MapData @@ -448,8 +451,8 @@ local function tick_dispatch(map_data, mod_settings) highest_prior = prior can_be_serviced = true elseif d < INF then - can_be_serviced = true best = j + can_be_serviced = true end end end diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index cfe3d8e..4565793 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -45,9 +45,10 @@ function create_inactivity_order(depot_name) return {station = depot_name, wait_conditions = create_inactivity_order_condition} end +local create_direct_to_station_order_condition = {{type = "time", compare_type = "and", ticks = 1}} ---@param stop LuaEntity local function create_direct_to_station_order(stop) - return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction} + return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction,wait_conditions = create_direct_to_station_order_condition} end ---@param depot_name string diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index e6afbb4..0f27d03 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -220,10 +220,10 @@ function register_gui_actions() local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local allows_all_trains = not element.state - set_comb_allows_all_trains(a, allows_all_trains) + set_comb_allows_all_trains(control, allows_all_trains) local stop = global.to_stop[comb.unit_number] if stop then diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 3a1ec3a..ebd5730 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -16,9 +16,6 @@ local function set_comb1(map_data, station, manifest, sign) end set_combinator_output(map_data, comb, signals) else - if station.deliveries_total == 0 then - set_combinator_operation(comb, OPERATION_PRIMARY_IO) - end set_combinator_output(map_data, comb, nil) end end @@ -131,7 +128,6 @@ local function on_station_built(map_data, stop, comb1, comb2) last_delivery_tick = 0, priority = 0, r_threshold = 0, - p_threshold = 0, locked_slots = 0, --network_name = param.first_signal and param.first_signal.name or nil, network_flag = 0, @@ -570,6 +566,8 @@ local function on_train_arrives_buffer(map_data, stop, train) set_comb1(map_data, station, train.manifest, -1) set_r_wagon_combs(map_data, station, train) end + elseif train.status == STATUS_P and train.p_station_id == station_id then + elseif train.status == STATUS_R and train.r_station_id == station_id then else on_failed_delivery(map_data, train) remove_train(map_data, train, train.entity.id) diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index ac79f7e..8cc7149 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -16,6 +16,13 @@ local migrations_table = { map_data.all_station_ids[#map_data.all_station_ids + 1] = id end end, + ["0.2.1"] = function() + ---@type MapData + local map_data = global + for id, station in pairs(map_data.stations) do + station.p_threshold = nil + end + end, } ---@param data ConfigurationChangedData From 5ccd52d214b93af83b8fee4c5c3ebc540fd3cd66 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sat, 12 Nov 2022 01:16:20 -0500 Subject: [PATCH 45/66] added minor improvement --- cybersyn/prototypes/signal.lua | 18 +++++----- cybersyn/scripts/central-planning.lua | 49 +++++++++++++++------------ 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/cybersyn/prototypes/signal.lua b/cybersyn/prototypes/signal.lua index a29220d..fa053d2 100644 --- a/cybersyn/prototypes/signal.lua +++ b/cybersyn/prototypes/signal.lua @@ -14,15 +14,6 @@ r_threshold_signal = { subgroup = "cybersyn-signal", order = "a" } -priority_signal = { - type = "virtual-signal", - name = SIGNAL_PRIORITY, - icon = "__cybersyn__/graphics/icons/priority.png", - icon_size = 64, - icon_mipmaps = 4, - subgroup = "cybersyn-signal", - order = "b" -} locked_slots_signal = { type = "virtual-signal", name = LOCKED_SLOTS, @@ -30,5 +21,14 @@ locked_slots_signal = { icon_size = 64, icon_mipmaps = 4, subgroup = "cybersyn-signal", + order = "b" +} +priority_signal = { + type = "virtual-signal", + name = SIGNAL_PRIORITY, + icon = "__cybersyn__/graphics/icons/priority.png", + icon_size = 64, + icon_mipmaps = 4, + subgroup = "cybersyn-signal", order = "c" } diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 0994233..29bb3b6 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -324,30 +324,37 @@ local function tick_poll_station(map_data, mod_settings) local item_name = v.signal.name local item_count = v.count local effective_item_count = item_count + (station.deliveries[item_name] or 0) - local r_threshold = get_threshold(map_data, station, v.signal) - if station.is_r and -effective_item_count >= r_threshold and -item_count >= r_threshold then - local item_network_name = station.network_name..":"..item_name - local stations = all_r_stations[item_network_name] - if stations == nil then - stations = {} - all_r_stations[item_network_name] = stations - all_names[#all_names + 1] = item_network_name - all_names[#all_names + 1] = v.signal + local flag = true + if station.is_r then + local r_threshold = get_threshold(map_data, station, v.signal) + if -effective_item_count >= r_threshold and -item_count >= r_threshold then + flag = false + local item_network_name = station.network_name..":"..item_name + local stations = all_r_stations[item_network_name] + if stations == nil then + stations = {} + all_r_stations[item_network_name] = stations + all_names[#all_names + 1] = item_network_name + all_names[#all_names + 1] = v.signal + end + stations[#stations + 1] = station_id + station.p_count_or_r_threshold_per_item[item_name] = r_threshold end - stations[#stations + 1] = station_id - station.p_count_or_r_threshold_per_item[item_name] = r_threshold - elseif station.is_p and effective_item_count > 0 and item_count > 0 then - local item_network_name = station.network_name..":"..item_name - local stations = all_p_stations[item_network_name] - if stations == nil then - stations = {} - all_p_stations[item_network_name] = stations + end + if flag then + if station.is_p and effective_item_count > 0 and item_count > 0 then + local item_network_name = station.network_name..":"..item_name + local stations = all_p_stations[item_network_name] + if stations == nil then + stations = {} + all_p_stations[item_network_name] = stations + end + stations[#stations + 1] = station_id + station.p_count_or_r_threshold_per_item[item_name] = effective_item_count + else + signals[k] = nil end - stations[#stations + 1] = station_id - station.p_count_or_r_threshold_per_item[item_name] = effective_item_count - else - signals[k] = nil end end end From bd320d5d78a09e46d9a77b7a9f1540265b4f6af3 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 13 Nov 2022 12:37:40 -0500 Subject: [PATCH 46/66] fixed trains limit --- cybersyn/scripts/central-planning.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 29bb3b6..9e2b4d6 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -434,7 +434,7 @@ local function tick_dispatch(map_data, mod_settings) local r_station_id = table_remove(r_stations--[[@as uint[] ]]) local r_station = stations[r_station_id] - if r_station then + if r_station and r_station.deliveries_total < r_station.entity_stop.trains_limit then local item_name = tick_data.item_name local item_type = tick_data.item_type local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] @@ -446,7 +446,7 @@ local function tick_dispatch(map_data, mod_settings) local can_be_serviced = false for j, p_station_id in ipairs(p_stations) do local p_station = stations[p_station_id] - if p_station and p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold then + if p_station and p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold and p_station.deliveries_total < p_station.entity_stop.trains_limit then local prior = p_station.priority local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name)) local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold) From 4198adedec1a1eac2336a9b7f86634a01549d932 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 13 Nov 2022 23:51:44 -0500 Subject: [PATCH 47/66] added a warmup time on stations --- cybersyn/changelog.txt | 6 +++++- cybersyn/info.json | 2 +- cybersyn/locale/en/base.cfg | 2 ++ cybersyn/scripts/central-planning.lua | 17 ++++++++++++++--- cybersyn/scripts/global.lua | 7 +++++-- cybersyn/scripts/main.lua | 6 ++++-- cybersyn/scripts/migrations.lua | 8 ++++++++ cybersyn/settings.lua | 9 +++++++++ 8 files changed, 48 insertions(+), 9 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index a563926..1ceff0f 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -9,9 +9,13 @@ Date: 2022-11-10 Features: - Removed provide-threshold - Added ability to specify station type on a cybernetic combinator - --------------------------------------------------------------------------------------------------- Version: 0.2.1 Date: 2022-11-11 Features: - Minor bugfixes and performance improvements +--------------------------------------------------------------------------------------------------- +Version: 0.3.0 +Date: 2022-11-13 + Features: + - Added warmup period on just built stations diff --git a/cybersyn/info.json b/cybersyn/info.json index 719e2ed..8dddfad 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.2.1", + "version": "0.3.0", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 055c857..f0f8c42 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -2,11 +2,13 @@ cybersyn-ticks-per-second=Dispatcher updates per second cybersyn-request-threshold=Default requester threshold cybersyn-network-flag=Default network flags +cybersyn-warmup-time=Station warmup time [mod-setting-description] cybersyn-ticks-per-second=How many times per second the dispather should check for new deliveries. Deliveries are made one at a time per update. This value will be rounded up to a divisor of 60. cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. Huge values will prevent stations from taking requests from the network unless an explicit threshold is set. cybersyn-network-flag=The default set of networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible network flags to choose from. +cybersyn-warmup-time=How many seconds a cybernetic combinator will wait before connecting to the Cybersyn network. A grace period to modify or correct the circuit network before trains start dispatching to a newly blueprinted station. [item-name] cybersyn-combinator=Cybernetic combinator diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 9e2b4d6..2da75b2 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -266,12 +266,12 @@ local function tick_poll_station(map_data, mod_settings) local station while true do--choose a station tick_data.i = (tick_data.i or 0) + 1 - if tick_data.i > #map_data.all_station_ids then + if tick_data.i > #map_data.active_station_ids then tick_data.i = nil map_data.tick_state = STATE_DISPATCH return true end - station_id = map_data.all_station_ids[tick_data.i] + station_id = map_data.active_station_ids[tick_data.i] station = map_data.stations[station_id] if station then if station.display_update then @@ -284,7 +284,7 @@ local function tick_poll_station(map_data, mod_settings) end else --lazy delete removed stations - table_remove(map_data.all_station_ids, tick_data.i) + table_remove(map_data.active_station_ids, tick_data.i) tick_data.i = tick_data.i - 1 end end @@ -489,6 +489,17 @@ function tick(map_data, mod_settings) map_data.economy.all_r_stations = {} map_data.economy.all_names = {} map_data.tick_state = STATE_POLL_STATIONS + for i, id in pairs(map_data.warmup_station_ids) do + local station = map_data.stations[id] + if station then + if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps >= map_data.total_ticks then + map_data.active_station_ids[#map_data.active_station_ids + 1] = id + map_data.warmup_station_ids[i] = nil + end + else + map_data.warmup_station_ids[i] = nil + end + end end if map_data.tick_state == STATE_POLL_STATIONS then diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 5ac47dd..19dfac3 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -6,7 +6,8 @@ ---@field public to_output {[uint]: LuaEntity} ---@field public to_stop {[uint]: LuaEntity} ---@field public stations {[uint]: Station} ----@field public all_station_ids uint[] +---@field public active_station_ids uint[] +---@field public warmup_station_ids uint[] ---@field public depots {[uint]: Depot} ---@field public trains {[uint]: Train} ---@field public trains_available {[string]: {[uint]: uint}} --{[network_name]: {[train_id]: depot_id}} @@ -74,6 +75,7 @@ ---@field public tps int ---@field public r_threshold int ---@field public network_flag int +---@field public warmup_time int ---@type CybersynModSettings mod_settings = {} @@ -91,7 +93,8 @@ function init_global() global.to_output = {} global.to_stop = {} global.stations = {} - global.all_station_ids = {} + global.active_station_ids = {} + global.warmup_station_ids = {} global.depots = {} global.trains = {} global.trains_available = {} diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index ebd5730..4980bf7 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -125,7 +125,7 @@ local function on_station_built(map_data, stop, comb1, comb2) entity_comb2 = comb2, wagon_combs = nil, deliveries_total = 0, - last_delivery_tick = 0, + last_delivery_tick = map_data.total_ticks, priority = 0, r_threshold = 0, locked_slots = 0, @@ -140,7 +140,7 @@ local function on_station_built(map_data, stop, comb1, comb2) set_station_from_comb_state(station) local id = stop.unit_number--[[@as uint]] map_data.stations[id] = station - map_data.all_station_ids[#map_data.all_station_ids + 1] = id + map_data.warmup_station_ids[#map_data.warmup_station_ids + 1] = id update_station_if_auto(map_data, station, nil) end @@ -747,6 +747,7 @@ local function on_settings_changed(event) mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] + mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]] if event.setting == "cybersyn-ticks-per-second" then local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nil) @@ -776,6 +777,7 @@ local function main() mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] + mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]] --NOTE: There is a concern that it is possible to build or destroy important entities without one of these events being triggered, in which case the mod will have undefined behavior flib_event.register(defines.events.on_built_entity, on_built, filter_built) diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 8cc7149..d854087 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -23,6 +23,14 @@ local migrations_table = { station.p_threshold = nil end end, + ["0.3.0"] = function() + ---@type MapData + local map_data = global + map_data.warmup_station_ids = {} + map_data.active_station_ids = map_data.all_station_ids + map_data.all_station_ids = nil + mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]] + end, } ---@param data ConfigurationChangedData diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index f25ed54..e60fd94 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -27,4 +27,13 @@ data:extend({ minimum_value = -2147483648, maximum_value = 2147483647, }, + { + type = "int-setting", + name = "cybersyn-warmup-time", + order = "ac", + setting_type = "runtime-global", + default_value = 20, + minimum_value = 0, + maximum_value = 2147483647, + }, }) From 43b210efbe823600fffddae4f3db585fb6dd6a65 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 15 Nov 2022 18:28:46 -0500 Subject: [PATCH 48/66] fixed copy-paste and added alert sounds --- cybersyn/changelog.txt | 6 ++ cybersyn/info.json | 2 +- cybersyn/locale/en/base.cfg | 1 + cybersyn/prototypes/entity.lua | 2 +- cybersyn/scripts/constants.lua | 1 + cybersyn/scripts/factorio-api.lua | 87 +++++++++++++++++-------- cybersyn/scripts/global.lua | 4 ++ cybersyn/scripts/gui.lua | 54 +++++----------- cybersyn/scripts/layout.lua | 16 +---- cybersyn/scripts/main.lua | 104 ++++++++++++++++++++++++------ cybersyn/scripts/migrations.lua | 9 +++ 11 files changed, 187 insertions(+), 99 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 1ceff0f..9d73006 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -19,3 +19,9 @@ Version: 0.3.0 Date: 2022-11-13 Features: - Added warmup period on just built stations +--------------------------------------------------------------------------------------------------- +Version: 0.3.0 +Date: 2022-11-13 + Features: + - Fixed copy-paste + - Added alert sounds diff --git a/cybersyn/info.json b/cybersyn/info.json index 8dddfad..6343fd8 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.3.0", + "version": "0.4.0", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index f0f8c42..de9e67f 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -39,6 +39,7 @@ cybersyn-locked-slots=Locked slots per cargo wagon missing-trains=No trains available to make a delivery from __2__ to __1__ lost-train=A train has become lost nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty +unexpected-train=A train has returned to the depot before completing it's delivery [cybersyn-gui] combinator-title=Cybernetic combinator diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index c5fd57c..44ebb83 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -12,7 +12,7 @@ combinator_entity.radius_visualisation_specification = { distance = 1.5, } combinator_entity.active_energy_usage = "10KW" -combinator_entity.allow_copy_paste = false +combinator_entity.allow_copy_paste = true diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index cab734f..14406aa 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -11,6 +11,7 @@ LOCKED_SLOTS = "cybersyn-locked-slots" COMBINATOR_NAME = "cybersyn-combinator" COMBINATOR_OUT_NAME = "cybersyn-combinator-output" COMBINATOR_CLOSE_SOUND = "entity-close/cybersyn-combinator" +ALERT_SOUND = "utility/console_message" OPERATION_DEFAULT = "*" OPERATION_PRIMARY_IO = "/" diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 4565793..71bf86b 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -70,7 +70,9 @@ function create_manifest_schedule(depot_name, p_stop, r_stop, manifest) }} end - +function get_comb_params(comb) + return comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] +end ---@param param ArithmeticCombinatorParameters function get_comb_secondary_state(param) local bits = param.second_constant or 0 @@ -78,14 +80,14 @@ function get_comb_secondary_state(param) end ---@param depot Depot function set_depot_from_comb_state(depot) - local param = depot.entity_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local param = get_comb_params(depot.entity_comb) local signal = param.first_signal depot.network_name = signal and signal.name or nil end ---@param station Station function set_station_from_comb_state(station) --NOTE: this does nothing to update currently active deliveries - local param = station.entity_comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local param = get_comb_params(station.entity_comb1) local bits = param.second_constant or 0 local is_pr_state = floor(bits/2)%3 local signal = param.first_signal @@ -94,19 +96,36 @@ function set_station_from_comb_state(station) station.is_p = is_pr_state == 0 or is_pr_state == 1 station.is_r = is_pr_state == 0 or is_pr_state == 2 end ----@param control LuaArithmeticCombinatorControlBehavior -function set_comb_allows_all_trains(control, allows_all_trains) +---@param comb LuaEntity +---@param allows_all_trains boolean +function set_comb_allows_all_trains(comb, allows_all_trains) + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local param = control.parameters local bits = param.second_constant or 0 param.second_constant = (bits - bits%2) + (allows_all_trains and 1 or 0) control.parameters = param + return param end ----@param control LuaArithmeticCombinatorControlBehavior -function set_comb_is_pr_state(control, is_pr_state) +---@param comb LuaEntity +---@param is_pr_state 0|1|2 +function set_comb_is_pr_state(comb, is_pr_state) + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local param = control.parameters local bits = param.second_constant or 0 param.second_constant = (bits%2) + (2*is_pr_state) control.parameters = param + return param +end + +---@param comb LuaEntity +---@param signal SignalID? +function set_comb_network_name(comb, signal) + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local param = control.parameters + + param.first_signal = signal + control.parameters = param + return param end @@ -122,24 +141,25 @@ end ---@param comb LuaEntity ---@param op string function set_combinator_operation(comb, op) - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - control.operation = op - a.parameters = control + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local param = control.parameters + param.operation = op + control.parameters = param + return param end ---@param comb LuaEntity ---@param is_failed boolean function update_combinator_display(comb, is_failed) - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local param = control.parameters if is_failed then - if control.operation == OPERATION_PRIMARY_IO then - control.operation = OPERATION_PRIMARY_IO_REQUEST_FAILED - a.parameters = control + if param.operation == OPERATION_PRIMARY_IO then + param.operation = OPERATION_PRIMARY_IO_REQUEST_FAILED + control.parameters = param end - elseif control.operation == OPERATION_PRIMARY_IO_REQUEST_FAILED then - control.operation = OPERATION_PRIMARY_IO - a.parameters = control + elseif param.operation == OPERATION_PRIMARY_IO_REQUEST_FAILED then + param.operation = OPERATION_PRIMARY_IO + control.parameters = param end end @@ -193,9 +213,8 @@ function send_missing_train_alert_for_stops(r_stop, p_stop) r_stop, send_missing_train_alert_for_stop_icon, {"cybersyn-messages.missing-trains", r_stop.backer_name, p_stop.backer_name}, - true - ) -end + true) + end end local send_lost_train_alert_icon = {name = LOST_TRAIN_NAME, type = "fluid"} @@ -208,12 +227,26 @@ function send_lost_train_alert(train) loco, send_lost_train_alert_icon, {"cybersyn-messages.lost-train"}, - true - ) + true) + player.play_sound({path = ALERT_SOUND}) + end end end +---@param train LuaTrain +function send_unexpected_train_alert(train) + local loco = train.front_stock or train.back_stock + if loco then + for _, player in pairs(loco.force.players) do + player.add_custom_alert( + loco, + send_lost_train_alert_icon, + {"cybersyn-messages.unexpected-train"}, + true) + end + end end + local send_nonempty_train_in_depot_alert_icon = {name = NONEMPTY_TRAIN_NAME, type = "fluid"} ---@param train LuaTrain function send_nonempty_train_in_depot_alert(train) @@ -224,8 +257,8 @@ function send_nonempty_train_in_depot_alert(train) loco, send_nonempty_train_in_depot_alert_icon, {"cybersyn-messages.nonempty-train"}, - true - ) + true) + player.play_sound({path = ALERT_SOUND}) + end end end -end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 19dfac3..9ab230c 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -2,7 +2,9 @@ ---@class MapData ---@field public total_ticks uint ---@field public layout_top_id uint +---@field public is_player_cursor_blueprint {[uint]: true|nil} ---@field public to_comb {[uint]: LuaEntity} +---@field public to_comb_params {[uint]: ArithmeticCombinatorParameters} ---@field public to_output {[uint]: LuaEntity} ---@field public to_stop {[uint]: LuaEntity} ---@field public stations {[uint]: Station} @@ -90,6 +92,7 @@ function init_global() all_names = {}, } global.to_comb = {} + global.to_comb_params = {} global.to_output = {} global.to_stop = {} global.stations = {} @@ -101,4 +104,5 @@ function init_global() global.layouts = {} global.layout_train_count = {} global.layout_top_id = 1 + global.is_player_cursor_blueprint = {} end diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 0f27d03..b00e32b 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -30,12 +30,12 @@ STATUS_NAMES_DEFAULT = "entity-status.disabled" ---@param player LuaPlayer function gui_opened(comb, player) local rootgui = player.gui.screen - local control = comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - local op = control.operation + local param = get_comb_params(comb) + local op = param.operation local selected_index = 0 local switch_state = "none" - local allows_all_trains, is_pr_state = get_comb_secondary_state(control) + local allows_all_trains, is_pr_state = get_comb_secondary_state(param) if is_pr_state == 0 then switch_state = "none" elseif is_pr_state == 1 then @@ -94,7 +94,7 @@ function gui_opened(comb, player) {type="line", style_mods={top_padding=10}}, {type="label", name="network_label", ref={"network_label"}, style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=8}}, {type="flow", name="bottom", direction="horizontal", style_mods={vertical_align="center"}, children={ - {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=control.first_signal, style_mods={bottom_margin=1, right_margin=6}, actions={ + {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=param.first_signal, style_mods={bottom_margin=1, right_margin=6}, actions={ on_elem_changed={"choose-elem-button", comb.unit_number} }}, {type="checkbox", name="radio_button", ref={"radio_button"}, state=not allows_all_trains, tooltip={"cybersyn-gui.auto-tooltip"}, actions={ @@ -162,29 +162,30 @@ function register_gui_actions() local top_flow = element.parent local all_flow = top_flow.parent local bottom_flow = all_flow.bottom + local param if element.selected_index == 1 then - set_combinator_operation(comb, OPERATION_PRIMARY_IO) + param = set_combinator_operation(comb, OPERATION_PRIMARY_IO) top_flow["switch"].visible = true all_flow["network_label"].visible = true bottom_flow["network"].visible = true bottom_flow["radio_button"].visible = true bottom_flow["radio_label"].visible = true elseif element.selected_index == 2 then - set_combinator_operation(comb, OPERATION_SECONDARY_IO) + param = set_combinator_operation(comb, OPERATION_SECONDARY_IO) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false bottom_flow["radio_button"].visible = false bottom_flow["radio_label"].visible = false elseif element.selected_index == 3 then - set_combinator_operation(comb, OPERATION_DEPOT) + param = set_combinator_operation(comb, OPERATION_DEPOT) top_flow["switch"].visible = false all_flow["network_label"].visible = true bottom_flow["network"].visible = true bottom_flow["radio_button"].visible = false bottom_flow["radio_label"].visible = false elseif element.selected_index == 4 then - set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) + param = set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false @@ -194,44 +195,31 @@ function register_gui_actions() return end - on_combinator_updated(global, comb) + on_combinator_updated(global, comb, param) elseif msg[1] == "choose-elem-button" then local element = event.element if not element then return end local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - local signal = element.elem_value if signal and (signal.name == "signal-everything" or signal.name == "signal-anything" or signal.name == "signal-each") then - control.first_signal = nil + signal = nil element.elem_value = nil - else - control.first_signal = signal end - a.parameters = control + local param = set_comb_network_name(comb, signal) - on_combinator_network_updated(global, comb, signal and signal.name or nil) + on_combinator_updated(global, comb, param) elseif msg[1] == "radio_button" then local element = event.element if not element then return end local comb = global.to_comb[msg[2]] if not comb or not comb.valid then return end - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local allows_all_trains = not element.state - set_comb_allows_all_trains(control, allows_all_trains) + local param = set_comb_allows_all_trains(comb, allows_all_trains) - local stop = global.to_stop[comb.unit_number] - if stop then - local station = global.stations[stop.unit_number] - if station then - set_station_train_class(global, station, allows_all_trains) - end - end + on_combinator_updated(global, comb, param) elseif msg[1] == "switch" then local element = event.element if not element then return end @@ -239,17 +227,9 @@ function register_gui_actions() if not comb or not comb.valid then return end local is_pr_state = (element.switch_state == "none" and 0) or (element.switch_state == "left" and 1) or 2 - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - set_comb_is_pr_state(a, is_pr_state) + local param = set_comb_is_pr_state(comb, is_pr_state) - local stop = global.to_stop[comb.unit_number] - if stop then - local station = global.stations[stop.unit_number] - if station then - station.is_p = is_pr_state == 0 or is_pr_state == 1 - station.is_r = is_pr_state == 0 or is_pr_state == 2 - end - end + on_combinator_updated(global, comb, param) end end end) diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 9b71a35..2c7423c 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -397,8 +397,8 @@ local function reset_station_layout(map_data, station, forbidden_entity) supports_fluid = true end elseif entity.name == COMBINATOR_NAME then - local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - if control.operation == OPERATION_WAGON_MANIFEST then + local param = map_data.to_comb_params[entity.unit_number] + if param.operation == OPERATION_WAGON_MANIFEST then local pos = entity.position local is_there if is_ver then @@ -445,18 +445,6 @@ local function reset_station_layout(map_data, station, forbidden_entity) end end ----@param map_data MapData ----@param station Station ----@param allows_all_trains boolean -function set_station_train_class(map_data, station, allows_all_trains) - if station.allows_all_trains ~= allows_all_trains then - station.allows_all_trains = allows_all_trains - if not allows_all_trains then - reset_station_layout(map_data, station, nil) - end - end -end - ---@param map_data MapData ---@param station Station ---@param forbidden_entity LuaEntity? diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 4980bf7..744e0ec 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -1,5 +1,6 @@ --By Mami local flib_event = require("__flib__.event") +local floor = math.floor ---@param map_data MapData @@ -186,8 +187,8 @@ local function search_for_station_combinator(map_data, stop, comb_operation, com entity.valid and entity.name == COMBINATOR_NAME and entity ~= comb_forbidden and map_data.to_stop[entity.unit_number] == stop then - local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - if control.operation == comb_operation then + local param = get_comb_params(entity) + if param.operation == comb_operation then return entity end end @@ -243,13 +244,15 @@ local function on_combinator_built(map_data, comb) wire = defines.wire_type.red, }) - map_data.to_comb[comb.unit_number] = comb - map_data.to_output[comb.unit_number] = out - map_data.to_stop[comb.unit_number] = stop - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local param = control.parameters local op = param.operation + + map_data.to_comb[comb.unit_number] = comb + map_data.to_output[comb.unit_number] = out + map_data.to_stop[comb.unit_number] = stop + map_data.to_comb_params[comb.unit_number] = param + if op == OPERATION_DEFAULT then op = OPERATION_PRIMARY_IO param.operation = op @@ -334,8 +337,10 @@ end ---@param comb LuaEntity local function on_combinator_broken(map_data, comb) --NOTE: we do not check for wagon manifest combinators and update their stations, it is assumed they will be lazy deleted later - local out = map_data.to_output[comb.unit_number] - local stop = map_data.to_stop[comb.unit_number] + ---@type uint + local comb_id = comb.unit_number + local out = map_data.to_output[comb_id] + local stop = map_data.to_stop[comb_id] if stop and stop.valid then local station = map_data.stations[stop.unit_number] @@ -345,6 +350,7 @@ local function on_combinator_broken(map_data, comb) if comb1 then station.entity_comb1 = comb1 set_station_from_comb_state(station) + update_station_if_auto(map_data, station) else on_station_broken(map_data, stop.unit_number, station) local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) @@ -374,16 +380,47 @@ local function on_combinator_broken(map_data, comb) if out and out.valid then out.destroy() end - map_data.to_comb[comb.unit_number] = nil - map_data.to_output[comb.unit_number] = nil - map_data.to_stop[comb.unit_number] = nil + map_data.to_comb[comb_id] = nil + map_data.to_output[comb_id] = nil + map_data.to_stop[comb_id] = nil + map_data.to_comb_params[comb_id] = nil end + ---@param map_data MapData ---@param comb LuaEntity -function on_combinator_updated(map_data, comb) - --NOTE: this is the lazy way to implement updates and puts strong restrictions on data validity on on_combinator_broken - on_combinator_broken(map_data, comb) - on_combinator_built(map_data, comb) +---@param new_params ArithmeticCombinatorParameters +function on_combinator_updated(map_data, comb, new_params) + local old_params = map_data.to_comb_params[comb.unit_number] + if new_params.operation ~= old_params.operation then + on_combinator_broken(map_data, comb) + on_combinator_built(map_data, comb) + else + local new_signal = new_params.first_signal + local old_signal = old_params.first_signal + local new_network = new_signal and new_signal.name or nil + local old_network = old_signal and old_signal.name or nil + if (new_network ~= old_network) then + on_combinator_network_updated(map_data, comb, new_network) + end + if new_params.second_constant ~= old_params.second_constant then + local stop = global.to_stop[comb.unit_number] + if stop then + local station = global.stations[stop.unit_number] + if station then + local bits = new_params.second_constant + local is_pr_state = floor(bits/2)%3 + station.is_p = is_pr_state == 0 or is_pr_state == 1 + station.is_r = is_pr_state == 0 or is_pr_state == 2 + local allow_all_trains = bits%2 == 1 + if station.allow_all_trains ~= allow_all_trains then + station.allow_all_trains = allow_all_trains + update_station_if_auto(map_data, station) + end + end + end + end + map_data.to_comb_params[comb.unit_number] = new_params + end end ---@param map_data MapData @@ -403,8 +440,8 @@ local function on_stop_built(map_data, stop) for _, entity in pairs(entities) do if entity.valid and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then map_data.to_stop[entity.unit_number] = stop - local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - local op = control.operation + local param = get_comb_params(entity) + local op = param.operation if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then comb1 = entity elseif op == OPERATION_SECONDARY_IO then @@ -510,7 +547,7 @@ local function on_train_arrives_depot(map_data, depot, train_entity) else if train.manifest then on_failed_delivery(map_data, train) - send_lost_train_alert(train.entity) + send_unexpected_train_alert(train.entity) end train.status = STATUS_D add_available_train(map_data, depot, train_id) @@ -734,15 +771,43 @@ local function on_surface_removed(event) end end + local function on_paste(event) local entity = event.destination if not entity or not entity.valid then return end if entity.name == COMBINATOR_NAME then - on_combinator_updated(global, entity) + on_combinator_updated(global, entity, get_comb_params(entity)) end end +local function on_cursor_stack_changed(event) + local i = event.player_index + local player = game.get_player(i) + if not player then return end + local cursor = player.cursor_stack + player.play_sound({path = ALERT_SOUND}) + + if global.is_player_cursor_blueprint[i] then + --TODO: check if we can limit this search somehow? + for id, comb in pairs(global.to_comb) do + on_combinator_updated(global, comb, get_comb_params(comb)) + end + end + local contains_comb = nil + if cursor and cursor.valid_for_read and cursor.type == "blueprint" then + local cost_to_build = cursor.cost_to_build + for k, v in pairs(cost_to_build) do + if k == COMBINATOR_NAME then + contains_comb = true + break + end + end + end + global.is_player_cursor_blueprint[i] = contains_comb +end + + local function on_settings_changed(event) mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] @@ -792,6 +857,7 @@ local function main() flib_event.register({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed) flib_event.register(defines.events.on_entity_settings_pasted, on_paste) + flib_event.register(defines.events.on_player_cursor_stack_changed, on_cursor_stack_changed) local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nth_tick, function() diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index d854087..d6d0db7 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -31,6 +31,15 @@ local migrations_table = { map_data.all_station_ids = nil mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]] end, + ["0.4.0"] = function() + ---@type MapData + local map_data = global + map_data.is_player_cursor_blueprint = {} + map_data.to_comb_params = {} + for id, comb in pairs(map_data.to_comb) do + map_data.to_comb_params[id] = get_comb_params(comb) + end + end, } ---@param data ConfigurationChangedData From cf8676f49ce1ecbead750d9539030f88bc6f68a1 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 15 Nov 2022 18:31:18 -0500 Subject: [PATCH 49/66] removed debug alert --- cybersyn/scripts/main.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 744e0ec..7e5d410 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -786,7 +786,6 @@ local function on_cursor_stack_changed(event) local player = game.get_player(i) if not player then return end local cursor = player.cursor_stack - player.play_sound({path = ALERT_SOUND}) if global.is_player_cursor_blueprint[i] then --TODO: check if we can limit this search somehow? From 073725024e75f54a519656e78f7db41cd0128cc5 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 15 Nov 2022 18:51:56 -0500 Subject: [PATCH 50/66] improved localization --- cybersyn/locale/en/base.cfg | 6 +++--- cybersyn/scripts/factorio-api.lua | 5 +++-- cybersyn/scripts/layout.lua | 7 +++---- cybersyn/scripts/main.lua | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index de9e67f..bb4e17a 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -37,9 +37,9 @@ cybersyn-locked-slots=Locked slots per cargo wagon [cybersyn-messages] missing-trains=No trains available to make a delivery from __2__ to __1__ -lost-train=A train has become lost -nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty -unexpected-train=A train has returned to the depot before completing it's delivery +lost-train=A train from depot __1__ has become lost +nonempty-train=A train is being held in the depot because it still has cargo +unexpected-train=A train has returned to the depot before completing its delivery [cybersyn-gui] combinator-title=Cybernetic combinator diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 71bf86b..1134e81 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -219,14 +219,15 @@ end local send_lost_train_alert_icon = {name = LOST_TRAIN_NAME, type = "fluid"} ---@param train LuaTrain -function send_lost_train_alert(train) +---@param depot_name string +function send_lost_train_alert(train, depot_name) local loco = train.front_stock or train.back_stock if loco then for _, player in pairs(loco.force.players) do player.add_custom_alert( loco, send_lost_train_alert_icon, - {"cybersyn-messages.lost-train"}, + {"cybersyn-messages.lost-train", depot_name}, true) player.play_sound({path = ALERT_SOUND}) end diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 2c7423c..7e86c1a 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -22,10 +22,8 @@ end ---@param train Train ---@param train_id uint function remove_train(map_data, train, train_id) - map_data.trains[train_id] = nil - local depot = train.depot - if depot then - remove_available_train(map_data, depot) + if train.depot then + remove_available_train(map_data, train.depot) end local layout_id = train.layout_id local count = map_data.layout_train_count[layout_id] @@ -38,6 +36,7 @@ function remove_train(map_data, train, train_id) else map_data.layout_train_count[layout_id] = count - 1 end + map_data.trains[train_id] = nil end ---@param map_data MapData diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 7e5d410..125d464 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -162,7 +162,7 @@ local function on_station_broken(map_data, station_id, station) on_failed_delivery(map_data, train) train.entity.schedule = nil remove_train(map_data, train, train_id) - send_lost_train_alert(train.entity) + send_lost_train_alert(train.entity, train.depot_name) end end end @@ -609,7 +609,7 @@ local function on_train_arrives_buffer(map_data, stop, train) on_failed_delivery(map_data, train) remove_train(map_data, train, train.entity.id) train.entity.schedule = nil - send_lost_train_alert(train.entity) + send_lost_train_alert(train.entity, train.depot_name) end else --train is lost somehow, probably from player intervention From c93d4bdcf800bd53c0056f39453431eeb4024a14 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 15 Nov 2022 18:53:30 -0500 Subject: [PATCH 51/66] improved localization --- cybersyn/locale/en/base.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index bb4e17a..1c064f4 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -39,7 +39,7 @@ cybersyn-locked-slots=Locked slots per cargo wagon missing-trains=No trains available to make a delivery from __2__ to __1__ lost-train=A train from depot __1__ has become lost nonempty-train=A train is being held in the depot because it still has cargo -unexpected-train=A train has returned to the depot before completing its delivery +unexpected-train=A train has unexpectedly returned to the depot before completing its delivery [cybersyn-gui] combinator-title=Cybernetic combinator From 3a8e91b11db40087ca52f74b97d6f79d177c57ac Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sat, 19 Nov 2022 10:21:40 -0500 Subject: [PATCH 52/66] fixed copy-paste --- TODO | 4 +- cybersyn/changelog.txt | 2 +- cybersyn/scripts/central-planning.lua | 76 ++++++++++++++------------- cybersyn/scripts/constants.lua | 2 +- cybersyn/scripts/factorio-api.lua | 5 +- cybersyn/scripts/gui.lua | 2 +- cybersyn/scripts/main.lua | 69 +++++++++++++----------- 7 files changed, 84 insertions(+), 76 deletions(-) diff --git a/TODO b/TODO index ec7f13b..716e321 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,7 @@ close gui when the combinator is destroyed do not play close sound when a different gui is opened +gui can desync if settings are changed outside of it while it is open do hardcore testing models & art space elevator compat railloader compat -add missing items alert -lost train can be repurposed and rescheduled while alert is active -display when train is coming diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 9d73006..31d9eb8 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -21,7 +21,7 @@ Date: 2022-11-13 - Added warmup period on just built stations --------------------------------------------------------------------------------------------------- Version: 0.3.0 -Date: 2022-11-13 +Date: 2022-11-15 Features: - Fixed copy-paste - Added alert sounds diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 2da75b2..cf43714 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -33,7 +33,7 @@ function remove_manifest(map_data, station, manifest, sign) set_comb2(map_data, station) station.deliveries_total = station.deliveries_total - 1 if station.deliveries_total == 0 and station.entity_comb1.valid then - set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) + map_data.to_comb_params[station.entity_comb1.unit_number] = set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) end end @@ -220,10 +220,10 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p set_comb2(map_data, p_station) set_comb2(map_data, r_station) if p_station.entity_comb1.valid then - set_combinator_operation(p_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + map_data.to_comb_params[p_station.entity_comb1.unit_number] = set_combinator_operation(p_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) end if r_station.entity_comb1.valid then - set_combinator_operation(r_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + map_data.to_comb_params[r_station.entity_comb1.unit_number] = set_combinator_operation(r_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) end end @@ -275,7 +275,7 @@ local function tick_poll_station(map_data, mod_settings) station = map_data.stations[station_id] if station then if station.display_update then - update_combinator_display(station.entity_comb1, station.display_failed_request) + map_data.to_comb_params[station.entity_comb1.unit_number] = update_combinator_display(station.entity_comb1, station.display_failed_request) station.display_update = station.display_failed_request station.display_failed_request = nil end @@ -439,43 +439,45 @@ local function tick_dispatch(map_data, mod_settings) local item_type = tick_data.item_type local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] - local best = 0 - local best_depot = nil - local best_dist = INF - local highest_prior = -INF - local can_be_serviced = false - for j, p_station_id in ipairs(p_stations) do - local p_station = stations[p_station_id] - if p_station and p_station.p_count_or_r_threshold_per_item[item_name] >= r_threshold and p_station.deliveries_total < p_station.entity_stop.trains_limit then - local prior = p_station.priority - local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name)) - local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold) - if prior > highest_prior or (prior == highest_prior and d < best_dist) then - if depot then - best = j - best_dist = d - best_depot = depot - highest_prior = prior - can_be_serviced = true - elseif d < INF then - best = j - can_be_serviced = true + if r_threshold then + local best = 0 + local best_depot = nil + local best_dist = INF + local highest_prior = -INF + local can_be_serviced = false + for j, p_station_id in ipairs(p_stations) do + local p_station = stations[p_station_id] + if p_station and (p_station.p_count_or_r_threshold_per_item[item_name] or -1) >= r_threshold and p_station.deliveries_total < p_station.entity_stop.trains_limit then + local prior = p_station.priority + local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name)) + local depot, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold) + if prior > highest_prior or (prior == highest_prior and d < best_dist) then + if depot then + best = j + best_dist = d + best_depot = depot + highest_prior = prior + can_be_serviced = true + elseif d < INF then + best = j + can_be_serviced = true + end end end end - end - if - best_depot and ( - best_depot.entity_comb.status == defines.entity_status.working or - best_depot.entity_comb.status == defines.entity_status.low_power) - then - send_train_between(map_data, r_station_id, table_remove(p_stations--[[@as {}]], best), best_depot, item_name) - else - if can_be_serviced then - send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations--[[@as {}]][best]].entity_stop) + if + best_depot and ( + best_depot.entity_comb.status == defines.entity_status.working or + best_depot.entity_comb.status == defines.entity_status.low_power) + then + send_train_between(map_data, r_station_id, table_remove(p_stations--[[@as {}]], best), best_depot, item_name) + else + if can_be_serviced then + send_missing_train_alert_for_stops(r_station.entity_stop, stations[p_stations--[[@as {}]][best]].entity_stop) + end + r_station.display_failed_request = true + r_station.display_update = true end - r_station.display_failed_request = true - r_station.display_update = true end end return false diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 14406aa..a2f187e 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -15,7 +15,7 @@ ALERT_SOUND = "utility/console_message" OPERATION_DEFAULT = "*" OPERATION_PRIMARY_IO = "/" -OPERATION_PRIMARY_IO_REQUEST_FAILED = "^" +OPERATION_PRIMARY_IO_FAILED_REQUEST = "^" OPERATION_PRIMARY_IO_ACTIVE = "<<" OPERATION_SECONDARY_IO = "%" OPERATION_DEPOT = "+" diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 1134e81..4a3f54d 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -154,13 +154,14 @@ function update_combinator_display(comb, is_failed) local param = control.parameters if is_failed then if param.operation == OPERATION_PRIMARY_IO then - param.operation = OPERATION_PRIMARY_IO_REQUEST_FAILED + param.operation = OPERATION_PRIMARY_IO_FAILED_REQUEST control.parameters = param end - elseif param.operation == OPERATION_PRIMARY_IO_REQUEST_FAILED then + elseif param.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST then param.operation = OPERATION_PRIMARY_IO control.parameters = param end + return param end diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index b00e32b..5d490d9 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -44,7 +44,7 @@ function gui_opened(comb, player) switch_state = "right" end - if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then + if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then selected_index = 1 elseif op == OPERATION_SECONDARY_IO then selected_index = 2 diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 125d464..4ea2bea 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -248,21 +248,22 @@ local function on_combinator_built(map_data, comb) local param = control.parameters local op = param.operation - map_data.to_comb[comb.unit_number] = comb - map_data.to_output[comb.unit_number] = out - map_data.to_stop[comb.unit_number] = stop - map_data.to_comb_params[comb.unit_number] = param - if op == OPERATION_DEFAULT then op = OPERATION_PRIMARY_IO param.operation = op param.first_signal = NETWORK_SIGNAL_DEFAULT control.parameters = param - elseif op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then + elseif op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then op = OPERATION_PRIMARY_IO param.operation = op control.parameters = param end + + map_data.to_comb[comb.unit_number] = comb + map_data.to_output[comb.unit_number] = out + map_data.to_stop[comb.unit_number] = stop + map_data.to_comb_params[comb.unit_number] = param + if op == OPERATION_WAGON_MANIFEST then if rail then force_update_station_from_rail(map_data, rail, nil) @@ -392,35 +393,41 @@ end function on_combinator_updated(map_data, comb, new_params) local old_params = map_data.to_comb_params[comb.unit_number] if new_params.operation ~= old_params.operation then - on_combinator_broken(map_data, comb) - on_combinator_built(map_data, comb) - else - local new_signal = new_params.first_signal - local old_signal = old_params.first_signal - local new_network = new_signal and new_signal.name or nil - local old_network = old_signal and old_signal.name or nil - if (new_network ~= old_network) then - on_combinator_network_updated(map_data, comb, new_network) + if (new_params.operation == OPERATION_PRIMARY_IO_ACTIVE or new_params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST or new_params.operation == OPERATION_PRIMARY_IO) and (old_params.operation == OPERATION_PRIMARY_IO_ACTIVE or old_params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST or old_params.operation == OPERATION_PRIMARY_IO) then + set_combinator_operation(comb, old_params.operation) + new_params.operation = old_params.operation + else + --NOTE: This is rather dangerous, we may need to actually implement operation changing + on_combinator_broken(map_data, comb) + on_combinator_built(map_data, comb) + return end - if new_params.second_constant ~= old_params.second_constant then - local stop = global.to_stop[comb.unit_number] - if stop then - local station = global.stations[stop.unit_number] - if station then - local bits = new_params.second_constant - local is_pr_state = floor(bits/2)%3 - station.is_p = is_pr_state == 0 or is_pr_state == 1 - station.is_r = is_pr_state == 0 or is_pr_state == 2 - local allow_all_trains = bits%2 == 1 - if station.allow_all_trains ~= allow_all_trains then - station.allow_all_trains = allow_all_trains - update_station_if_auto(map_data, station) - end + end + local new_signal = new_params.first_signal + local old_signal = old_params.first_signal + local new_network = new_signal and new_signal.name or nil + local old_network = old_signal and old_signal.name or nil + if new_network ~= old_network then + on_combinator_network_updated(map_data, comb, new_network) + end + if new_params.second_constant ~= old_params.second_constant then + local stop = global.to_stop[comb.unit_number] + if stop then + local station = global.stations[stop.unit_number] + if station then + local bits = new_params.second_constant + local is_pr_state = floor(bits/2)%3 + station.is_p = is_pr_state == 0 or is_pr_state == 1 + station.is_r = is_pr_state == 0 or is_pr_state == 2 + local allow_all_trains = bits%2 == 1 + if station.allow_all_trains ~= allow_all_trains then + station.allow_all_trains = allow_all_trains + update_station_if_auto(map_data, station) end end end - map_data.to_comb_params[comb.unit_number] = new_params end + map_data.to_comb_params[comb.unit_number] = new_params end ---@param map_data MapData @@ -442,7 +449,7 @@ local function on_stop_built(map_data, stop) map_data.to_stop[entity.unit_number] = stop local param = get_comb_params(entity) local op = param.operation - if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then + if op == OPERATION_PRIMARY_IO then comb1 = entity elseif op == OPERATION_SECONDARY_IO then comb2 = entity From 99201beee841985cc34fe6cc116a74c04024a3f3 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Sun, 20 Nov 2022 09:19:51 -0500 Subject: [PATCH 53/66] added bugfix to train blacklisting --- TODO | 1 + cybersyn/changelog.txt | 7 ++++++- cybersyn/info.json | 2 +- cybersyn/prototypes/entity.lua | 2 +- cybersyn/scripts/main.lua | 10 +++++----- cybersyn/scripts/migrations.lua | 9 +++++++++ 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index 716e321..52bf5a6 100644 --- a/TODO +++ b/TODO @@ -5,3 +5,4 @@ do hardcore testing models & art space elevator compat railloader compat +major bug with copy-paste when the operation is changed by blueprint but it gets copied to the old settings before it's checked for update diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 31d9eb8..8640985 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -20,8 +20,13 @@ Date: 2022-11-13 Features: - Added warmup period on just built stations --------------------------------------------------------------------------------------------------- -Version: 0.3.0 +Version: 0.4.0 Date: 2022-11-15 Features: - Fixed copy-paste - Added alert sounds +--------------------------------------------------------------------------------------------------- +Version: 0.4.1 +Date: 2022-11-20 + Features: + - Bugfix with train blacklisting diff --git a/cybersyn/info.json b/cybersyn/info.json index 6343fd8..e27bf34 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.4.0", + "version": "0.4.1", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index 44ebb83..c5fd57c 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -12,7 +12,7 @@ combinator_entity.radius_visualisation_specification = { distance = 1.5, } combinator_entity.active_energy_usage = "10KW" -combinator_entity.allow_copy_paste = true +combinator_entity.allow_copy_paste = false diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 4ea2bea..a68b927 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -419,9 +419,9 @@ function on_combinator_updated(map_data, comb, new_params) local is_pr_state = floor(bits/2)%3 station.is_p = is_pr_state == 0 or is_pr_state == 1 station.is_r = is_pr_state == 0 or is_pr_state == 2 - local allow_all_trains = bits%2 == 1 - if station.allow_all_trains ~= allow_all_trains then - station.allow_all_trains = allow_all_trains + local allows_all_trains = bits%2 == 1 + if station.allows_all_trains ~= allows_all_trains then + station.allows_all_trains = allows_all_trains update_station_if_auto(map_data, station) end end @@ -862,8 +862,8 @@ local function main() flib_event.register({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed) - flib_event.register(defines.events.on_entity_settings_pasted, on_paste) - flib_event.register(defines.events.on_player_cursor_stack_changed, on_cursor_stack_changed) + --flib_event.register(defines.events.on_entity_settings_pasted, on_paste) + --flib_event.register(defines.events.on_player_cursor_stack_changed, on_cursor_stack_changed) local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nth_tick, function() diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index d6d0db7..1b0679c 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -40,6 +40,15 @@ local migrations_table = { map_data.to_comb_params[id] = get_comb_params(comb) end end, + ["0.4.1"] = function() + ---@type MapData + local map_data = global + map_data.tick_state = STATE_INIT + for id, station in pairs(map_data.stations) do + station.allows_all_trains = station.allow_all_trains or station.allows_all_trains + station.allow_all_trains = nil + end + end, } ---@param data ConfigurationChangedData From c7125a43f14c20fb87a46d1ad3d1ecde8286556a Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 22 Nov 2022 22:37:09 -0500 Subject: [PATCH 54/66] simplified api --- TODO | 1 + cybersyn/info.json | 2 +- cybersyn/scripts/central-planning.lua | 41 ++------ cybersyn/scripts/factorio-api.lua | 14 +-- cybersyn/scripts/global.lua | 16 +-- cybersyn/scripts/layout.lua | 5 +- cybersyn/scripts/main.lua | 137 +++++++++++++++----------- cybersyn/scripts/migrations.lua | 26 +++++ 8 files changed, 131 insertions(+), 111 deletions(-) diff --git a/TODO b/TODO index 52bf5a6..03b6a8a 100644 --- a/TODO +++ b/TODO @@ -6,3 +6,4 @@ models & art space elevator compat railloader compat major bug with copy-paste when the operation is changed by blueprint but it gets copied to the old settings before it's checked for update +catch inserter rotation diff --git a/cybersyn/info.json b/cybersyn/info.json index e27bf34..048d289 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.4.1", + "version": "0.4.2", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index cf43714..5f4ebb2 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -62,7 +62,7 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type, local valid_train_exists = false local is_fluid = item_type == "fluid" - local trains = map_data.trains_available[network_name] + local trains = map_data.available_trains[network_name] if trains then for train_id, depot_id in pairs(trains) do local depot = map_data.depots[depot_id] @@ -73,14 +73,14 @@ local function get_valid_train(map_data, r_station_id, p_station_id, item_type, local capacity = (is_fluid and train.fluid_capacity) or train.item_slot_capacity if capacity >= min_slots_to_move and - btest(netand, depot.network_flag) and + btest(netand, train.network_flag) and (r_station.allows_all_trains or r_station.accepted_layouts[layout_id]) and (p_station.allows_all_trains or p_station.accepted_layouts[layout_id]) then valid_train_exists = true --check if exists valid path --check if path is shortest so we prioritize locality - local d_to_p_dist = get_stop_dist(depot.entity_stop, p_station.entity_stop) - DEPOT_PRIORITY_MULT*depot.priority + local d_to_p_dist = get_stop_dist(depot.entity_stop, p_station.entity_stop) - DEPOT_PRIORITY_MULT*train.priority local dist = d_to_p_dist if capacity > best_capacity or (capacity == best_capacity and dist < best_dist) then @@ -110,9 +110,9 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p local economy = map_data.economy local r_station = map_data.stations[r_station_id] local p_station = map_data.stations[p_station_id] - local train = map_data.trains[depot.available_train] + local train = map_data.trains[depot.available_train_id] ---@type string - local network_name = depot.network_name + local network_name = r_station.network_name local manifest = {} @@ -210,7 +210,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p end end - remove_available_train(map_data, depot) + remove_available_train(map_data, train, depot) train.status = STATUS_D_TO_P train.p_station_id = p_station_id train.r_station_id = r_station_id @@ -227,33 +227,6 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p end end - ----@param map_data MapData -function poll_depot(map_data, depot) - local comb = depot.entity_comb - if depot.network_name then - depot.priority = 0 - depot.network_flag = 1 - local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) - if signals then - for k, v in pairs(signals) do - local item_name = v.signal.name - local item_count = v.count - if item_name then - if item_name == SIGNAL_PRIORITY then - depot.priority = item_count - end - if item_name == depot.network_name then - depot.network_flag = item_count - end - end - end - end - else - depot.network_flag = 0 - end -end - ---@param map_data MapData ---@param mod_settings CybersynModSettings local function tick_poll_station(map_data, mod_settings) @@ -494,7 +467,7 @@ function tick(map_data, mod_settings) for i, id in pairs(map_data.warmup_station_ids) do local station = map_data.stations[id] if station then - if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps >= map_data.total_ticks then + if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps >= map_data.total_ticks then--TODO: bug HERE map_data.active_station_ids[#map_data.active_station_ids + 1] = id map_data.warmup_station_ids[i] = nil end diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 4a3f54d..0752077 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -78,12 +78,6 @@ function get_comb_secondary_state(param) local bits = param.second_constant or 0 return bits%2 == 1, floor(bits/2)%3 end ----@param depot Depot -function set_depot_from_comb_state(depot) - local param = get_comb_params(depot.entity_comb) - local signal = param.first_signal - depot.network_name = signal and signal.name or nil -end ---@param station Station function set_station_from_comb_state(station) --NOTE: this does nothing to update currently active deliveries @@ -117,6 +111,14 @@ function set_comb_is_pr_state(comb, is_pr_state) return param end +---@param comb LuaEntity +function get_comb_network_name(comb) + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local signal = control.parameters.first_signal + + return signal and signal.name +end + ---@param comb LuaEntity ---@param signal SignalID? function set_comb_network_name(comb, signal) diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 9ab230c..60a4c63 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -12,7 +12,7 @@ ---@field public warmup_station_ids uint[] ---@field public depots {[uint]: Depot} ---@field public trains {[uint]: Train} ----@field public trains_available {[string]: {[uint]: uint}} --{[network_name]: {[train_id]: depot_id}} +---@field public available_trains {[string]: {[uint]: uint}} --{[network_name]: {[train_id]: depot_id}} ---@field public layouts {[uint]: string} ---@field public layout_train_count {[uint]: int} ---@field public tick_state uint @@ -43,25 +43,25 @@ ---@field public display_update true? ---@class Depot ----@field public priority int --transient ---@field public entity_stop LuaEntity ---@field public entity_comb LuaEntity ----@field public network_name string? ----@field public network_flag int --transient ----@field public available_train uint? +---@field public available_train_id uint?--train_id ---@class Train ---@field public entity LuaTrain ---@field public layout_id uint ---@field public item_slot_capacity int ---@field public fluid_capacity int ----@field public depot_name string ----@field public depot Depot? ---@field public status int ---@field public p_station_id uint ---@field public r_station_id uint ---@field public manifest Manifest ---@field public has_filtered_wagon boolean +---@field public depot_id uint? +---@field public depot_name string +---@field public network_name string +---@field public network_flag int +---@field public priority int ---@alias Manifest {}[] ---@alias TrainClass {[uint]: true} @@ -100,7 +100,7 @@ function init_global() global.warmup_station_ids = {} global.depots = {} global.trains = {} - global.trains_available = {} + global.available_trains = {} global.layouts = {} global.layout_train_count = {} global.layout_top_id = 1 diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 7e86c1a..479cc23 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -22,8 +22,9 @@ end ---@param train Train ---@param train_id uint function remove_train(map_data, train, train_id) - if train.depot then - remove_available_train(map_data, train.depot) + if train.depot_id then + local depot = map_data.depots[train.depot_id] + remove_available_train(map_data, train, depot) end local layout_id = train.layout_id local count = map_data.layout_train_count[layout_id] diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index a68b927..4754cc6 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -51,41 +51,63 @@ end ---@param map_data MapData ----@param depot Depot +---@param depot_id uint ---@param train_id uint -local function add_available_train(map_data, depot, train_id) - if depot.network_name then - local network = map_data.trains_available[depot.network_name] +local function add_available_train(map_data, depot_id, train_id) + local depot = map_data.depots[depot_id] + local train = map_data.trains[train_id] + local comb = depot.entity_comb + local network_name = get_comb_network_name(comb) + if network_name then + local network = map_data.available_trains[network_name] if not network then network = {} - map_data.trains_available[depot.network_name] = network + map_data.available_trains[network_name] = network end network[train_id] = depot.entity_stop.unit_number end - depot.available_train = train_id - local train = map_data.trains[train_id] + depot.available_train_id = train_id + train.depot_id = depot_id train.depot_name = depot.entity_stop.backer_name - train.depot = depot - poll_depot(map_data, depot) -end ----@param map_data MapData ----@param depot Depot -function remove_available_train(map_data, depot) - if depot.available_train then - if depot.network_name then - local network = map_data.trains_available[depot.network_name] - if network then - network[depot.available_train] = nil - if next(network) == nil then - map_data.trains_available[depot.network_name] = nil + train.network_name = network_name + train.network_flag = mod_settings.network_flag + train.priority = 0 + if network_name then + local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input) + if signals then + for k, v in pairs(signals) do + local item_name = v.signal.name + local item_count = v.count + if item_name then + if item_name == SIGNAL_PRIORITY then + train.priority = item_count + end + if item_name == network_name then + train.network_flag = item_count + end end end end - local train = map_data.trains[depot.available_train] - train.depot = nil - depot.available_train = nil end end +---@param map_data MapData +---@param train Train +---@param depot Depot +function remove_available_train(map_data, train, depot) + ---@type uint + local train_id = depot.available_train_id + if train.network_name then + local network = map_data.available_trains[train.network_name] + if network then + network[train_id] = nil + if next(network) == nil then + map_data.available_trains[train.network_name] = nil + end + end + end + train.depot_id = nil + depot.available_train_id = nil +end ---@param map_data MapData @@ -95,22 +117,21 @@ local function on_depot_built(map_data, stop, comb) local depot = { entity_stop = stop, entity_comb = comb, - --network_name = nil, - priority = 0, - network_flag = 0, + --available_train = nil, } map_data.depots[stop.unit_number] = depot - set_depot_from_comb_state(depot) - poll_depot(map_data, depot) end +---@param map_data MapData +---@param depot Depot local function on_depot_broken(map_data, depot) - --remove train - if depot.available_train then - --NOTE: we could remove the schedule from this train - --local train = map_data.trains[depot.available_train] - map_data.trains[depot.available_train] = nil - remove_available_train(map_data, depot) + local train_id = depot.available_train_id + if train_id then + local train = map_data.trains[train_id] + train.entity.schedule = nil + send_lost_train_alert(train.entity, depot.entity_stop.backer_name) + remove_available_train(map_data, train, depot) + map_data.trains[train_id] = nil end map_data.depots[depot.entity_stop.unit_number] = nil end @@ -319,16 +340,14 @@ function on_combinator_network_updated(map_data, comb, network_name) station.network_name = network_name end else - local depot = map_data.depots[stop.unit_number] - if depot.entity_comb == comb then - if depot.available_train then - ---@type uint - local train_id = depot.available_train - remove_available_train(map_data, depot) - depot.network_name = network_name - add_available_train(map_data, depot, train_id) - else - depot.network_name = network_name + local depot_id = stop.unit_number + local depot = map_data.depots[depot_id] + if depot and depot.entity_comb == comb then + local train_id = depot.available_train_id + if train_id then + local train = map_data.trains[train_id] + remove_available_train(map_data, train, depot) + add_available_train(map_data, depot_id, train_id) end end end @@ -369,8 +388,6 @@ local function on_combinator_broken(map_data, comb) local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) if depot_comb then depot.entity_comb = depot_comb - set_depot_from_comb_state(depot) - poll_depot(map_data, depot) else on_depot_broken(map_data, depot) end @@ -516,8 +533,8 @@ local function on_station_rename(map_data, stop) end else local depot = map_data.depots[station_id] - if depot and depot.available_train then - local train = map_data.trains[depot.available_train] + if depot and depot.available_train_id then + local train = map_data.trains[depot.available_train_id] train.depot_name = stop.backer_name end end @@ -537,9 +554,9 @@ local function find_and_add_all_stations_from_nothing(map_data) end ---@param map_data MapData ----@param depot Depot +---@param depot_id uint ---@param train_entity LuaTrain -local function on_train_arrives_depot(map_data, depot, train_entity) +local function on_train_arrives_depot(map_data, depot_id, train_entity) local contents = train_entity.get_contents() local train_id = train_entity.id local train = map_data.trains[train_id] @@ -550,14 +567,14 @@ local function on_train_arrives_depot(map_data, depot, train_entity) train.r_station_id = 0 train.manifest = nil train.status = STATUS_D - add_available_train(map_data, depot, train_id) + add_available_train(map_data, depot_id, train_id) else if train.manifest then on_failed_delivery(map_data, train) send_unexpected_train_alert(train.entity) end train.status = STATUS_D - add_available_train(map_data, depot, train_id) + add_available_train(map_data, depot_id, train_id) end if next(contents) ~= nil then --train still has cargo @@ -569,8 +586,6 @@ local function on_train_arrives_depot(map_data, depot, train_entity) end elseif next(contents) == nil then train = { - --depot_name = train_entity.station.backer_name, - --depot = depot, status = STATUS_D, entity = train_entity, layout_id = 0, @@ -582,7 +597,8 @@ local function on_train_arrives_depot(map_data, depot, train_entity) } update_train_layout(map_data, train) map_data.trains[train_id] = train - add_available_train(map_data, depot, train_id) + add_available_train(map_data, depot_id, train_id) + local schedule = create_depot_schedule(train.depot_name) train_entity.schedule = schedule else @@ -654,8 +670,9 @@ local function on_train_leaves_station(map_data, train) set_comb1(map_data, station, nil) unset_wagon_combs(map_data, station) end - elseif train.depot then - remove_available_train(map_data, train.depot) + elseif train.depot_id then + local depot = map_data.depots[train.depot_id] + remove_available_train(map_data, train, depot) end end @@ -752,9 +769,9 @@ local function on_train_changed(event) on_train_arrives_buffer(global, stop, train) end else - local depot = global.depots[stop.unit_number] - if depot then - on_train_arrives_depot(global, depot, train_e) + local depot_id = stop.unit_number + if global.depots[depot_id] then + on_train_arrives_depot(global, depot_id, train_e) end end end diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 1b0679c..db32a3e 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -49,6 +49,32 @@ local migrations_table = { station.allow_all_trains = nil end end, + ["0.4.2"] = function() + ---@type MapData + local map_data = global + map_data.tick_state = STATE_INIT + map_data.available_trains = map_data.trains_available + for id, train in pairs(map_data.trains) do + local depot = train.depot + if depot then + train.depot_id = depot.entity_comb.unit_number + train.network_name = depot.network_name + train.network_flag = depot.network_flag + train.priority = depot.priority + else + train.network_name = "" + train.network_flag = 0 + train.priority = 0 + end + end + for id, depot in pairs(map_data.depots) do + map_data.depots[id] = { + entity_comb = depot.entity_comb, + entity_stop = depot.entity_stop, + available_train_id = depot.available_train, + } + end + end, } ---@param data ConfigurationChangedData From 39235c62507725339c9e2a26045aa54bb6eb5a5b Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 22 Nov 2022 22:51:30 -0500 Subject: [PATCH 55/66] fixed warmup time --- cybersyn/scripts/central-planning.lua | 2 +- cybersyn/scripts/main.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 5f4ebb2..0492d3d 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -467,7 +467,7 @@ function tick(map_data, mod_settings) for i, id in pairs(map_data.warmup_station_ids) do local station = map_data.stations[id] if station then - if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps >= map_data.total_ticks then--TODO: bug HERE + if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps <= map_data.total_ticks then map_data.active_station_ids[#map_data.active_station_ids + 1] = id map_data.warmup_station_ids[i] = nil end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 4754cc6..19534b0 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -64,7 +64,7 @@ local function add_available_train(map_data, depot_id, train_id) network = {} map_data.available_trains[network_name] = network end - network[train_id] = depot.entity_stop.unit_number + network[train_id] = depot_id end depot.available_train_id = train_id train.depot_id = depot_id From 35a0994ae301cb87db942c0772d5b03976e2eb0f Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 22 Nov 2022 22:52:59 -0500 Subject: [PATCH 56/66] updated version --- cybersyn/changelog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 8640985..f7db7ad 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -30,3 +30,8 @@ Version: 0.4.1 Date: 2022-11-20 Features: - Bugfix with train blacklisting +--------------------------------------------------------------------------------------------------- +Version: 0.4.2 +Date: 2022-11-22 + Features: + - Bugfix with station warmup time From fe202642e76eec29dfbcef425a80250e515a9671 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 24 Nov 2022 17:38:32 -0500 Subject: [PATCH 57/66] added train timeout --- TODO | 2 ++ cybersyn/changelog.txt | 7 +++++++ cybersyn/info.json | 2 +- cybersyn/locale/en/base.cfg | 13 ++++++++----- cybersyn/scripts/central-planning.lua | 16 +++++++++++++++- cybersyn/scripts/factorio-api.lua | 17 +++++++++++++++++ cybersyn/scripts/global.lua | 2 ++ cybersyn/scripts/main.lua | 14 ++++++++++---- cybersyn/scripts/migrations.lua | 13 +++++++++++++ cybersyn/settings.lua | 9 +++++++++ 10 files changed, 84 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index 03b6a8a..1cb3fbb 100644 --- a/TODO +++ b/TODO @@ -7,3 +7,5 @@ space elevator compat railloader compat major bug with copy-paste when the operation is changed by blueprint but it gets copied to the old settings before it's checked for update catch inserter rotation +trains of capacity greater than the stations current contents can be dispatched + diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index f7db7ad..a687159 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -35,3 +35,10 @@ Version: 0.4.2 Date: 2022-11-22 Features: - Bugfix with station warmup time +--------------------------------------------------------------------------------------------------- +Version: 0.4.3 +Date: 2022-11-24 + Features: + - Added a stuck train alert + - Improved localization + - Fixed Bug with fluid cargo not being detected by depots diff --git a/cybersyn/info.json b/cybersyn/info.json index 048d289..7274d32 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.4.2", + "version": "0.4.3", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 1c064f4..0ce4490 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -1,14 +1,16 @@ [mod-setting-name] -cybersyn-ticks-per-second=Dispatcher updates per second +cybersyn-ticks-per-second=Central planning updates per second cybersyn-request-threshold=Default requester threshold cybersyn-network-flag=Default network flags -cybersyn-warmup-time=Station warmup time +cybersyn-warmup-time=Station warmup time (sec) +cybersyn-stuck-train-time=Stuck train timeout (sec) [mod-setting-description] -cybersyn-ticks-per-second=How many times per second the dispather should check for new deliveries. Deliveries are made one at a time per update. This value will be rounded up to a divisor of 60. -cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. Huge values will prevent stations from taking requests from the network unless an explicit threshold is set. +cybersyn-ticks-per-second=How many times per second the central planner should update the state of the network and schedule deliveries. Only one deliveries can be made per update. This value will be rounded up to a divisor of 60. +cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. When a station receives a negative item signal that surpasses its request threshold, so long as any station exists with a positive signal greater than the request threshold, a delivery of that item will be scheduled between the two stations. cybersyn-network-flag=The default set of networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible network flags to choose from. -cybersyn-warmup-time=How many seconds a cybernetic combinator will wait before connecting to the Cybersyn network. A grace period to modify or correct the circuit network before trains start dispatching to a newly blueprinted station. +cybersyn-warmup-time=How many seconds a cybernetic combinator will wait before connecting to the Cybersyn network. This is a grace period to modify or correct the circuit network before trains start dispatching to a newly blueprinted station. +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. [item-name] cybersyn-combinator=Cybernetic combinator @@ -40,6 +42,7 @@ missing-trains=No trains available to make a delivery from __2__ to __1__ lost-train=A train from depot __1__ has become lost nonempty-train=A train is being held in the depot because it still has cargo unexpected-train=A train has unexpectedly returned to the depot before completing its delivery +stuck-train=A train from depot __1__ is stuck [cybersyn-gui] combinator-title=Cybernetic combinator diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 0492d3d..5fd46f9 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -215,6 +215,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p train.p_station_id = p_station_id train.r_station_id = r_station_id train.manifest = manifest + train.last_manifest_tick = map_data.total_ticks train.entity.schedule = create_manifest_schedule(train.depot_name, p_station.entity_stop, r_station.entity_stop, manifest) set_comb2(map_data, p_station) @@ -227,6 +228,18 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p end end +---@param map_data MapData +---@param mod_settings CybersynModSettings +local function tick_poll_train(map_data, mod_settings) + local tick_data = map_data.tick_data + --NOTE: the following has undefined behavior if last_train is deleted, this should be ok since the following doesn't care how inconsistent our access pattern is + local train_id, train = next(map_data.trains, tick_data.last_train) + tick_data.last_train = train_id + + if train and train.manifest and train.last_manifest_tick + mod_settings.stuck_train_time*mod_settings.tps < map_data.total_ticks then + send_stuck_train_alert(train.entity, train.depot_name) + end +end ---@param map_data MapData ---@param mod_settings CybersynModSettings local function tick_poll_station(map_data, mod_settings) @@ -467,7 +480,7 @@ function tick(map_data, mod_settings) for i, id in pairs(map_data.warmup_station_ids) do local station = map_data.stations[id] if station then - if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps <= map_data.total_ticks then + if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps < map_data.total_ticks then map_data.active_station_ids[#map_data.active_station_ids + 1] = id map_data.warmup_station_ids[i] = nil end @@ -475,6 +488,7 @@ function tick(map_data, mod_settings) map_data.warmup_station_ids[i] = nil end end + tick_poll_train(map_data, mod_settings) end if map_data.tick_state == STATE_POLL_STATIONS then diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 0752077..6eca48d 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -266,3 +266,20 @@ function send_nonempty_train_in_depot_alert(train) end end end + + +local send_stuck_train_alert_icon = {name = LOST_TRAIN_NAME, type = "fluid"} +---@param train LuaTrain +---@param depot_name string +function send_stuck_train_alert(train, depot_name) + local loco = train.front_stock or train.back_stock + if loco then + for _, player in pairs(loco.force.players) do + player.add_custom_alert( + loco, + send_stuck_train_alert_icon, + {"cybersyn-messages.stuck-train", depot_name}, + true) + end + end +end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 60a4c63..a97228d 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -56,6 +56,7 @@ ---@field public p_station_id uint ---@field public r_station_id uint ---@field public manifest Manifest +---@field public last_manifest_tick int ---@field public has_filtered_wagon boolean ---@field public depot_id uint? ---@field public depot_name string @@ -78,6 +79,7 @@ ---@field public r_threshold int ---@field public network_flag int ---@field public warmup_time int +---@field public stuck_train_time int ---@type CybersynModSettings mod_settings = {} diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 19534b0..c729ebe 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -558,6 +558,8 @@ end ---@param train_entity LuaTrain local function on_train_arrives_depot(map_data, depot_id, train_entity) local contents = train_entity.get_contents() + local fluid_contents = train_entity.get_fluid_contents() + local is_train_empty = next(contents) == nil and next(fluid_contents) == nil local train_id = train_entity.id local train = map_data.trains[train_id] if train then @@ -576,15 +578,15 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) train.status = STATUS_D add_available_train(map_data, depot_id, train_id) end - if next(contents) ~= nil then + if is_train_empty then + train_entity.schedule = create_depot_schedule(train.depot_name) + else --train still has cargo train_entity.schedule = nil remove_train(map_data, train, train_id) send_nonempty_train_in_depot_alert(train_entity) - else - train_entity.schedule = create_depot_schedule(train.depot_name) end - elseif next(contents) == nil then + elseif is_train_empty then train = { status = STATUS_D, entity = train_entity, @@ -593,6 +595,7 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) fluid_capacity = 0, p_station_id = 0, r_station_id = 0, + last_manifest_tick = map_data.total_ticks, manifest = nil, } update_train_layout(map_data, train) @@ -602,6 +605,7 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) local schedule = create_depot_schedule(train.depot_name) train_entity.schedule = schedule else + train_entity.schedule = nil send_nonempty_train_in_depot_alert(train_entity) end end @@ -836,6 +840,7 @@ local function on_settings_changed(event) mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]] + mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as int]] if event.setting == "cybersyn-ticks-per-second" then local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nil) @@ -866,6 +871,7 @@ local function main() mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]] mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]] + mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as int]] --NOTE: There is a concern that it is possible to build or destroy important entities without one of these events being triggered, in which case the mod will have undefined behavior flib_event.register(defines.events.on_built_entity, on_built, filter_built) diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index db32a3e..32caeff 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -75,6 +75,19 @@ local migrations_table = { } end end, + ["0.4.3"] = function() + ---@type MapData + local map_data = global + map_data.tick_state = STATE_INIT + for id, station in pairs(map_data.stations) do + set_station_from_comb_state(station) + station.allow_all_trains = nil + end + for id, train in pairs(map_data.trains) do + train.last_manifest_tick = map_data.total_ticks + end + mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as int]] + end, } ---@param data ConfigurationChangedData diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index e60fd94..3f4e315 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -36,4 +36,13 @@ data:extend({ minimum_value = 0, maximum_value = 2147483647, }, + { + type = "int-setting", + name = "cybersyn-stuck-train-time", + order = "ad", + setting_type = "runtime-global", + default_value = 600, + minimum_value = 0, + maximum_value = 2147483647, + }, }) From d3733c59d7af4cd923ee4b5fc2c2f8a34c592131 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 24 Nov 2022 18:23:54 -0500 Subject: [PATCH 58/66] improved automatic train blacklist logic --- cybersyn/changelog.txt | 3 +- cybersyn/scripts/layout.lua | 66 ++++++++++++++++++------------------- cybersyn/scripts/main.lua | 2 +- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index a687159..d6e64db 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -41,4 +41,5 @@ Date: 2022-11-24 Features: - Added a stuck train alert - Improved localization - - Fixed Bug with fluid cargo not being detected by depots + - Fixed bug with fluid cargo not being detected by depots + - Greatly improved automatic train blacklist logic diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 479cc23..e418754 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -351,9 +351,11 @@ local function reset_station_layout(map_data, station, forbidden_entity) local type_filter = {"inserter", "pump", "arithmetic-combinator"} local wagon_number = 0 local pattern_length = 1 + local is_break = false for i = 1, 100 do local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_station, rail_connection_direction = defines.rail_connection_direction.straight}) if not rail or rail_connection_direction ~= defines.rail_connection_direction.straight or not rail.valid then + is_break = true break end pre_rail = rail @@ -433,7 +435,10 @@ local function reset_station_layout(map_data, station, forbidden_entity) search_area = area.move(search_area, area_delta) end end - layout_pattern = string_sub(layout_pattern, 1, pattern_length)..STATION_LAYOUT_NA.."*$" + layout_pattern = string_sub(layout_pattern, 1, pattern_length) + if is_break then + layout_pattern = layout_pattern..STATION_LAYOUT_NA.."*$" + end station.layout_pattern = layout_pattern local accepted_layouts = station.accepted_layouts for id, layout in pairs(map_data.layouts) do @@ -457,45 +462,40 @@ end ---@param map_data MapData ---@param rail LuaEntity ---@param forbidden_entity LuaEntity? -function force_update_station_from_rail(map_data, rail, forbidden_entity) - --NOTE: should we search further or better? it would be more expensive - local entity = rail.get_rail_segment_entity(defines.rail_direction.back, false) - if entity and entity.valid and entity.name == "train-stop" then - local station = map_data.stations[entity.unit_number] - if station then - reset_station_layout(map_data, station, forbidden_entity) +---@param force boolean? +function update_station_from_rail(map_data, rail, forbidden_entity, force) + --NOTE: is this a correct way to figure out the direction? + ---@type defines.rail_direction + local rail_direction = defines.rail_direction.back + local entity = rail.get_rail_segment_entity(rail_direction, false) + if not entity then + rail_direction = defines.rail_direction.front + entity = rail.get_rail_segment_entity(rail_direction, false) + end + for i = 1, 100 do + if not entity or not entity.valid then + return end - else - entity = rail.get_rail_segment_entity(defines.rail_direction.front, false) - if entity and entity.valid and entity.name == "train-stop" then + if entity.name == "train-stop" then local station = map_data.stations[entity.unit_number] if station then - reset_station_layout(map_data, station, forbidden_entity) - end - end - end -end ----@param map_data MapData ----@param rail LuaEntity ----@param forbidden_entity LuaEntity? -function update_station_from_rail(map_data, rail, forbidden_entity) - --NOTE: should we search further or better? it would be more expensive - local entity = rail.get_rail_segment_entity(defines.rail_direction.back, false) - if entity and entity.valid and entity.name == "train-stop" then - local station = map_data.stations[entity.unit_number] - if station then - update_station_if_auto(map_data, station, forbidden_entity) - end - else - entity = rail.get_rail_segment_entity(defines.rail_direction.front, false) - if entity and entity.valid and entity.name == "train-stop" then - local station = map_data.stations[entity.unit_number] - if station then - update_station_if_auto(map_data, station, forbidden_entity) + if force then + reset_station_layout(map_data, station, forbidden_entity) + else + update_station_if_auto(map_data, station, forbidden_entity) + end end + return end + + rail = rail.get_connected_rail({rail_direction = rail_direction, rail_connection_direction = defines.rail_connection_direction.straight})--[[@as LuaEntity]] + if not rail or not rail.valid then + return + end + entity = rail.get_rail_segment_entity(rail_direction, false) end end + ---@param map_data MapData ---@param pump LuaEntity ---@param forbidden_entity LuaEntity? diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index c729ebe..2c6f0f2 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -287,7 +287,7 @@ local function on_combinator_built(map_data, comb) if op == OPERATION_WAGON_MANIFEST then if rail then - force_update_station_from_rail(map_data, rail, nil) + update_station_from_rail(map_data, rail, nil, true) end elseif op == OPERATION_DEPOT then if stop then From dd948b0f178018a7c4f3cee2865afd00a276106b Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 25 Nov 2022 00:27:44 -0500 Subject: [PATCH 59/66] fixed layout pattern logic --- TODO | 3 -- cybersyn/changelog.txt | 5 +++ cybersyn/info.json | 2 +- cybersyn/scripts/global.lua | 4 +- cybersyn/scripts/layout.lua | 77 +++++++++++++++++++-------------- cybersyn/scripts/main.lua | 12 ++++- cybersyn/scripts/migrations.lua | 22 ++++++++++ 7 files changed, 86 insertions(+), 39 deletions(-) diff --git a/TODO b/TODO index 1cb3fbb..52bf5a6 100644 --- a/TODO +++ b/TODO @@ -6,6 +6,3 @@ models & art space elevator compat railloader compat major bug with copy-paste when the operation is changed by blueprint but it gets copied to the old settings before it's checked for update -catch inserter rotation -trains of capacity greater than the stations current contents can be dispatched - diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index d6e64db..58affb5 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -42,4 +42,9 @@ Date: 2022-11-24 - Added a stuck train alert - Improved localization - Fixed bug with fluid cargo not being detected by depots + +--------------------------------------------------------------------------------------------------- +Version: 0.4.4 +Date: 2022-11-25 + Features: - Greatly improved automatic train blacklist logic diff --git a/cybersyn/info.json b/cybersyn/info.json index 7274d32..88b9ef8 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.4.3", + "version": "0.4.4", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index a97228d..f6e072c 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -13,7 +13,7 @@ ---@field public depots {[uint]: Depot} ---@field public trains {[uint]: Train} ---@field public available_trains {[string]: {[uint]: uint}} --{[network_name]: {[train_id]: depot_id}} ----@field public layouts {[uint]: string} +---@field public layouts {[uint]: int[]} ---@field public layout_train_count {[uint]: int} ---@field public tick_state uint ---@field public tick_data {} @@ -36,7 +36,7 @@ ---@field public network_name string? ---@field public network_flag int --transient ---@field public accepted_layouts TrainClass ----@field public layout_pattern string? +---@field public layout_pattern {[uint]: int} ---@field public tick_signals {[uint]: Signal}? --transient ---@field public p_count_or_r_threshold_per_item {[string]: int} --transient ---@field public display_failed_request true? diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index e418754..18b3301 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -5,6 +5,7 @@ local floor = math.floor local ceil = math.ceil local string_find = string.find local string_sub = string.sub +local table_compare = table.compare local function iterr(a, i) i = i + 1 @@ -18,6 +19,26 @@ local function irpairs(a) end +local function is_layout_accepted(layout_pattern, layout) + local valid = true + for i, v in ipairs(layout) do + local p = layout_pattern[i] or 0 + if (v == 0 and p == 2) or (v == 1 and (p == 0 or p == 2)) or (v == 2 and (p == 0 or p == 1)) then + valid = false + break + end + end + if valid or not layout[0] then return valid end + for i, v in irpairs(layout) do + local p = layout_pattern[i] or 0 + if (v == 0 and p == 2) or (v == 1 and (p == 0 or p == 2)) or (v == 2 and (p == 0 or p == 1)) then + valid = false + break + end + end + return valid +end + ---@param map_data MapData ---@param train Train ---@param train_id uint @@ -40,32 +61,37 @@ function remove_train(map_data, train, train_id) map_data.trains[train_id] = nil end + ---@param map_data MapData ---@param train Train function update_train_layout(map_data, train) local carriages = train.entity.carriages - local layout = "" + local layout = {} local i = 1 local item_slot_capacity = 0 local fluid_capacity = 0 for _, carriage in pairs(carriages) do if carriage.type == "cargo-wagon" then - layout = layout..TRAIN_LAYOUT_CARGO + layout[#layout + 1] = 1 local inv = carriage.get_inventory(defines.inventory.cargo_wagon) item_slot_capacity = item_slot_capacity + #inv elseif carriage.type == "fluid-wagon" then - layout = layout..TRAIN_LAYOUT_FLUID + layout[#layout + 1] = 2 fluid_capacity = fluid_capacity + carriage.prototype.fluid_capacity - --elseif carriage.type == "artillery-wagon" then - --layout = layout..TRAIN_LAYOUT_ARTILLERY else - layout = layout..TRAIN_LAYOUT_NA + layout[#layout + 1] = 0 end i = i + 1 end + local back_movers = train.entity.locomotives["back_movers"] + if back_movers and #back_movers > 0 then + --mark the layout as reversible + layout[0] = true + end + local layout_id = 0 for id, cur_layout in pairs(map_data.layouts) do - if layout == cur_layout then + if table_compare(layout, cur_layout) then layout = cur_layout layout_id = id break @@ -79,8 +105,8 @@ function update_train_layout(map_data, train) map_data.layouts[layout_id] = layout map_data.layout_train_count[layout_id] = 1 for _, station in pairs(map_data.stations) do - if station.layout_pattern and string.find(layout, station.layout_pattern) ~= nil then - station.accepted_layouts[layout_id] = true + if station.layout_pattern then + station.accepted_layouts[layout_id] = is_layout_accepted(station.layout_pattern, layout) or nil end end else @@ -298,12 +324,12 @@ end ---@param map_data MapData ---@param station Station ---@param forbidden_entity LuaEntity? -local function reset_station_layout(map_data, station, forbidden_entity) +function reset_station_layout(map_data, station, forbidden_entity) --NOTE: station must be in auto mode local station_rail = station.entity_stop.connected_rail if station_rail == nil then --cannot accept deliveries - station.layout_pattern = "X" + station.layout_pattern = nil station.accepted_layouts = {} return end @@ -347,12 +373,10 @@ local function reset_station_layout(map_data, station, forbidden_entity) end local length = 2 local pre_rail = station_rail - local layout_pattern = "^" + local layout_pattern = {0} local type_filter = {"inserter", "pump", "arithmetic-combinator"} local wagon_number = 0 - local pattern_length = 1 - local is_break = false - for i = 1, 100 do + for i = 1, 112 do local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_station, rail_connection_direction = defines.rail_connection_direction.straight}) if not rail or rail_connection_direction ~= defines.rail_connection_direction.straight or not rail.valid then is_break = true @@ -421,32 +445,21 @@ local function reset_station_layout(map_data, station, forbidden_entity) if supports_cargo then if supports_fluid then - layout_pattern = layout_pattern..STATION_LAYOUT_ALL + layout_pattern[wagon_number] = 3 else - layout_pattern = layout_pattern..STATION_LAYOUT_NOT_FLUID + layout_pattern[wagon_number] = 2 end - pattern_length = #layout_pattern elseif supports_fluid then - layout_pattern = layout_pattern..STATION_LAYOUT_NOT_CARGO - pattern_length = #layout_pattern + layout_pattern[wagon_number] = 1 else - layout_pattern = layout_pattern..STATION_LAYOUT_NA + --layout_pattern[wagon_number] = nil end search_area = area.move(search_area, area_delta) end end - layout_pattern = string_sub(layout_pattern, 1, pattern_length) - if is_break then - layout_pattern = layout_pattern..STATION_LAYOUT_NA.."*$" - end station.layout_pattern = layout_pattern - local accepted_layouts = station.accepted_layouts for id, layout in pairs(map_data.layouts) do - if string_find(layout, layout_pattern) ~= nil then - accepted_layouts[id] = true - else - accepted_layouts[id] = nil - end + station.accepted_layouts[id] = is_layout_accepted(layout_pattern, layout) or nil end end @@ -472,7 +485,7 @@ function update_station_from_rail(map_data, rail, forbidden_entity, force) rail_direction = defines.rail_direction.front entity = rail.get_rail_segment_entity(rail_direction, false) end - for i = 1, 100 do + for i = 1, 112 do if not entity or not entity.valid then return end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 2c6f0f2..5dd5873 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -710,7 +710,7 @@ end local function on_built(event) - local entity = event.entity or event.created_entity or event.destination + local entity = event.entity or event.created_entity if not entity or not entity.valid then return end if entity.name == "train-stop" then @@ -746,6 +746,14 @@ local function on_broken(event) end end end +local function on_rotate(event) + local entity = event.entity or event.created_entity + if not entity or not entity.valid then return end + + if entity.type == "inserter" then + update_station_from_inserter(global, entity) + end +end local function on_rename(event) if event.entity.name == "train-stop" then on_station_rename(global, event.entity) @@ -878,6 +886,8 @@ local function main() flib_event.register(defines.events.on_robot_built_entity, on_built, filter_built) flib_event.register({defines.events.script_raised_built, defines.events.script_raised_revive, defines.events.on_entity_cloned}, on_built) + flib_event.register(defines.events.on_player_rotated_entity, on_rotate) + flib_event.register(defines.events.on_pre_player_mined_item, on_broken, filter_broken) flib_event.register(defines.events.on_robot_pre_mined, on_broken, filter_broken) flib_event.register(defines.events.on_entity_died, on_broken, filter_broken) diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 32caeff..8c1cbd4 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -88,6 +88,28 @@ local migrations_table = { end mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as int]] end, + ["0.4.4"] = function() + ---@type MapData + local map_data = global + map_data.tick_state = STATE_INIT + for id, layout in pairs(map_data.layouts) do + local new_layout = {} + local i = 1 + for c in string.gmatch(layout, ".") do + if c == "N" then + elseif c == "C" then + new_layout[i] = 1 + elseif c == "F" then + new_layout[i] = 2 + end + i = i + 1 + end + map_data.layouts[id] = new_layout + end + for id, station in pairs(map_data.stations) do + reset_station_layout(map_data, station) + end + end, } ---@param data ConfigurationChangedData From 78958f4f22dd541aff5bc1b3b1b03a1d4827f9ae Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 25 Nov 2022 07:01:33 -0500 Subject: [PATCH 60/66] blacklisted the network flag signal --- cybersyn/changelog.txt | 1 - cybersyn/scripts/central-planning.lua | 1 + cybersyn/settings.lua | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 58affb5..048ae83 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -42,7 +42,6 @@ Date: 2022-11-24 - Added a stuck train alert - Improved localization - Fixed bug with fluid cargo not being detected by depots - --------------------------------------------------------------------------------------------------- Version: 0.4.4 Date: 2022-11-25 diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 5fd46f9..dcff70e 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -300,6 +300,7 @@ local function tick_poll_station(map_data, mod_settings) end if item_name == station.network_name then station.network_flag = item_count + signals[k] = nil end else signals[k] = nil diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index 3f4e315..424bfd8 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -21,7 +21,7 @@ data:extend({ { type = "int-setting", name = "cybersyn-network-flag", - order = "ad", + order = "ac", setting_type = "runtime-global", default_value = 1, minimum_value = -2147483648, @@ -30,7 +30,7 @@ data:extend({ { type = "int-setting", name = "cybersyn-warmup-time", - order = "ac", + order = "ad", setting_type = "runtime-global", default_value = 20, minimum_value = 0, @@ -39,7 +39,7 @@ data:extend({ { type = "int-setting", name = "cybersyn-stuck-train-time", - order = "ad", + order = "ae", setting_type = "runtime-global", default_value = 600, minimum_value = 0, From be3f2f1d079b0fe79452d98c5face6bb147ed210 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 25 Nov 2022 14:20:37 -0500 Subject: [PATCH 61/66] added space elevator compat --- TODO | 1 + cybersyn/changelog.txt | 5 + cybersyn/info.json | 3 +- cybersyn/scripts/central-planning.lua | 9 +- cybersyn/scripts/constants.lua | 16 +-- cybersyn/scripts/factorio-api.lua | 133 +++++++++++++++++-- cybersyn/scripts/global.lua | 9 ++ cybersyn/scripts/layout.lua | 4 +- cybersyn/scripts/main.lua | 183 +++++++++++++++++++------- cybersyn/scripts/migrations.lua | 5 +- 10 files changed, 293 insertions(+), 75 deletions(-) diff --git a/TODO b/TODO index 52bf5a6..6bdfff0 100644 --- a/TODO +++ b/TODO @@ -6,3 +6,4 @@ models & art space elevator compat railloader compat major bug with copy-paste when the operation is changed by blueprint but it gets copied to the old settings before it's checked for update +go over init and make it agree with type info diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 048ae83..a61230a 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -47,3 +47,8 @@ Version: 0.4.4 Date: 2022-11-25 Features: - Greatly improved automatic train blacklist logic +--------------------------------------------------------------------------------------------------- +Version: 0.5.0 +Date: 2022-11-25 + Features: + - Added SE space elevator compat diff --git a/cybersyn/info.json b/cybersyn/info.json index 88b9ef8..6c19641 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,12 +1,13 @@ { "name": "cybersyn", - "version": "0.4.4", + "version": "0.5.0", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", "dependencies": [ "base", "flib >= 0.6.0", + "? space-exploration >= 0.6.90", "? miniloader" ] } diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index dcff70e..b61aeac 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -1,5 +1,4 @@ --By Mami -local get_distance = require("__flib__.misc").get_distance local min = math.min local max = math.max local abs = math.abs @@ -12,12 +11,6 @@ local table_sort = table.sort local random = math.random ----@param stop0 LuaEntity ----@param stop1 LuaEntity -local function get_stop_dist(stop0, stop1) - return get_distance(stop0.position, stop1.position) -end - ---@param map_data MapData ---@param station Station @@ -217,7 +210,7 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p train.manifest = manifest train.last_manifest_tick = map_data.total_ticks - train.entity.schedule = create_manifest_schedule(train.depot_name, p_station.entity_stop, r_station.entity_stop, manifest) + set_manifest_schedule(train.entity, depot.entity_stop, p_station.entity_stop, r_station.entity_stop, manifest) set_comb2(map_data, p_station) set_comb2(map_data, r_station) if p_station.entity_comb1.valid then diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index a2f187e..e76795d 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -34,17 +34,15 @@ STATUS_P_TO_R = 3 STATUS_R = 4 STATUS_R_TO_D = 5 -TRAIN_LAYOUT_NA = "N" -TRAIN_LAYOUT_CARGO = "C" -TRAIN_LAYOUT_FLUID = "F" ---TRAIN_LAYOUT_ARTILLERY = "A" -STATION_LAYOUT_NA = "N" -STATION_LAYOUT_ALL = "." -STATION_LAYOUT_NOT_FLUID = "[NC]" -STATION_LAYOUT_NOT_CARGO = "[NF]" - LONGEST_INSERTER_REACH = 2 STATE_INIT = 0 STATE_POLL_STATIONS = 1 STATE_DISPATCH = 2 + +DIFFERENT_SURFACE_DISTANCE = 1000000000 + +SE_ELEVATOR_STOP_PROTO_NAME = "se-space-elevator-train-stop" +SE_ELEVATOR_ORBIT_SUFFIX = " ↓" +SE_ELEVATOR_PLANET_SUFFIX = " ↑" +SE_ELEVATOR_SUFFIX_LENGTH = 4 diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 6eca48d..ca20e59 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -1,4 +1,5 @@ --By Mami +local get_distance = require("__flib__.misc").get_distance local abs = math.abs local floor = math.floor @@ -10,6 +11,13 @@ function get_stack_size(map_data, item_name) end +---@param stop0 LuaEntity +---@param stop1 LuaEntity +function get_stop_dist(stop0, stop1) + return get_distance(stop0.position, stop1.position) +end + + local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ---@param stop LuaEntity ---@param manifest Manifest @@ -47,29 +55,50 @@ end local create_direct_to_station_order_condition = {{type = "time", compare_type = "and", ticks = 1}} ---@param stop LuaEntity -local function create_direct_to_station_order(stop) - return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction,wait_conditions = create_direct_to_station_order_condition} +function create_direct_to_station_order(stop) + return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction, wait_conditions = create_direct_to_station_order_condition} end +---@param train LuaTrain ---@param depot_name string -function create_depot_schedule(depot_name) - return {current = 1, records = {create_inactivity_order(depot_name)}} +function set_depot_schedule(train, depot_name) + train.schedule = {current = 1, records = {create_inactivity_order(depot_name)}} end ----@param depot_name string +---@param train LuaTrain +function lock_train(train) + train.manual_mode = true +end +--[[ +---@param train LuaTrain +---@param depot_stop LuaEntity ---@param p_stop LuaEntity ---@param r_stop LuaEntity ---@param manifest Manifest -function create_manifest_schedule(depot_name, p_stop, r_stop, manifest) - return {current = 1, records = { - create_inactivity_order(depot_name), +function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) + train.schedule = {current = 1, records = { + create_inactivity_order(depot_stop.backer_name), create_direct_to_station_order(p_stop), create_loading_order(p_stop, manifest), create_direct_to_station_order(r_stop), create_unloading_order(r_stop), }} end - +]] +---@param train LuaTrain +---@param stop LuaEntity +---@param old_name string +function rename_manifest_schedule(train, stop, old_name) + local new_name = stop.backer_name + local schedule = train.schedule + if not schedule then return end + for i, record in ipairs(schedule.records) do + if record.station == old_name then + record.station = new_name + end + end + train.schedule = schedule +end function get_comb_params(comb) return comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] end @@ -283,3 +312,89 @@ function send_stuck_train_alert(train, depot_name) end end end + +--function se_create_placeholder_order() +--end + +---@param surface LuaSurface +local function se_get_space_elevator_name(surface) + --TODO: check how expensive the following is and potentially cache it's results + local entity = surface.find_entities_filtered({ + name = SE_ELEVATOR_STOP_PROTO_NAME, + type = "train-stop", + limit = 1, + })[1] + if entity and entity.valid then + return string.sub(entity.backer_name, 1, string.len(entity.backer_name) - SE_ELEVATOR_SUFFIX_LENGTH) + end +end + +---@param train LuaTrain +---@param depot_stop LuaEntity +---@param p_stop LuaEntity +---@param r_stop LuaEntity +---@param manifest Manifest +function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) + --NOTE: train must be on same surface as depot_stop + local d_surface = depot_stop.surface + local p_surface = p_stop.surface + local r_surface = r_stop.surface + local d_surface_i = d_surface.index + local p_surface_i = p_surface.index + local r_surface_i = r_surface.index + if d_surface_i == p_surface_i and p_surface_i == r_surface_i then + train.schedule = {current = 1, records = { + create_inactivity_order(depot_stop.backer_name), + create_direct_to_station_order(p_stop), + create_loading_order(p_stop, manifest), + create_direct_to_station_order(r_stop), + create_unloading_order(r_stop), + }} + return + elseif IS_SE_PRESENT and (d_surface_i == p_surface_i or p_surface_i == r_surface_i or r_surface_i == d_surface_i) then + local d_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = d_surface_i}) + local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = (d_surface_i == p_surface_i) and r_surface_i or p_surface_i}) + local is_train_in_orbit = other_zone.orbit_index == d_zone.index + if is_train_in_orbit or d_zone.orbit_index == other_zone.index then + local elevator_name = se_get_space_elevator_name(d_surface) + if elevator_name then + local records = {create_inactivity_order(depot_stop.backer_name)} + if d_surface_i == p_surface_i then + records[#records + 1] = create_direct_to_station_order(p_stop) + else + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + records[#records + 1] = create_loading_order(p_stop, manifest) + if p_surface_i ~= r_surface_i then + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + records[#records + 1] = create_unloading_order(r_stop) + if r_surface_i ~= d_surface_i then + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + + train.schedule = {current = 1, records = records} + return + end + end + end + --NOTE: create a schedule that cannot be fulfilled, the train will be stuck but it will give the player information what went wrong + train.schedule = {current = 1, records = { + create_inactivity_order(depot_stop.backer_name), + create_loading_order(p_stop, manifest), + create_unloading_order(r_stop), + }} + lock_train(train) + send_lost_train_alert(train, depot_stop.backer_name) +end + +---@param stop0 LuaEntity +---@param stop1 LuaEntity +function se_get_stop_dist(stop0, stop1) + local surface0 = stop0.surface.index + local surface1 = stop1.surface.index + return (surface0 == surface1 and get_distance(stop0.position, stop1.position) or DIFFERENT_SURFACE_DISTANCE) +end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index f6e072c..38f57dc 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -18,6 +18,7 @@ ---@field public tick_state uint ---@field public tick_data {} ---@field public economy Economy +---@field public se_tele_old_id {[any]: uint} ---@class Station ---@field public is_p boolean @@ -63,6 +64,8 @@ ---@field public network_name string ---@field public network_flag int ---@field public priority int +---@field public se_awaiting_removal any? +---@field public se_awaiting_rename any? ---@alias Manifest {}[] ---@alias TrainClass {[uint]: true} @@ -84,6 +87,8 @@ ---@type CybersynModSettings mod_settings = {} +IS_SE_PRESENT = remote.interfaces["space-exploration"] ~= nil + function init_global() global.total_ticks = 0 global.tick_state = STATE_INIT @@ -107,4 +112,8 @@ function init_global() global.layout_train_count = {} global.layout_top_id = 1 global.is_player_cursor_blueprint = {} + + if IS_SE_PRESENT then + global.se_tele_old_id = {} + end end diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 18b3301..c0cd134 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -43,7 +43,7 @@ end ---@param train Train ---@param train_id uint function remove_train(map_data, train, train_id) - if train.depot_id then + if train.status == STATUS_D then local depot = map_data.depots[train.depot_id] remove_available_train(map_data, train, depot) end @@ -84,7 +84,7 @@ function update_train_layout(map_data, train) i = i + 1 end local back_movers = train.entity.locomotives["back_movers"] - if back_movers and #back_movers > 0 then + if #back_movers > 0 then --mark the layout as reversible layout[0] = true end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 5dd5873..2cd2e3f 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -1,6 +1,7 @@ --By Mami local flib_event = require("__flib__.event") local floor = math.floor +local table_insert = table.insert ---@param map_data MapData @@ -67,6 +68,7 @@ local function add_available_train(map_data, depot_id, train_id) network[train_id] = depot_id end depot.available_train_id = train_id + train.status = STATUS_D train.depot_id = depot_id train.depot_name = depot.entity_stop.backer_name train.network_name = network_name @@ -105,7 +107,6 @@ function remove_available_train(map_data, train, depot) end end end - train.depot_id = nil depot.available_train_id = nil end @@ -128,7 +129,7 @@ local function on_depot_broken(map_data, depot) local train_id = depot.available_train_id if train_id then local train = map_data.trains[train_id] - train.entity.schedule = nil + lock_train(train.entity) send_lost_train_alert(train.entity, depot.entity_stop.backer_name) remove_available_train(map_data, train, depot) map_data.trains[train_id] = nil @@ -181,9 +182,13 @@ local function on_station_broken(map_data, station_id, station) if (is_r and not is_r_delivery_made) or (is_p and not is_p_delivery_made) then --train is attempting delivery to a stop that was destroyed, stop it on_failed_delivery(map_data, train) - train.entity.schedule = nil - remove_train(map_data, train, train_id) - send_lost_train_alert(train.entity, train.depot_name) + if train.entity then + remove_train(map_data, train, train_id) + lock_train(train.entity) + send_lost_train_alert(train.entity, train.depot_name) + else + train.se_awaiting_removal = train_id + end end end end @@ -510,7 +515,8 @@ local function on_stop_broken(map_data, stop) end ---@param map_data MapData ---@param stop LuaEntity -local function on_station_rename(map_data, stop) +---@param old_name string +local function on_station_rename(map_data, stop, old_name) --search for trains coming to the renamed station local station_id = stop.unit_number local station = map_data.stations[station_id] @@ -521,13 +527,21 @@ local function on_station_rename(map_data, stop) if is_p or is_r then local is_p_delivery_made = train.status ~= STATUS_D_TO_P and train.status ~= STATUS_P local is_r_delivery_made = train.status == STATUS_R_TO_D - if (is_r and not is_r_delivery_made) or (is_p and not is_p_delivery_made) then + if is_r and not is_r_delivery_made then + local r_station = map_data.stations[train.r_station_id] + if train.entity then + rename_manifest_schedule(train.entity, r_station.entity_stop, old_name) + elseif IS_SE_PRESENT then + train.se_awaiting_rename = {r_station.entity_stop, old_name} + end + elseif is_p and not is_p_delivery_made then --train is attempting delivery to a stop that was renamed local p_station = map_data.stations[train.p_station_id] - local r_station = map_data.stations[train.r_station_id] - local schedule = create_manifest_schedule(train.depot_name, p_station.entity_stop, r_station.entity_stop, train.manifest) - schedule.current = train.entity.schedule.current - train.entity.schedule = schedule + if train.entity then + rename_manifest_schedule(train.entity, p_station.entity_stop, old_name) + elseif IS_SE_PRESENT then + train.se_awaiting_rename = {p_station.entity_stop, old_name} + end end end end @@ -568,21 +582,19 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) train.p_station_id = 0 train.r_station_id = 0 train.manifest = nil - train.status = STATUS_D add_available_train(map_data, depot_id, train_id) else if train.manifest then on_failed_delivery(map_data, train) send_unexpected_train_alert(train.entity) end - train.status = STATUS_D add_available_train(map_data, depot_id, train_id) end if is_train_empty then - train_entity.schedule = create_depot_schedule(train.depot_name) + set_depot_schedule(train_entity, train.depot_name) else --train still has cargo - train_entity.schedule = nil + lock_train(train.entity) remove_train(map_data, train, train_id) send_nonempty_train_in_depot_alert(train_entity) end @@ -596,16 +608,15 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) p_station_id = 0, r_station_id = 0, last_manifest_tick = map_data.total_ticks, - manifest = nil, + --manifest = nil, } update_train_layout(map_data, train) map_data.trains[train_id] = train add_available_train(map_data, depot_id, train_id) - local schedule = create_depot_schedule(train.depot_name) - train_entity.schedule = schedule + set_depot_schedule(train_entity, train.depot_name) else - train_entity.schedule = nil + lock_train(train.entity) send_nonempty_train_in_depot_alert(train_entity) end end @@ -635,7 +646,7 @@ local function on_train_arrives_buffer(map_data, stop, train) else on_failed_delivery(map_data, train) remove_train(map_data, train, train.entity.id) - train.entity.schedule = nil + lock_train(train.entity) send_lost_train_alert(train.entity, train.depot_name) end else @@ -674,7 +685,7 @@ local function on_train_leaves_station(map_data, train) set_comb1(map_data, station, nil) unset_wagon_combs(map_data, station) end - elseif train.depot_id then + elseif train.status == STATUS_D then local depot = map_data.depots[train.depot_id] remove_available_train(map_data, train, depot) end @@ -684,11 +695,12 @@ end ---@param map_data MapData ---@param train Train local function on_train_broken(map_data, train) - if train.manifest then + --NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0 + if train.manifest and train.entity then on_failed_delivery(map_data, train) remove_train(map_data, train, train.entity.id) if train.entity.valid then - train.entity.schedule = nil + lock_train(train.entity) end end end @@ -697,13 +709,14 @@ end ---@param train_entity LuaEntity local function on_train_modified(map_data, pre_train_id, train_entity) local train = map_data.trains[pre_train_id] - if train then + --NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0 + if train and train.entity then if train.manifest then on_failed_delivery(map_data, train) end remove_train(map_data, train, pre_train_id) if train.entity.valid then - train.entity.schedule = nil + lock_train(train.entity) end end end @@ -756,7 +769,7 @@ local function on_rotate(event) end local function on_rename(event) if event.entity.name == "train-stop" then - on_station_rename(global, event.entity) + on_station_rename(global, event.entity, event.old_name) end end @@ -771,26 +784,25 @@ local function on_train_built(event) end local function on_train_changed(event) local train_e = event.train - if train_e.valid then - local train = global.trains[train_e.id] - if train_e.state == defines.train_state.wait_station then - local stop = train_e.station - if stop and stop.valid and stop.name == "train-stop" then - if global.stations[stop.unit_number] then - if train then - on_train_arrives_buffer(global, stop, train) - end - else - local depot_id = stop.unit_number - if global.depots[depot_id] then - on_train_arrives_depot(global, depot_id, train_e) - end + if not train_e.valid then return end + local train = global.trains[train_e.id] + if train_e.state == defines.train_state.wait_station then + local stop = train_e.station + if stop and stop.valid and stop.name == "train-stop" then + if global.stations[stop.unit_number] then + if train then + on_train_arrives_buffer(global, stop, train) + end + else + local depot_id = stop.unit_number + if global.depots[depot_id] then + on_train_arrives_depot(global, depot_id, train_e) end end - elseif event.old_state == defines.train_state.wait_station then - if train then - on_train_leaves_station(global, train) - end + end + elseif event.old_state == defines.train_state.wait_station then + if train then + on_train_leaves_station(global, train) end end end @@ -800,7 +812,7 @@ local function on_surface_removed(event) if surface then local train_stops = surface.find_entities_filtered({type = "train-stop"}) for _, entity in pairs(train_stops) do - if entity.name == "train-stop" then + if entity.valid and entity.name == "train-stop" then on_stop_broken(global, entity) end end @@ -915,6 +927,87 @@ local function main() flib_event.on_init(init_global) flib_event.on_configuration_changed(on_config_changed) + + + if IS_SE_PRESENT then + flib_event.on_load(function() + local se_on_train_teleport_finished_event = remote.call("space-exploration", "get_on_train_teleport_finished_event") + local se_on_train_teleport_started_event = remote.call("space-exploration", "get_on_train_teleport_started_event") + + + flib_event.register(se_on_train_teleport_started_event, function(event) + ---@type MapData + local map_data = global + local old_id = event.old_train_id_1 + local old_surface_index = event.old_surface_index + --NOTE: this is not guaranteed to be unique, it should be fine since the window of time for another train to mistakenly steal this train's event data is miniscule + --NOTE: please SE dev if you read this fix the issue where se_on_train_teleport_finished_event is returning the wrong old train id + local train_unique_identifier = event.train.front_stock.backer_name + + local train = map_data.trains[old_id] + if not train then return end + --NOTE: IMPORTANT, until se_on_train_teleport_finished_event is called map_data.trains[old_id] will reference an invalid train entity; very few of our events care about this and the ones that do should be impossible to trigger until teleportation is finished + train.entity = nil + map_data.se_tele_old_id[train_unique_identifier] = old_id + end) + flib_event.register(se_on_train_teleport_finished_event, function(event) + ---@type MapData + local map_data = global + ---@type LuaTrain + local train_entity = event.train + ---@type uint + local new_id = train_entity.id + local old_surface_index = event.old_surface_index + local train_unique_identifier = event.train.front_stock.backer_name + + --NOTE: event.old_train_id_1 from this event is useless, it's for one of the many transient trains SE spawns while teleporting the old train, only se_on_train_teleport_started_event returns the correct old train id + --NOTE: please SE dev if you read this fix the issue where se_on_train_teleport_finished_event is returning the wrong old train id + local old_id = map_data.se_tele_old_id[train_unique_identifier] + map_data.se_tele_old_id[train_unique_identifier] = nil + local train = map_data.trains[old_id] + if not train then return end + + map_data.trains[new_id] = train + map_data.trains[old_id] = nil + train.entity = train_entity + + if train.se_awaiting_removal then + remove_train(map_data, train, train.se_awaiting_removal) + lock_train(train.entity) + send_lost_train_alert(train.entity, train.depot_name) + return + elseif train.se_awaiting_rename then + rename_manifest_schedule(train.entity, train.se_awaiting_rename[1], train.se_awaiting_rename[2]) + train.se_awaiting_rename = nil + end + + if not (train.status == STATUS_D_TO_P or train.status == STATUS_P_TO_R) then return end + + local schedule = train_entity.schedule + if schedule then + local p_station = map_data.stations[train.p_station_id] + local p_name = p_station.entity_stop.backer_name + local p_surface_i = p_station.entity_stop.surface.index + local r_station = map_data.stations[train.r_station_id] + local r_name = r_station.entity_stop.backer_name + local r_surface_i = r_station.entity_stop.surface.index + local records = schedule.records + local i = schedule.current + while i <= #records do + if records[i].station == p_name and p_surface_i ~= old_surface_index then + table_insert(records, i, create_direct_to_station_order(p_station.entity_stop)) + i = i + 1 + elseif records[i].station == r_name and r_surface_i ~= old_surface_index then + table_insert(records, i, create_direct_to_station_order(r_station.entity_stop)) + i = i + 1 + end + i = i + 1 + end + train_entity.schedule = schedule + end + end) + end) + end end diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 8c1cbd4..6209f04 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -109,10 +109,13 @@ local migrations_table = { for id, station in pairs(map_data.stations) do reset_station_layout(map_data, station) end - end, + end } ---@param data ConfigurationChangedData function on_config_changed(data) flib_migration.on_config_changed(data, migrations_table) + if IS_SE_PRESENT and not global.se_tele_old_id then + global.se_tele_old_id = {} + end end From c4d8d3d21b1940d61e3f0f43ae6cfb21ec05ec87 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Fri, 25 Nov 2022 22:57:12 -0500 Subject: [PATCH 62/66] readded copy-paste --- TODO | 22 +- cybersyn/changelog.txt | 7 + cybersyn/info.json | 2 +- cybersyn/prototypes/entity.lua | 2 +- cybersyn/scripts/central-planning.lua | 26 +- cybersyn/scripts/factorio-api.lua | 368 +++++++++++++++----------- cybersyn/scripts/global.lua | 13 +- cybersyn/scripts/gui.lua | 50 +--- cybersyn/scripts/layout.lua | 18 +- cybersyn/scripts/main.lua | 86 +++--- cybersyn/scripts/migrations.lua | 36 ++- 11 files changed, 346 insertions(+), 284 deletions(-) diff --git a/TODO b/TODO index 6bdfff0..9dfd64e 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,13 @@ -close gui when the combinator is destroyed -do not play close sound when a different gui is opened -gui can desync if settings are changed outside of it while it is open -do hardcore testing -models & art -space elevator compat -railloader compat -major bug with copy-paste when the operation is changed by blueprint but it gets copied to the old settings before it's checked for update -go over init and make it agree with type info +bugs: + + +major: + do hardcore testing + models & art + railloader compat + +minor: + close gui when the combinator is destroyed + do not play close sound when a different gui is opened + gui can desync if settings are changed outside of it while it is open + go over init and make it agree with type info diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index a61230a..4491ea7 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -52,3 +52,10 @@ Version: 0.5.0 Date: 2022-11-25 Features: - Added SE space elevator compat +--------------------------------------------------------------------------------------------------- +Version: 0.5.1 +Date: 2022-11-25 + Features: + - Re-added combinator copy-paste + - Fixed bugs within train blacklist logic + - Fixed a crash diff --git a/cybersyn/info.json b/cybersyn/info.json index 6c19641..5ffb433 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.5.0", + "version": "0.5.1", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index c5fd57c..172a2df 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -12,7 +12,7 @@ combinator_entity.radius_visualisation_specification = { distance = 1.5, } combinator_entity.active_energy_usage = "10KW" -combinator_entity.allow_copy_paste = false +--combinator_entity.allow_copy_paste = false diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index b61aeac..9f915ee 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -26,7 +26,7 @@ function remove_manifest(map_data, station, manifest, sign) set_comb2(map_data, station) station.deliveries_total = station.deliveries_total - 1 if station.deliveries_total == 0 and station.entity_comb1.valid then - map_data.to_comb_params[station.entity_comb1.unit_number] = set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) + set_comb_operation_with_check(map_data, station.entity_comb1, OPERATION_PRIMARY_IO) end end @@ -214,10 +214,10 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p set_comb2(map_data, p_station) set_comb2(map_data, r_station) if p_station.entity_comb1.valid then - map_data.to_comb_params[p_station.entity_comb1.unit_number] = set_combinator_operation(p_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + set_comb_operation_with_check(map_data, p_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) end if r_station.entity_comb1.valid then - map_data.to_comb_params[r_station.entity_comb1.unit_number] = set_combinator_operation(r_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + set_comb_operation_with_check(map_data, r_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) end end @@ -229,11 +229,22 @@ local function tick_poll_train(map_data, mod_settings) local train_id, train = next(map_data.trains, tick_data.last_train) tick_data.last_train = train_id - if train and train.manifest and train.last_manifest_tick + mod_settings.stuck_train_time*mod_settings.tps < map_data.total_ticks then + if train and train.manifest and train.entity and train.last_manifest_tick + mod_settings.stuck_train_time*mod_settings.tps < map_data.total_ticks then send_stuck_train_alert(train.entity, train.depot_name) end end ---@param map_data MapData +local function tick_poll_comb(map_data) + local tick_data = map_data.tick_data + --NOTE: the following has undefined behavior if last_comb is deleted + local comb_id, comb = next(map_data.to_comb, tick_data.last_comb) + tick_data.last_comb = comb_id + + if comb and comb.valid then + combinator_update(map_data, comb) + end +end +---@param map_data MapData ---@param mod_settings CybersynModSettings local function tick_poll_station(map_data, mod_settings) local tick_data = map_data.tick_data @@ -254,7 +265,7 @@ local function tick_poll_station(map_data, mod_settings) station = map_data.stations[station_id] if station then if station.display_update then - map_data.to_comb_params[station.entity_comb1.unit_number] = update_combinator_display(station.entity_comb1, station.display_failed_request) + update_combinator_display(map_data, station.entity_comb1, station.display_failed_request) station.display_update = station.display_failed_request station.display_failed_request = nil end @@ -373,6 +384,7 @@ local function tick_dispatch(map_data, mod_settings) local name_i = size <= 2 and 2 or 2*random(size/2) local item_network_name = all_names[name_i - 1] local signal = all_names[name_i] + --swap remove all_names[name_i - 1] = all_names[size - 1] all_names[name_i] = all_names[size] @@ -384,7 +396,7 @@ local function tick_dispatch(map_data, mod_settings) if p_stations then tick_data.r_stations = r_stations tick_data.p_stations = p_stations - tick_data.item_name = signal.name + tick_data.item_name = signal.name--[[@as string]] tick_data.item_type = signal.type table_sort(r_stations, function(a_id, b_id) local a = stations[a_id] @@ -417,6 +429,7 @@ local function tick_dispatch(map_data, mod_settings) if r_station and r_station.deliveries_total < r_station.entity_stop.trains_limit then local item_name = tick_data.item_name local item_type = tick_data.item_type + --NOTE: the station at r_station_id could have been deleted and reregistered since last poll, this check here prevents it from being processed for a delivery in that case local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] if r_threshold then @@ -483,6 +496,7 @@ function tick(map_data, mod_settings) end end tick_poll_train(map_data, mod_settings) + tick_poll_comb(map_data) end if map_data.tick_state == STATE_POLL_STATIONS then diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index ca20e59..19d8897 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -14,10 +14,31 @@ end ---@param stop0 LuaEntity ---@param stop1 LuaEntity function get_stop_dist(stop0, stop1) - return get_distance(stop0.position, stop1.position) + local surface0 = stop0.surface.index + local surface1 = stop1.surface.index + return (surface0 == surface1 and get_distance(stop0.position, stop1.position) or DIFFERENT_SURFACE_DISTANCE) end +---@param surface LuaSurface +local function se_get_space_elevator_name(surface) + --TODO: check how expensive the following is and potentially cache it's results + local entity = surface.find_entities_filtered({ + name = SE_ELEVATOR_STOP_PROTO_NAME, + type = "train-stop", + limit = 1, + })[1] + if entity and entity.valid then + return string.sub(entity.backer_name, 1, string.len(entity.backer_name) - SE_ELEVATOR_SUFFIX_LENGTH) + end +end + + +------------------------------------------------------------------------------ +--[[train schedules]]-- +------------------------------------------------------------------------------ + + local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ---@param stop LuaEntity ---@param manifest Manifest @@ -69,22 +90,7 @@ end function lock_train(train) train.manual_mode = true end ---[[ ----@param train LuaTrain ----@param depot_stop LuaEntity ----@param p_stop LuaEntity ----@param r_stop LuaEntity ----@param manifest Manifest -function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) - train.schedule = {current = 1, records = { - create_inactivity_order(depot_stop.backer_name), - create_direct_to_station_order(p_stop), - create_loading_order(p_stop, manifest), - create_direct_to_station_order(r_stop), - create_unloading_order(r_stop), - }} -end -]] + ---@param train LuaTrain ---@param stop LuaEntity ---@param old_name string @@ -99,64 +105,230 @@ function rename_manifest_schedule(train, stop, old_name) end train.schedule = schedule end + +---@param train LuaTrain +---@param depot_stop LuaEntity +---@param p_stop LuaEntity +---@param r_stop LuaEntity +---@param manifest Manifest +function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) + --NOTE: train must be on same surface as depot_stop + local d_surface = depot_stop.surface + local p_surface = p_stop.surface + local r_surface = r_stop.surface + local d_surface_i = d_surface.index + local p_surface_i = p_surface.index + local r_surface_i = r_surface.index + if d_surface_i == p_surface_i and p_surface_i == r_surface_i then + train.schedule = {current = 1, records = { + create_inactivity_order(depot_stop.backer_name), + create_direct_to_station_order(p_stop), + create_loading_order(p_stop, manifest), + create_direct_to_station_order(r_stop), + create_unloading_order(r_stop), + }} + return + elseif IS_SE_PRESENT and (d_surface_i == p_surface_i or p_surface_i == r_surface_i or r_surface_i == d_surface_i) then + local d_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = d_surface_i}) + local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = (d_surface_i == p_surface_i) and r_surface_i or p_surface_i}) + local is_train_in_orbit = other_zone.orbit_index == d_zone.index + if is_train_in_orbit or d_zone.orbit_index == other_zone.index then + local elevator_name = se_get_space_elevator_name(d_surface) + if elevator_name then + local records = {create_inactivity_order(depot_stop.backer_name)} + if d_surface_i == p_surface_i then + records[#records + 1] = create_direct_to_station_order(p_stop) + else + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + records[#records + 1] = create_loading_order(p_stop, manifest) + if p_surface_i ~= r_surface_i then + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + records[#records + 1] = create_unloading_order(r_stop) + if r_surface_i ~= d_surface_i then + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + + train.schedule = {current = 1, records = records} + return + end + end + end + --NOTE: create a schedule that cannot be fulfilled, the train will be stuck but it will give the player information what went wrong + train.schedule = {current = 1, records = { + create_inactivity_order(depot_stop.backer_name), + create_loading_order(p_stop, manifest), + create_unloading_order(r_stop), + }} + lock_train(train) + send_lost_train_alert(train, depot_stop.backer_name) +end + + +------------------------------------------------------------------------------ +--[[combinators]]-- +------------------------------------------------------------------------------ + + +---@param comb LuaEntity +function get_comb_control(comb) + --NOTE: using this as opposed to get_comb_params gives you R/W access + return comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] +end +---@param comb LuaEntity function get_comb_params(comb) return comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] end ----@param param ArithmeticCombinatorParameters -function get_comb_secondary_state(param) - local bits = param.second_constant or 0 - return bits%2 == 1, floor(bits/2)%3 +---@param comb LuaEntity +function get_comb_gui_settings(comb) + local params = get_comb_params(comb) + local op = params.operation + + local selected_index = 0 + local switch_state = "none" + local bits = params.second_constant or 0 + local allows_all_trains = bits%2 == 1 + local is_pr_state = floor(bits/2)%3 + if is_pr_state == 0 then + switch_state = "none" + elseif is_pr_state == 1 then + switch_state = "left" + elseif is_pr_state == 2 then + switch_state = "right" + end + + if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then + selected_index = 1 + elseif op == OPERATION_SECONDARY_IO then + selected_index = 2 + elseif op == OPERATION_DEPOT then + selected_index = 3 + elseif op == OPERATION_WAGON_MANIFEST then + selected_index = 4 + end + return selected_index, params.first_signal, not allows_all_trains, switch_state +end +---@param comb LuaEntity +function get_comb_network_name(comb) + local params = get_comb_params(comb) + local signal = params.first_signal + + return signal and signal.name or nil end ---@param station Station function set_station_from_comb_state(station) --NOTE: this does nothing to update currently active deliveries - local param = get_comb_params(station.entity_comb1) - local bits = param.second_constant or 0 + local params = get_comb_params(station.entity_comb1) + local bits = params.second_constant or 0 local is_pr_state = floor(bits/2)%3 - local signal = param.first_signal + local signal = params.first_signal station.network_name = signal and signal.name or nil station.allows_all_trains = bits%2 == 1 station.is_p = is_pr_state == 0 or is_pr_state == 1 station.is_r = is_pr_state == 0 or is_pr_state == 2 end +---@param map_data MapData +---@param unit_number uint +---@param params ArithmeticCombinatorParameters +function has_comb_params_changed(map_data, unit_number, params) + local old_params = map_data.to_comb_params[unit_number] + + if params.operation ~= old_params.operation then + if (old_params.operation == OPERATION_PRIMARY_IO) and (params.operation == OPERATION_PRIMARY_IO_ACTIVE or params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST) then + else + return true + end + end + local new_signal = params.first_signal + local old_signal = old_params.first_signal + local new_network = new_signal and new_signal.name or nil + local old_network = old_signal and old_signal.name or nil + if new_network ~= old_network then + return true + end + if params.second_constant ~= old_params.second_constant then + return true + end + return false +end +---@param map_data MapData +---@param comb LuaEntity +---@param op string +function set_comb_operation_with_check(map_data, comb, op) + ---@type uint + local unit_number = comb.unit_number + local control = get_comb_control(comb) + local params = control.parameters + if not has_comb_params_changed(map_data, unit_number, params) then + params.operation = op + control.parameters = params + if (op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST) then + params.operation = OPERATION_PRIMARY_IO + end + map_data.to_comb_params[unit_number] = params + end +end +---@param map_data MapData +---@param comb LuaEntity +---@param is_failed boolean +function update_combinator_display(map_data, comb, is_failed) + ---@type uint + local unit_number = comb.unit_number + local control = get_comb_control(comb) + local params = control.parameters + if not has_comb_params_changed(map_data, unit_number, params) then + if is_failed then + if params.operation == OPERATION_PRIMARY_IO then + params.operation = OPERATION_PRIMARY_IO_FAILED_REQUEST + control.parameters = params + params.operation = OPERATION_PRIMARY_IO + map_data.to_comb_params[unit_number] = params + end + elseif params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST then + params.operation = OPERATION_PRIMARY_IO + control.parameters = params + map_data.to_comb_params[unit_number] = params + end + end +end ---@param comb LuaEntity ---@param allows_all_trains boolean function set_comb_allows_all_trains(comb, allows_all_trains) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = get_comb_control(comb) local param = control.parameters local bits = param.second_constant or 0 param.second_constant = (bits - bits%2) + (allows_all_trains and 1 or 0) control.parameters = param - return param end ---@param comb LuaEntity ---@param is_pr_state 0|1|2 function set_comb_is_pr_state(comb, is_pr_state) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = get_comb_control(comb) local param = control.parameters local bits = param.second_constant or 0 param.second_constant = (bits%2) + (2*is_pr_state) control.parameters = param - return param end - ----@param comb LuaEntity -function get_comb_network_name(comb) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local signal = control.parameters.first_signal - - return signal and signal.name -end - ---@param comb LuaEntity ---@param signal SignalID? function set_comb_network_name(comb, signal) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = get_comb_control(comb) local param = control.parameters param.first_signal = signal control.parameters = param - return param +end +---@param comb LuaEntity +---@param op string +function set_comb_operation(comb, op) + local control = get_comb_control(comb) + local params = control.parameters + params.operation = op + control.parameters = params end @@ -169,32 +341,6 @@ function set_combinator_output(map_data, comb, signals) out.get_or_create_control_behavior().parameters = signals end end ----@param comb LuaEntity ----@param op string -function set_combinator_operation(comb, op) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local param = control.parameters - param.operation = op - control.parameters = param - return param -end ----@param comb LuaEntity ----@param is_failed boolean -function update_combinator_display(comb, is_failed) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local param = control.parameters - if is_failed then - if param.operation == OPERATION_PRIMARY_IO then - param.operation = OPERATION_PRIMARY_IO_FAILED_REQUEST - control.parameters = param - end - elseif param.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST then - param.operation = OPERATION_PRIMARY_IO - control.parameters = param - end - return param -end - ---@param station Station function get_signals(station) @@ -236,6 +382,12 @@ function get_threshold(map_data, station, signal) return station.r_threshold end + +------------------------------------------------------------------------------ +--[[alerts]]-- +------------------------------------------------------------------------------ + + local send_missing_train_alert_for_stop_icon = {name = MISSING_TRAIN_NAME, type = "fluid"} ---@param r_stop LuaEntity ---@param p_stop LuaEntity @@ -312,89 +464,3 @@ function send_stuck_train_alert(train, depot_name) end end end - ---function se_create_placeholder_order() ---end - ----@param surface LuaSurface -local function se_get_space_elevator_name(surface) - --TODO: check how expensive the following is and potentially cache it's results - local entity = surface.find_entities_filtered({ - name = SE_ELEVATOR_STOP_PROTO_NAME, - type = "train-stop", - limit = 1, - })[1] - if entity and entity.valid then - return string.sub(entity.backer_name, 1, string.len(entity.backer_name) - SE_ELEVATOR_SUFFIX_LENGTH) - end -end - ----@param train LuaTrain ----@param depot_stop LuaEntity ----@param p_stop LuaEntity ----@param r_stop LuaEntity ----@param manifest Manifest -function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) - --NOTE: train must be on same surface as depot_stop - local d_surface = depot_stop.surface - local p_surface = p_stop.surface - local r_surface = r_stop.surface - local d_surface_i = d_surface.index - local p_surface_i = p_surface.index - local r_surface_i = r_surface.index - if d_surface_i == p_surface_i and p_surface_i == r_surface_i then - train.schedule = {current = 1, records = { - create_inactivity_order(depot_stop.backer_name), - create_direct_to_station_order(p_stop), - create_loading_order(p_stop, manifest), - create_direct_to_station_order(r_stop), - create_unloading_order(r_stop), - }} - return - elseif IS_SE_PRESENT and (d_surface_i == p_surface_i or p_surface_i == r_surface_i or r_surface_i == d_surface_i) then - local d_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = d_surface_i}) - local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = (d_surface_i == p_surface_i) and r_surface_i or p_surface_i}) - local is_train_in_orbit = other_zone.orbit_index == d_zone.index - if is_train_in_orbit or d_zone.orbit_index == other_zone.index then - local elevator_name = se_get_space_elevator_name(d_surface) - if elevator_name then - local records = {create_inactivity_order(depot_stop.backer_name)} - if d_surface_i == p_surface_i then - records[#records + 1] = create_direct_to_station_order(p_stop) - else - records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} - is_train_in_orbit = not is_train_in_orbit - end - records[#records + 1] = create_loading_order(p_stop, manifest) - if p_surface_i ~= r_surface_i then - records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} - is_train_in_orbit = not is_train_in_orbit - end - records[#records + 1] = create_unloading_order(r_stop) - if r_surface_i ~= d_surface_i then - records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} - is_train_in_orbit = not is_train_in_orbit - end - - train.schedule = {current = 1, records = records} - return - end - end - end - --NOTE: create a schedule that cannot be fulfilled, the train will be stuck but it will give the player information what went wrong - train.schedule = {current = 1, records = { - create_inactivity_order(depot_stop.backer_name), - create_loading_order(p_stop, manifest), - create_unloading_order(r_stop), - }} - lock_train(train) - send_lost_train_alert(train, depot_stop.backer_name) -end - ----@param stop0 LuaEntity ----@param stop1 LuaEntity -function se_get_stop_dist(stop0, stop1) - local surface0 = stop0.surface.index - local surface1 = stop1.surface.index - return (surface0 == surface1 and get_distance(stop0.position, stop1.position) or DIFFERENT_SURFACE_DISTANCE) -end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 38f57dc..d7fb57a 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -2,7 +2,6 @@ ---@class MapData ---@field public total_ticks uint ---@field public layout_top_id uint ----@field public is_player_cursor_blueprint {[uint]: true|nil} ---@field public to_comb {[uint]: LuaEntity} ---@field public to_comb_params {[uint]: ArithmeticCombinatorParameters} ---@field public to_output {[uint]: LuaEntity} @@ -36,7 +35,7 @@ ---@field public deliveries {[string]: int} ---@field public network_name string? ---@field public network_flag int --transient ----@field public accepted_layouts TrainClass +---@field public accepted_layouts {[uint]: true?} ---@field public layout_pattern {[uint]: int} ---@field public tick_signals {[uint]: Signal}? --transient ---@field public p_count_or_r_threshold_per_item {[string]: int} --transient @@ -61,21 +60,20 @@ ---@field public has_filtered_wagon boolean ---@field public depot_id uint? ---@field public depot_name string ----@field public network_name string +---@field public network_name string? ---@field public network_flag int ---@field public priority int ---@field public se_awaiting_removal any? ---@field public se_awaiting_rename any? ---@alias Manifest {}[] ----@alias TrainClass {[uint]: true} ---@alias cybersyn.global MapData ---@class Economy ---could contain invalid stations ----@field public all_r_stations {[string]: uint[]} --{[network_name:item_name]: count} ----@field public all_p_stations {[string]: uint[]} --{[network_name:item_name]: count} ----@field public all_names {[string]: uint[]} --{[network_name:item_name]: count} +---@field public all_r_stations {[string]: uint[]} --{[network_name:item_name]: station_id} +---@field public all_p_stations {[string]: uint[]} --{[network_name:item_name]: station_id} +---@field public all_names (string|SignalID)[] ---@class CybersynModSettings ---@field public tps int @@ -111,7 +109,6 @@ function init_global() global.layouts = {} global.layout_train_count = {} global.layout_top_id = 1 - global.is_player_cursor_blueprint = {} if IS_SE_PRESENT then global.se_tele_old_id = {} diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 5d490d9..6d661c4 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -30,29 +30,7 @@ STATUS_NAMES_DEFAULT = "entity-status.disabled" ---@param player LuaPlayer function gui_opened(comb, player) local rootgui = player.gui.screen - local param = get_comb_params(comb) - local op = param.operation - - local selected_index = 0 - local switch_state = "none" - local allows_all_trains, is_pr_state = get_comb_secondary_state(param) - if is_pr_state == 0 then - switch_state = "none" - elseif is_pr_state == 1 then - switch_state = "left" - elseif is_pr_state == 2 then - switch_state = "right" - end - - if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then - selected_index = 1 - elseif op == OPERATION_SECONDARY_IO then - selected_index = 2 - elseif op == OPERATION_DEPOT then - selected_index = 3 - elseif op == OPERATION_WAGON_MANIFEST then - selected_index = 4 - end + local selected_index, signal, check, switch_state = get_comb_gui_settings(comb) local window = flib_gui.build(rootgui, { {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={ @@ -94,10 +72,10 @@ function gui_opened(comb, player) {type="line", style_mods={top_padding=10}}, {type="label", name="network_label", ref={"network_label"}, style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=8}}, {type="flow", name="bottom", direction="horizontal", style_mods={vertical_align="center"}, children={ - {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=param.first_signal, style_mods={bottom_margin=1, right_margin=6}, actions={ + {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=signal, style_mods={bottom_margin=1, right_margin=6}, actions={ on_elem_changed={"choose-elem-button", comb.unit_number} }}, - {type="checkbox", name="radio_button", ref={"radio_button"}, state=not allows_all_trains, tooltip={"cybersyn-gui.auto-tooltip"}, actions={ + {type="checkbox", name="radio_button", ref={"radio_button"}, state=check, tooltip={"cybersyn-gui.auto-tooltip"}, actions={ on_checked_state_changed={"radio_button", comb.unit_number} }}, {type="label", name="radio_label", style_mods={left_padding=3}, ref={"radio_label"}, caption={"cybersyn-gui.auto-description"}}, @@ -164,28 +142,28 @@ function register_gui_actions() local bottom_flow = all_flow.bottom local param if element.selected_index == 1 then - param = set_combinator_operation(comb, OPERATION_PRIMARY_IO) + set_comb_operation(comb, OPERATION_PRIMARY_IO) top_flow["switch"].visible = true all_flow["network_label"].visible = true bottom_flow["network"].visible = true bottom_flow["radio_button"].visible = true bottom_flow["radio_label"].visible = true elseif element.selected_index == 2 then - param = set_combinator_operation(comb, OPERATION_SECONDARY_IO) + set_comb_operation(comb, OPERATION_SECONDARY_IO) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false bottom_flow["radio_button"].visible = false bottom_flow["radio_label"].visible = false elseif element.selected_index == 3 then - param = set_combinator_operation(comb, OPERATION_DEPOT) + set_comb_operation(comb, OPERATION_DEPOT) top_flow["switch"].visible = false all_flow["network_label"].visible = true bottom_flow["network"].visible = true bottom_flow["radio_button"].visible = false bottom_flow["radio_label"].visible = false elseif element.selected_index == 4 then - param = set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) + set_comb_operation(comb, OPERATION_WAGON_MANIFEST) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false @@ -195,7 +173,7 @@ function register_gui_actions() return end - on_combinator_updated(global, comb, param) + combinator_update(global, comb) elseif msg[1] == "choose-elem-button" then local element = event.element if not element then return end @@ -207,9 +185,9 @@ function register_gui_actions() signal = nil element.elem_value = nil end - local param = set_comb_network_name(comb, signal) + set_comb_network_name(comb, signal) - on_combinator_updated(global, comb, param) + combinator_update(global, comb) elseif msg[1] == "radio_button" then local element = event.element if not element then return end @@ -217,9 +195,9 @@ function register_gui_actions() if not comb or not comb.valid then return end local allows_all_trains = not element.state - local param = set_comb_allows_all_trains(comb, allows_all_trains) + set_comb_allows_all_trains(comb, allows_all_trains) - on_combinator_updated(global, comb, param) + combinator_update(global, comb) elseif msg[1] == "switch" then local element = event.element if not element then return end @@ -227,9 +205,9 @@ function register_gui_actions() if not comb or not comb.valid then return end local is_pr_state = (element.switch_state == "none" and 0) or (element.switch_state == "left" and 1) or 2 - local param = set_comb_is_pr_state(comb, is_pr_state) + set_comb_is_pr_state(comb, is_pr_state) - on_combinator_updated(global, comb, param) + combinator_update(global, comb) end end end) diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index c0cd134..6ac779f 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -5,7 +5,19 @@ local floor = math.floor local ceil = math.ceil local string_find = string.find local string_sub = string.sub -local table_compare = table.compare + + +local function table_compare(t0, t1) + if #t0 ~= #t1 then + return false + end + for i = 0, #t0 do + if t0[i] ~= t1[i] then + return false + end + end + return true +end local function iterr(a, i) i = i + 1 @@ -447,10 +459,10 @@ function reset_station_layout(map_data, station, forbidden_entity) if supports_fluid then layout_pattern[wagon_number] = 3 else - layout_pattern[wagon_number] = 2 + layout_pattern[wagon_number] = 1 end elseif supports_fluid then - layout_pattern[wagon_number] = 1 + layout_pattern[wagon_number] = 2 else --layout_pattern[wagon_number] = nil end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 2cd2e3f..a877c37 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -152,10 +152,8 @@ local function on_station_built(map_data, stop, comb1, comb2) priority = 0, r_threshold = 0, locked_slots = 0, - --network_name = param.first_signal and param.first_signal.name or nil, network_flag = 0, deliveries = {}, - --allows_all_trains = param.second_constant == 1, accepted_layouts = {}, layout_pattern = nil, p_count_or_r_threshold_per_item = {}, @@ -270,25 +268,25 @@ local function on_combinator_built(map_data, comb) wire = defines.wire_type.red, }) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local param = control.parameters - local op = param.operation + local control = get_comb_control(comb) + local params = control.parameters + local op = params.operation if op == OPERATION_DEFAULT then op = OPERATION_PRIMARY_IO - param.operation = op - param.first_signal = NETWORK_SIGNAL_DEFAULT - control.parameters = param - elseif op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then + params.operation = op + params.first_signal = NETWORK_SIGNAL_DEFAULT + control.parameters = params + elseif op ~= OPERATION_PRIMARY_IO and op ~= OPERATION_SECONDARY_IO and op ~= OPERATION_DEPOT and op ~= OPERATION_WAGON_MANIFEST then op = OPERATION_PRIMARY_IO - param.operation = op - control.parameters = param + params.operation = op + control.parameters = params end map_data.to_comb[comb.unit_number] = comb + map_data.to_comb_params[comb.unit_number] = params map_data.to_output[comb.unit_number] = out map_data.to_stop[comb.unit_number] = stop - map_data.to_comb_params[comb.unit_number] = param if op == OPERATION_WAGON_MANIFEST then if rail then @@ -360,7 +358,7 @@ function on_combinator_network_updated(map_data, comb, network_name) end ---@param map_data MapData ---@param comb LuaEntity -local function on_combinator_broken(map_data, comb) +function on_combinator_broken(map_data, comb) --NOTE: we do not check for wagon manifest combinators and update their stations, it is assumed they will be lazy deleted later ---@type uint local comb_id = comb.unit_number @@ -411,13 +409,17 @@ end ---@param map_data MapData ---@param comb LuaEntity ----@param new_params ArithmeticCombinatorParameters -function on_combinator_updated(map_data, comb, new_params) - local old_params = map_data.to_comb_params[comb.unit_number] - if new_params.operation ~= old_params.operation then - if (new_params.operation == OPERATION_PRIMARY_IO_ACTIVE or new_params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST or new_params.operation == OPERATION_PRIMARY_IO) and (old_params.operation == OPERATION_PRIMARY_IO_ACTIVE or old_params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST or old_params.operation == OPERATION_PRIMARY_IO) then - set_combinator_operation(comb, old_params.operation) - new_params.operation = old_params.operation +function combinator_update(map_data, comb) + ---@type uint + local unit_number = comb.unit_number + local control = get_comb_control(comb) + local params = control.parameters + local old_params = map_data.to_comb_params[unit_number] + + if params.operation ~= old_params.operation then + if (old_params.operation == OPERATION_PRIMARY_IO) and (params.operation == OPERATION_PRIMARY_IO_ACTIVE or params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST) then + --make sure only OPERATION_PRIMARY_IO gets stored on map_data.to_comb_params + params.operation = OPERATION_PRIMARY_IO else --NOTE: This is rather dangerous, we may need to actually implement operation changing on_combinator_broken(map_data, comb) @@ -425,19 +427,20 @@ function on_combinator_updated(map_data, comb, new_params) return end end - local new_signal = new_params.first_signal + local new_signal = params.first_signal local old_signal = old_params.first_signal local new_network = new_signal and new_signal.name or nil local old_network = old_signal and old_signal.name or nil if new_network ~= old_network then on_combinator_network_updated(map_data, comb, new_network) + map_data.to_comb_params[unit_number] = params end - if new_params.second_constant ~= old_params.second_constant then + if params.second_constant ~= old_params.second_constant then local stop = global.to_stop[comb.unit_number] if stop then local station = global.stations[stop.unit_number] if station then - local bits = new_params.second_constant + local bits = params.second_constant local is_pr_state = floor(bits/2)%3 station.is_p = is_pr_state == 0 or is_pr_state == 1 station.is_r = is_pr_state == 0 or is_pr_state == 2 @@ -448,8 +451,8 @@ function on_combinator_updated(map_data, comb, new_params) end end end + map_data.to_comb_params[unit_number] = params end - map_data.to_comb_params[comb.unit_number] = new_params end ---@param map_data MapData @@ -594,7 +597,7 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) set_depot_schedule(train_entity, train.depot_name) else --train still has cargo - lock_train(train.entity) + lock_train(train_entity) remove_train(map_data, train, train_id) send_nonempty_train_in_depot_alert(train_entity) end @@ -616,7 +619,7 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) set_depot_schedule(train_entity, train.depot_name) else - lock_train(train.entity) + lock_train(train_entity) send_nonempty_train_in_depot_alert(train_entity) end end @@ -825,35 +828,10 @@ local function on_paste(event) if not entity or not entity.valid then return end if entity.name == COMBINATOR_NAME then - on_combinator_updated(global, entity, get_comb_params(entity)) + combinator_update(global, entity) end end -local function on_cursor_stack_changed(event) - local i = event.player_index - local player = game.get_player(i) - if not player then return end - local cursor = player.cursor_stack - - if global.is_player_cursor_blueprint[i] then - --TODO: check if we can limit this search somehow? - for id, comb in pairs(global.to_comb) do - on_combinator_updated(global, comb, get_comb_params(comb)) - end - end - local contains_comb = nil - if cursor and cursor.valid_for_read and cursor.type == "blueprint" then - local cost_to_build = cursor.cost_to_build - for k, v in pairs(cost_to_build) do - if k == COMBINATOR_NAME then - contains_comb = true - break - end - end - end - global.is_player_cursor_blueprint[i] = contains_comb -end - local function on_settings_changed(event) mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] @@ -907,8 +885,7 @@ local function main() flib_event.register({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed) - --flib_event.register(defines.events.on_entity_settings_pasted, on_paste) - --flib_event.register(defines.events.on_player_cursor_stack_changed, on_cursor_stack_changed) + flib_event.register(defines.events.on_entity_settings_pasted, on_paste) local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nth_tick, function() @@ -939,7 +916,6 @@ local function main() ---@type MapData local map_data = global local old_id = event.old_train_id_1 - local old_surface_index = event.old_surface_index --NOTE: this is not guaranteed to be unique, it should be fine since the window of time for another train to mistakenly steal this train's event data is miniscule --NOTE: please SE dev if you read this fix the issue where se_on_train_teleport_finished_event is returning the wrong old train id local train_unique_identifier = event.train.front_stock.backer_name diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 6209f04..163378b 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -2,20 +2,6 @@ local flib_migration = require("__flib__.migration") local migrations_table = { - ["0.2.0"] = function() - ---@type MapData - local map_data = global - map_data.tick_state = STATE_INIT - map_data.all_station_ids = {} - for id, station in pairs(map_data.stations) do - station.p_count_or_r_threshold_per_item = {} - station.p_threshold = nil - station.is_all = nil - set_station_from_comb_state(station) - set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) - map_data.all_station_ids[#map_data.all_station_ids + 1] = id - end - end, ["0.2.1"] = function() ---@type MapData local map_data = global @@ -106,6 +92,28 @@ local migrations_table = { end map_data.layouts[id] = new_layout end + end, + ["0.5.1"] = function() + ---@type MapData + local map_data = global + map_data.tick_state = STATE_INIT + map_data.is_player_cursor_blueprint = nil + for id, layout in pairs(map_data.layouts) do + local new_layout = {} + local max_i = 0 + for i, v in pairs(layout) do + new_layout[i] = v + if i > max_i then + max_i = i + end + end + for i = 1, max_i do + if new_layout[i] == nil then + new_layout[i] = 0 + end + end + map_data.layouts[id] = new_layout + end for id, station in pairs(map_data.stations) do reset_station_layout(map_data, station) end From 090c60e627a2be65ff9e34dda09dbe06853a16ca Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 29 Nov 2022 15:12:28 -0500 Subject: [PATCH 63/66] prepared for 1.0.0 --- TODO | 4 ++-- cybersyn/locale/en/base.cfg | 6 ++++-- cybersyn/scripts/constants.lua | 1 + cybersyn/scripts/factorio-api.lua | 4 ++-- cybersyn/scripts/main.lua | 4 +++- cybersyn/settings.lua | 25 +++++++++++++++++-------- 6 files changed, 29 insertions(+), 15 deletions(-) diff --git a/TODO b/TODO index 9dfd64e..afa49f5 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,12 @@ bugs: - + request threshold is being bypassed somehow major: do hardcore testing models & art - railloader compat minor: + railloader compat close gui when the combinator is destroyed do not play close sound when a different gui is opened gui can desync if settings are changed outside of it while it is open diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index 0ce4490..ba5eecf 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -1,14 +1,16 @@ [mod-setting-name] cybersyn-ticks-per-second=Central planning updates per second +cybersyn-wait-time=Required inactivity duration (sec) cybersyn-request-threshold=Default requester threshold -cybersyn-network-flag=Default network flags +cybersyn-network-flag=Default sub-network flags cybersyn-warmup-time=Station warmup time (sec) cybersyn-stuck-train-time=Stuck train timeout (sec) [mod-setting-description] cybersyn-ticks-per-second=How many times per second the central planner should update the state of the network and schedule deliveries. Only one deliveries can be made per update. This value will be rounded up to a divisor of 60. +cybersyn-wait-time=How many seconds the duration of a train's inactivity order will be set to. Trains will be forced to wait in the station or depot until this amount of time has passed without the train receiving any kind of interaction. Non-zero values prevent inserters from getting stuck and prevent trains from leaving depots without refueling. Decimal values are allowed, they will be rounded up to a multiple of 1/60 (0.01667). cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. When a station receives a negative item signal that surpasses its request threshold, so long as any station exists with a positive signal greater than the request threshold, a delivery of that item will be scheduled between the two stations. -cybersyn-network-flag=The default set of networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible network flags to choose from. +cybersyn-network-flag=The default set of sub-networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible sub-network flags to choose from. cybersyn-warmup-time=How many seconds a cybernetic combinator will wait before connecting to the Cybersyn network. This is a grace period to modify or correct the circuit network before trains start dispatching to a newly blueprinted station. 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. diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index e76795d..869cf7d 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -22,6 +22,7 @@ OPERATION_DEPOT = "+" OPERATION_WAGON_MANIFEST = "-" NETWORK_SIGNAL_DEFAULT = {name="signal-A", type="virtual"} +INACTIVITY_TIME = 60 DELTA = 1/2048 diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 19d8897..6f64eb4 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -39,7 +39,7 @@ end ------------------------------------------------------------------------------ -local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} +local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = INACTIVITY_TIME} ---@param stop LuaEntity ---@param manifest Manifest function create_loading_order(stop, manifest) @@ -68,7 +68,7 @@ function create_unloading_order(stop) return {station = stop.backer_name, wait_conditions = create_unloading_order_condition} end -local create_inactivity_order_condition = {{type = "inactivity", compare_type = "and", ticks = 120}} +local create_inactivity_order_condition = {{type = "inactivity", compare_type = "and", ticks = INACTIVITY_TIME}} ---@param depot_name string function create_inactivity_order(depot_name) return {station = depot_name, wait_conditions = create_inactivity_order_condition} diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index a877c37..24907d7 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -645,7 +645,9 @@ local function on_train_arrives_buffer(map_data, stop, train) set_r_wagon_combs(map_data, station, train) end elseif train.status == STATUS_P and train.p_station_id == station_id then - elseif train.status == STATUS_R and train.r_station_id == station_id then + --this player intervention that is considered valid + elseif (train.status == STATUS_R or train.status == STATUS_R_TO_D) and train.r_station_id == station_id then + --this player intervention that is considered valid else on_failed_delivery(map_data, train) remove_train(map_data, train, train.entity.id) diff --git a/cybersyn/settings.lua b/cybersyn/settings.lua index 424bfd8..60bbaa6 100644 --- a/cybersyn/settings.lua +++ b/cybersyn/settings.lua @@ -9,28 +9,37 @@ data:extend({ minimum_value = 1, maximum_value = 60, }, + --{ + -- type = "int-setting", + -- name = "cybersyn-wait-time", + -- order = "ab", + -- setting_type = "runtime-global", + -- default_value = 2000, + -- minimum_value = 1, + -- maximum_value = 2147483647, + --}, { - type = "int-setting", + type = "double-setting", name = "cybersyn-request-threshold", - order = "ab", + order = "ac", setting_type = "runtime-global", - default_value = 2000, - minimum_value = 1, + default_value = 1, + minimum_value = 0, maximum_value = 2147483647, }, { type = "int-setting", name = "cybersyn-network-flag", - order = "ac", + order = "ad", setting_type = "runtime-global", - default_value = 1, + default_value = -1, minimum_value = -2147483648, maximum_value = 2147483647, }, { type = "int-setting", name = "cybersyn-warmup-time", - order = "ad", + order = "ae", setting_type = "runtime-global", default_value = 20, minimum_value = 0, @@ -39,7 +48,7 @@ data:extend({ { type = "int-setting", name = "cybersyn-stuck-train-time", - order = "ae", + order = "af", setting_type = "runtime-global", default_value = 600, minimum_value = 0, From d156a2e14d6bbb844932aac46c4001d2ec032e7a Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 29 Nov 2022 16:24:58 -0500 Subject: [PATCH 64/66] updated sprites --- cybersyn/changelog.txt | 8 +- .../combinator/combinator-displays.png | Bin 4690 -> 0 bytes .../combinator/cybernetic-displays.png | Bin 0 -> 7404 bytes .../combinator/hr-combinator-displays.png | Bin 15385 -> 0 bytes .../combinator/hr-cybernetic-displays.png | Bin 0 -> 13691 bytes cybersyn/prototypes/entity.lua | 278 +++++++++++++----- 6 files changed, 205 insertions(+), 81 deletions(-) delete mode 100644 cybersyn/graphics/combinator/combinator-displays.png create mode 100644 cybersyn/graphics/combinator/cybernetic-displays.png delete mode 100644 cybersyn/graphics/combinator/hr-combinator-displays.png create mode 100644 cybersyn/graphics/combinator/hr-cybernetic-displays.png diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 4491ea7..3d342e4 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -56,6 +56,12 @@ Date: 2022-11-25 Version: 0.5.1 Date: 2022-11-25 Features: - - Re-added combinator copy-paste + - Re-added combinator copy-paste after fixing major bug - Fixed bugs within train blacklist logic - Fixed a crash +--------------------------------------------------------------------------------------------------- +Version: 1.0.0 +Date: 2022-11-29 + Features: + - Minor bugfix + - Added placeholder sprites diff --git a/cybersyn/graphics/combinator/combinator-displays.png b/cybersyn/graphics/combinator/combinator-displays.png deleted file mode 100644 index ef1db3dfbdcc48ce8c26bdcd6b640cf1aed76d7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4690 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 UjR4i~9RL6T07*qoM6N<$f_!pQ8vp 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 R8z2Y{qGJ{%cBd^6B)Xm&puXD$6*XU+_TUXE3 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 Dy0gd| diff --git a/cybersyn/graphics/combinator/hr-cybernetic-displays.png b/cybersyn/graphics/combinator/hr-cybernetic-displays.png new file mode 100644 index 0000000000000000000000000000000000000000..aa4b5dc82537f0578345f93c9e627ddaa5caeacf GIT binary patch literal 13691 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 CvWR*B literal 0 HcmV?d00001 diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index 172a2df..28e46d0 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -12,89 +12,207 @@ combinator_entity.radius_visualisation_specification = { distance = 1.5, } combinator_entity.active_energy_usage = "10KW" ---combinator_entity.allow_copy_paste = false +local COMBINATOR_SPRITE = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" +local COMBINATOR_HR_SPRITE = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator.png" +local COMBINATOR_SHADOW = "__cybersyn__/graphics/combinator/cybernetic-combinator-shadow.png" +local COMBINATOR_HR_SHADOW = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" +combinator_entity.sprites = { + north = {layers = { + { + filename=COMBINATOR_SPRITE, + priority="high", + x=0, y=0, + width=74, height=64, + frame_count=1, + shift={ 0.03125, 0.25, }, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SPRITE, + priority="high", + x=0, y=0, + width=144, height=124, + frame_count=1, + shift={ 0.015625, 0.234375, }, + scale=0.5, + }, + }, + { + filename=COMBINATOR_SHADOW, + priority="high", + x=0, y=0, + width=76, height=78, + frame_count=1, + shift={ 0.4375, 0.75, }, + draw_as_shadow=true, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SHADOW, + priority="high", + x=0, y=0, + width=148, height=156, + frame_count=1, + shift={ 0.421875, 0.765625, }, + draw_as_shadow=true, + scale=0.5, + }, + } + }}, + east = {layers={ + { + filename=COMBINATOR_SPRITE, + priority="high", + x=74, y=0, + width=74, height=64, + frame_count=1, + shift={ 0.03125, 0.25, }, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SPRITE, + priority="high", + x=144, y=0, + width=144, height=124, + frame_count=1, + shift={ 0.015625, 0.234375, }, + scale=0.5, + }, + }, + { + filename=COMBINATOR_SHADOW, + priority="high", + x=76, y=0, + width=76, height=78, + frame_count=1, + shift={ 0.4375, 0.75, }, + draw_as_shadow=true, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SHADOW, + priority="high", + x=148, y=0, + width=148, height=156, + frame_count=1, + shift={ 0.421875, 0.765625, }, + draw_as_shadow=true, + scale=0.5, + }, + }, + }}, + south = {layers={ + { + filename=COMBINATOR_SPRITE, + priority="high", + x=148, y=0, + width=74, height=64, + frame_count=1, + shift={ 0.03125, 0.25, }, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SPRITE, + priority="high", + x=288, y=0, + width=144, height=124, + frame_count=1, + shift={ 0.015625, 0.234375, }, + scale=0.5, + }, + }, + { + filename=COMBINATOR_SHADOW, + priority="high", + x=152, y=0, + width=76, height=78, + frame_count=1, + shift={ 0.4375, 0.75, }, + draw_as_shadow=true, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SHADOW, + priority="high", + x=296, y=0, + width=148, height=156, + frame_count=1, + shift={ 0.421875, 0.765625, }, + draw_as_shadow=true, + scale=0.5, + }, + } + }}, + west = {layers={ + { + filename=COMBINATOR_SPRITE, + priority="high", + x=222, y=0, + width=74, height=64, + frame_count=1, + shift={ 0.03125, 0.25, }, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SPRITE, + priority="high", + x=432, y=0, + width=144, height=124, + frame_count=1, + shift={ 0.015625, 0.234375, }, + scale=0.5, + }, + }, + { + filename=COMBINATOR_SHADOW, + priority="high", + x=228, y=0, + width=76, height=78, + frame_count=1, + shift={ 0.4375, 0.75, }, + draw_as_shadow=true, + scale=1, + hr_version={ + filename=COMBINATOR_HR_SHADOW, + priority="high", + x=444, y=0, + width=148, height=156, + frame_count=1, + shift={ 0.421875, 0.765625, }, + draw_as_shadow=true, + scale=0.5, + }, + } + }}, +} -local comb = combinator_entity ---local display_base = { --- filename = "__cybersyn__/graphics/combinator/combinator-displays.png", --- width = 15, --- height = 11, --- hr_version = { --- filename = "__cybersyn__/graphics/combinator/hr-combinator-displays.png", --- width = 30, --- height = 21, --- } ---} - ---local north = table.deepcopy(display_base) ---north.scale = comb.and_symbol_sprites.north.scale ---north.shift = comb.and_symbol_sprites.north.shift ---north.hr_version.scale = comb.and_symbol_sprites.north.hr_version.scale ---north.hr_version.shift = comb.and_symbol_sprites.north.hr_version.shift ---local east = table.deepcopy(display_base) ---east.scale = comb.and_symbol_sprites.east.scale ---east.shift = comb.and_symbol_sprites.east.shift ---east.hr_version.scale = comb.and_symbol_sprites.east.hr_version.scale ---east.hr_version.shift = comb.and_symbol_sprites.east.hr_version.shift ---local south = table.deepcopy(display_base) ---south.scale = comb.and_symbol_sprites.south.scale ---south.shift = comb.and_symbol_sprites.south.shift ---south.hr_version.scale = comb.and_symbol_sprites.south.hr_version.scale ---south.hr_version.shift = comb.and_symbol_sprites.south.hr_version.shift ---local west = table.deepcopy(display_base) ---west.scale = comb.and_symbol_sprites.west.scale ---west.shift = comb.and_symbol_sprites.west.shift ---west.hr_version.scale = comb.and_symbol_sprites.west.hr_version.scale ---west.hr_version.shift = comb.and_symbol_sprites.west.hr_version.shift - ---local display = { --- north = north, --- east = east, --- south = south, --- west = west ---} ---comb.and_symbol_sprites = table.deepcopy(display) ---comb.divide_symbol_sprites = table.deepcopy(display) ---comb.left_shift_symbol_sprites = table.deepcopy(display) ---comb.minus_symbol_sprites = table.deepcopy(display) ---comb.modulo_symbol_sprites = table.deepcopy(display) ---comb.multiply_symbol_sprites = table.deepcopy(display) ---comb.or_symbol_sprites = table.deepcopy(display) ---comb.plus_symbol_sprites = table.deepcopy(display) ---comb.power_symbol_sprites = table.deepcopy(display) ---comb.right_shift_symbol_sprites = table.deepcopy(display) ---comb.xor_symbol_sprites = table.deepcopy(display) - - ---local sprite_base = { --- filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png", --- hr_version = { --- filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png", --- } ---} - ---comb.sprites.north.layers[1].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.north.layers[1].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" ---comb.sprites.north.layers[2].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.north.layers[2].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" - ---comb.sprites.east.layers[1].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.east.layers[1].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" ---comb.sprites.east.layers[2].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.east.layers[2].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" - ---comb.sprites.south.layers[1].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.south.layers[1].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" ---comb.sprites.south.layers[2].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.south.layers[2].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" - ---comb.sprites.west.layers[1].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.west.layers[1].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" ---comb.sprites.west.layers[2].filename = "__cybersyn__/graphics/combinator/cybernetic-combinator.png" ---comb.sprites.west.layers[2].hr_version.filename = "__cybersyn__/graphics/combinator/hr-cybernetic-combinator-shadow.png" - +local function create_combinator_display_direction(x, y, shift) + return { + filename="__cybersyn__/graphics/combinator/cybernetic-displays.png", + x=x, y=y, + width=15, height=11, + shift=shift, + draw_as_glow=true, + hr_version={ + scale=0.5, + filename="__cybersyn__/graphics/combinator/hr-cybernetic-displays.png", + x=2*x, y=2*y, + width=30, height=22, + shift=shift, + draw_as_glow=true, + }, + } +end +local function create_combinator_display(x, y, shiftv, shifth) + return { + north=create_combinator_display_direction(x, y, shiftv), + east=create_combinator_display_direction(x, y, shifth), + south=create_combinator_display_direction(x, y, shiftv), + west=create_combinator_display_direction(x, y, shifth), + } +end +combinator_entity.plus_symbol_sprites = create_combinator_display(15, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.minus_symbol_sprites = create_combinator_display(30, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.divide_symbol_sprites = create_combinator_display(60, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.modulo_symbol_sprites = create_combinator_display(75, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.power_symbol_sprites = create_combinator_display(0, 11, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.left_shift_symbol_sprites = create_combinator_display(15, 11, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.multiply_symbol_sprites = combinator_entity.divide_symbol_sprites combinator_out_entity = flib.copy_prototype(data.raw["constant-combinator"]["constant-combinator"], COMBINATOR_OUT_NAME) From e2ecead62f752e9c2fa605f7185a1d877ccee3ce Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 29 Nov 2022 16:41:06 -0500 Subject: [PATCH 65/66] simplified display sprites --- .../combinator/cybernetic-displays.png | Bin 7404 -> 9336 bytes .../combinator/hr-cybernetic-displays.png | Bin 13691 -> 16313 bytes cybersyn/prototypes/entity.lua | 8 ++++---- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cybersyn/graphics/combinator/cybernetic-displays.png b/cybersyn/graphics/combinator/cybernetic-displays.png index c232f95452ef984567cafb97cbb7cea6e1aa4899..febe91590a975e981768280252627285c0da6aaf 100644 GIT binary patch literal 9336 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 muxs6SOEoo68-9Ae7XA-8A1!3cPg0Tq0000 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 CvWR*B diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index 28e46d0..29080e7 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -206,10 +206,10 @@ local function create_combinator_display(x, y, shiftv, shifth) west=create_combinator_display_direction(x, y, shifth), } end -combinator_entity.plus_symbol_sprites = create_combinator_display(15, 0, { 0, -0.140625, }, { 0, -0.328125, }) -combinator_entity.minus_symbol_sprites = create_combinator_display(30, 0, { 0, -0.140625, }, { 0, -0.328125, }) -combinator_entity.divide_symbol_sprites = create_combinator_display(60, 0, { 0, -0.140625, }, { 0, -0.328125, }) -combinator_entity.modulo_symbol_sprites = create_combinator_display(75, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.plus_symbol_sprites = create_combinator_display(0, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.minus_symbol_sprites = create_combinator_display(15, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.divide_symbol_sprites = create_combinator_display(30, 0, { 0, -0.140625, }, { 0, -0.328125, }) +combinator_entity.modulo_symbol_sprites = create_combinator_display(45, 0, { 0, -0.140625, }, { 0, -0.328125, }) combinator_entity.power_symbol_sprites = create_combinator_display(0, 11, { 0, -0.140625, }, { 0, -0.328125, }) combinator_entity.left_shift_symbol_sprites = create_combinator_display(15, 11, { 0, -0.140625, }, { 0, -0.328125, }) combinator_entity.multiply_symbol_sprites = combinator_entity.divide_symbol_sprites From d749dce48e0e4c93444f231ec5f89d6352048f30 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Tue, 29 Nov 2022 17:05:32 -0500 Subject: [PATCH 66/66] changed flag to mask --- cybersyn/locale/en/base.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index ba5eecf..58809ed 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -2,7 +2,7 @@ cybersyn-ticks-per-second=Central planning updates per second cybersyn-wait-time=Required inactivity duration (sec) cybersyn-request-threshold=Default requester threshold -cybersyn-network-flag=Default sub-network flags +cybersyn-network-flag=Default network mask cybersyn-warmup-time=Station warmup time (sec) cybersyn-stuck-train-time=Stuck train timeout (sec) @@ -10,7 +10,7 @@ cybersyn-stuck-train-time=Stuck train timeout (sec) cybersyn-ticks-per-second=How many times per second the central planner should update the state of the network and schedule deliveries. Only one deliveries can be made per update. This value will be rounded up to a divisor of 60. cybersyn-wait-time=How many seconds the duration of a train's inactivity order will be set to. Trains will be forced to wait in the station or depot until this amount of time has passed without the train receiving any kind of interaction. Non-zero values prevent inserters from getting stuck and prevent trains from leaving depots without refueling. Decimal values are allowed, they will be rounded up to a multiple of 1/60 (0.01667). cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. When a station receives a negative item signal that surpasses its request threshold, so long as any station exists with a positive signal greater than the request threshold, a delivery of that item will be scheduled between the two stations. -cybersyn-network-flag=The default set of sub-networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible sub-network flags to choose from. +cybersyn-network-flag=The default set of sub-networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible sub-networks to choose from. cybersyn-warmup-time=How many seconds a cybernetic combinator will wait before connecting to the Cybersyn network. This is a grace period to modify or correct the circuit network before trains start dispatching to a newly blueprinted station. 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.