From c158745679e181ff173de7fd0f938ffc31af0959 Mon Sep 17 00:00:00 2001 From: Monica Moniot Date: Thu, 8 Dec 2022 09:12:22 -0500 Subject: [PATCH] added a refueler --- cybersyn/changelog.txt | 1 + cybersyn/control.lua | 3 +- cybersyn/scripts/central-planning.lua | 2 +- cybersyn/scripts/constants.lua | 10 +- cybersyn/scripts/factorio-api.lua | 82 ++- cybersyn/scripts/global.lua | 26 +- cybersyn/scripts/layout.lua | 121 +++-- cybersyn/scripts/main.lua | 694 ++++++++------------------ cybersyn/scripts/migrations.lua | 117 +---- cybersyn/scripts/remote-interface.lua | 22 +- cybersyn/scripts/train-events.lua | 432 ++++++++++++++++ 11 files changed, 841 insertions(+), 669 deletions(-) create mode 100644 cybersyn/scripts/train-events.lua diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 62ca7b0..f3b7db6 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -117,4 +117,5 @@ Date: 2022-12-5 Features: - Added the ability to use the priority signal as input to optional station control so one can override priority on items with optional station control thresholds - Added refueler stations + - Slightly more permissive allow-list logic - Fixed a crash relating to wagon control combinators on request stations diff --git a/cybersyn/control.lua b/cybersyn/control.lua index c1eff6c..f3580ad 100644 --- a/cybersyn/control.lua +++ b/cybersyn/control.lua @@ -3,8 +3,9 @@ require("scripts.constants") require("scripts.global") require("scripts.factorio-api") -require("scripts.central-planning") require("scripts.layout") +require("scripts.central-planning") +require("scripts.train-events") require("scripts.gui") require("scripts.migrations") require("scripts.main") diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index 48f4373..dbe303f 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -175,7 +175,7 @@ function send_train_between(map_data, r_station_id, p_station_id, train_id, prim end --NOTE: we assume that the train is not being teleported at this time if set_manifest_schedule(train.entity, train.depot_name, train.se_depot_surface_i, p_station.entity_stop, r_station.entity_stop, manifest, depot_id ~= nil) then - train.status = STATUS_D_TO_P + train.status = STATUS_TO_P train.p_station_id = p_station_id train.r_station_id = r_station_id train.manifest = manifest diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index 3127558..902ada7 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -20,6 +20,7 @@ OPERATION_PRIMARY_IO_ACTIVE = "<<" OPERATION_SECONDARY_IO = "%" OPERATION_DEPOT = "+" OPERATION_WAGON_MANIFEST = "-" +OPERATION_REFUELER = ">>" NETWORK_SIGNAL_DEFAULT = {name="signal-A", type="virtual"} INACTIVITY_TIME = 100 @@ -29,11 +30,14 @@ DELTA = 1/2048 DEPOT_PRIORITY_MULT = 2048 STATUS_D = 0 -STATUS_D_TO_P = 1 +STATUS_TO_P = 1 STATUS_P = 2 -STATUS_P_TO_R = 3 +STATUS_TO_R = 3 STATUS_R = 4 -STATUS_R_TO_D = 5 +STATUS_TO_D = 5 +STATUS_TO_D_BYPASS = 6 +STATUS_TO_F = 7 +STATUS_F = 8 STATUS_CUSTOM = 256 --this status and any status greater than it can be used by other mods (I've reserved the lower integers for myself in case I want to add more statuses) LONGEST_INSERTER_REACH = 2 diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 88bd349..340c28f 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -1,7 +1,10 @@ --By Mami local get_distance = require("__flib__.misc").get_distance -local abs = math.abs local floor = math.floor +local table_insert = table.insert +local DEFINES_WORKING = defines.entity_status.working +local DEFINES_LOW_POWER = defines.entity_status.low_power +local DEFINES_COMBINATOR_INPUT = defines.circuit_connector_id.combinator_input ---@param map_data MapData @@ -130,7 +133,7 @@ function set_manifest_schedule(train, depot_name, d_surface_i, p_stop, r_stop, m local t_surface_i = t_surface.index local p_surface_i = p_surface.index local r_surface_i = r_surface.index - if t_surface_i == p_surface_i and p_surface_i == r_surface_i then + if t_surface_i == p_surface_i and p_surface_i == r_surface_i and d_surface_i == t_surface_i then train.schedule = {current = start_at_depot and 1 or 2, records = { create_inactivity_order(depot_name), create_direct_to_station_order(p_stop), @@ -159,7 +162,9 @@ function set_manifest_schedule(train, depot_name, d_surface_i, p_stop, r_stop, m 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 + if t_surface_i == r_surface_i then + records[#records + 1] = create_direct_to_station_order(r_stop) + elseif p_surface_i ~= r_surface_i then records[#records + 1] = se_create_elevator_order(elevator_name, is_train_in_orbit) is_train_in_orbit = not is_train_in_orbit end @@ -190,6 +195,46 @@ function set_manifest_schedule(train, depot_name, d_surface_i, p_stop, r_stop, m return true end +---@param train LuaTrain +---@param stop LuaEntity +function add_refueler_schedule(train, stop) + local schedule = train.schedule or {current = 1, records = {}} + local i = schedule.current + + local t_surface = train.front_stock.surface + local f_surface = stop.surface + local t_surface_i = t_surface.index + local f_surface_i = f_surface.index + if t_surface_i == f_surface_i then + table_insert(schedule.records, i, create_direct_to_station_order(stop)) + i = i + 1 + table_insert(schedule.records, i, create_inactivity_order(stop.backer_name)) + + train.schedule = schedule + elseif IS_SE_PRESENT then + local t_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = t_surface_i}) + local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = f_surface}) + local is_train_in_orbit = other_zone.orbit_index == t_zone.index + if is_train_in_orbit or t_zone.orbit_index == other_zone.index then + local elevator_name = se_get_space_elevator_name(t_surface) + local cur_order = schedule.records[i] + local is_elevator_in_orders_already = cur_order and cur_order.station == elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX) + if not is_elevator_in_orders_already then + table_insert(schedule.records, i, se_create_elevator_order(elevator_name, is_train_in_orbit)) + end + i = i + 1 + is_train_in_orbit = not is_train_in_orbit + table_insert(schedule.records, i, create_inactivity_order(stop.backer_name)) + i = i + 1 + if not is_elevator_in_orders_already then + table_insert(schedule.records, i, se_create_elevator_order(elevator_name, is_train_in_orbit)) + i = i + 1 + is_train_in_orbit = not is_train_in_orbit + end + end + end +end + ------------------------------------------------------------------------------ --[[combinators]]-- @@ -253,6 +298,34 @@ 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 mod_settings CybersynModSettings +---@param refueler Refueler +function set_refueler_from_comb(mod_settings, refueler) + --NOTE: this does nothing to update currently active deliveries + local params = get_comb_params(refueler.entity_comb) + local bits = params.second_constant or 0 + local signal = params.first_signal + refueler.network_name = signal and signal.name or nil + refueler.allows_all_trains = bits%2 == 1 + + local signals = refueler.entity_comb.get_merged_signals(DEFINES_COMBINATOR_INPUT) + refueler.priority = 0 + refueler.network_flag = mod_settings.network_flag + if not signals then return end + 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 + refueler.priority = item_count + end + if item_name == refueler.network_name then + refueler.network_flag = item_count + end + end + end +end + ---@param map_data MapData ---@param station Station function update_display(map_data, station) @@ -320,9 +393,6 @@ function set_combinator_output(map_data, comb, signals) end end -local DEFINES_WORKING = defines.entity_status.working -local DEFINES_LOW_POWER = defines.entity_status.low_power -local DEFINES_COMBINATOR_INPUT = defines.circuit_connector_id.combinator_input ---@param station Station function get_signals(station) --NOTE: the combinator must be valid, but checking for valid every time is too slow diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 0da4b92..2502286 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -10,8 +10,10 @@ ---@field public active_station_ids uint[] ---@field public warmup_station_ids uint[] ---@field public depots {[uint]: Depot} +---@field public refuelers {[uint]: Refueler} ---@field public trains {[uint]: Train} ----@field public available_trains {[string]: {[uint]: true?}} --{[network_name]: {[train_id]: depot_id}} +---@field public available_trains {[string]: {[uint]: true?}} --{[network_name]: {[train_id]: true}} +---@field public to_refueler {[string]: {[uint]: true?}} --{[network_name]: {[refeuler_id]: true}} ---@field public layouts {[uint]: (0|1|2)[]} ---@field public layout_train_count {[uint]: int} ---@field public tick_state uint @@ -29,6 +31,7 @@ ---@field public deliveries_total int ---@field public last_delivery_tick int ---@field public priority int --transient +---@field public item_priority int? --transient ---@field public r_threshold int >= 0 --transient ---@field public locked_slots int >= 0 --transient ---@field public network_name string? @@ -38,7 +41,8 @@ ---@field public accepted_layouts {[uint]: true?} ---@field public layout_pattern (0|1|2|3)[]? ---@field public tick_signals {[uint]: Signal}? --transient ----@field public p_count_or_r_threshold_per_item {[string]: int} --transient +---@field public item_p_counts {[string]: int} --transient +---@field public item_thresholds {[string]: int}? --transient ---@field public display_state 0|1|2|3 --low bit is if this station's request has failed, high bit is if a train is heading to this station ---@class Depot @@ -46,15 +50,26 @@ ---@field public entity_comb LuaEntity ---@field public available_train_id uint?--train_id +---@class Refueler +---@field public entity_stop LuaEntity +---@field public entity_comb LuaEntity +---@field public trains_total int +---@field public accepted_layouts {[uint]: true?} +---@field public layout_pattern (0|1|2|3)[]? +---@field public allows_all_trains boolean +---@field public priority int +---@field public network_name string? +---@field public network_flag int + ---@class Train ---@field public entity LuaTrain --should only be invalid if se_is_being_teleported is true ---@field public layout_id uint ---@field public item_slot_capacity int ---@field public fluid_capacity int ---@field public status int ----@field public p_station_id uint ----@field public r_station_id uint ----@field public manifest Manifest +---@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 true? ---@field public is_available true? @@ -63,6 +78,7 @@ ---@field public network_name string? --can only be nil when the train is parked at a depot ---@field public network_flag int ---@field public priority int +---@field public refueler_id uint? ---@field public se_depot_surface_i uint --se only ---@field public se_is_being_teleported true? --se only ---@field public se_awaiting_removal any? --se only diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 2d103ae..a04f617 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -30,11 +30,11 @@ end ---@param layout_pattern (0|1|2|3)[] ---@param layout (0|1|2)[] -function is_layout_accepted(layout_pattern, layout) +function is_refuel_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 + if (v == 1 and (p == 1 or p == 3)) or (v == 2 and (p == 2 or p == 3)) then valid = false break end @@ -42,7 +42,28 @@ function is_layout_accepted(layout_pattern, layout) 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 + if (v == 1 and (p == 1 or p == 3)) or (v == 2 and (p == 2 or p == 3)) then + valid = false + break + end + end + return valid +end +---@param layout_pattern (0|1|2|3)[] +---@param layout (0|1|2)[] +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 == 1 and not (p == 1 or p == 3)) or (v == 2 and not (p == 2 or p == 3)) 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 == 1 and not (p == 1 or p == 3)) or (v == 2 and not (p == 2 or p == 3)) then valid = false break end @@ -142,7 +163,7 @@ end 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 manifest = train.manifest--[[@as Manifest]] local is_reversed = get_train_direction(station.entity_stop, train.entity) @@ -325,57 +346,58 @@ end ---@param map_data MapData ----@param station Station +---@param stop Station|Refueler +---@param is_station_or_refueler boolean ---@param forbidden_entity LuaEntity? -function reset_station_layout(map_data, station, forbidden_entity) +function reset_stop_layout(map_data, stop, is_station_or_refueler, forbidden_entity) --NOTE: station must be in auto mode - local station_rail = station.entity_stop.connected_rail - if station_rail == nil then + local stop_rail = stop.entity_stop.connected_rail + if stop_rail == nil then --cannot accept deliveries - station.layout_pattern = nil - station.accepted_layouts = {} + stop.layout_pattern = nil + stop.accepted_layouts = {} return end - local rail_direction_from_station - if station.entity_stop.connected_rail_direction == defines.rail_direction.front then - rail_direction_from_station = defines.rail_direction.back + local rail_direction_from_stop + if stop.entity_stop.connected_rail_direction == defines.rail_direction.front then + rail_direction_from_stop = defines.rail_direction.back else - rail_direction_from_station = defines.rail_direction.front + rail_direction_from_stop = defines.rail_direction.front end - local station_direction = station.entity_stop.direction - local surface = station.entity_stop.surface - local middle_x = station_rail.position.x - local middle_y = station_rail.position.y + local stop_direction = stop.entity_stop.direction + local surface = stop.entity_stop.surface + local middle_x = stop_rail.position.x + local middle_y = stop_rail.position.y local reach = LONGEST_INSERTER_REACH + 1 local search_area local area_delta local is_ver - if station_direction == defines.direction.north then + if stop_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} is_ver = true - elseif station_direction == defines.direction.east then + elseif stop_direction == defines.direction.east then 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} is_ver = false - elseif station_direction == defines.direction.south then + elseif stop_direction == defines.direction.south then search_area = {left_top = {x = middle_x - reach, y = middle_y - 6}, right_bottom = {x = middle_x + reach, y = middle_y}} area_delta = {x = 0, y = -7} is_ver = true - elseif station_direction == defines.direction.west then + elseif stop_direction == defines.direction.west then 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} is_ver = false else - assert(false, "cybersyn: invalid station direction") + assert(false, "cybersyn: invalid stop direction") end local length = 2 - local pre_rail = station_rail + local pre_rail = stop_rail local layout_pattern = {0} local type_filter = {"inserter", "pump", "arithmetic-combinator"} local wagon_number = 0 for i = 1, 112 do - local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_station, rail_connection_direction = defines.rail_connection_direction.straight}) + local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_stop, rail_connection_direction = defines.rail_connection_direction.straight}) if not rail or rail_connection_direction ~= defines.rail_connection_direction.straight or not rail.valid then is_break = true break @@ -426,7 +448,7 @@ function reset_station_layout(map_data, station, forbidden_entity) supports_fluid = true end end - elseif entity.name == COMBINATOR_NAME then + elseif entity.name == COMBINATOR_NAME and is_station_or_refueler then local param = map_data.to_comb_params[entity.unit_number] if param.operation == OPERATION_WAGON_MANIFEST then local pos = entity.position @@ -437,10 +459,10 @@ function reset_station_layout(map_data, station, forbidden_entity) is_there = middle_y - 2.1 <= pos.y and pos.y <= middle_y + 2.1 end if is_there then - if not station.wagon_combs then - station.wagon_combs = {} + if not stop.wagon_combs then + stop.wagon_combs = {} end - station.wagon_combs[wagon_number] = entity + stop.wagon_combs[wagon_number] = entity end end end @@ -461,18 +483,19 @@ function reset_station_layout(map_data, station, forbidden_entity) search_area = area.move(search_area, area_delta) end end - station.layout_pattern = layout_pattern + stop.layout_pattern = layout_pattern for id, layout in pairs(map_data.layouts) do - station.accepted_layouts[id] = is_layout_accepted(layout_pattern, layout) or nil + stop.accepted_layouts[id] = is_layout_accepted(layout_pattern, layout) or nil end end ---@param map_data MapData ----@param station Station +---@param stop Station|Refueler +---@param is_station_or_refueler boolean ---@param forbidden_entity LuaEntity? -function update_station_if_auto(map_data, station, forbidden_entity) - if not station.allows_all_trains then - reset_station_layout(map_data, station, forbidden_entity) +function update_stop_if_auto(map_data, stop, is_station_or_refueler, forbidden_entity) + if not stop.allows_all_trains then + reset_stop_layout(map_data, stop, is_station_or_refueler, forbidden_entity) end end @@ -480,7 +503,7 @@ end ---@param rail LuaEntity ---@param forbidden_entity LuaEntity? ---@param force boolean? -function update_station_from_rail(map_data, rail, forbidden_entity, force) +function update_stop_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 @@ -494,12 +517,18 @@ function update_station_from_rail(map_data, rail, forbidden_entity, force) return end if entity.name == "train-stop" then - local station = map_data.stations[entity.unit_number] - if station then + local id = entity.unit_number + local is_station = true + local stop = map_data.stations[id] + if not stop then + stop = map_data.refuelers[id] + is_station = false + end + if stop then if force then - reset_station_layout(map_data, station, forbidden_entity) - else - update_station_if_auto(map_data, station, forbidden_entity) + reset_stop_layout(map_data, stop, is_station, forbidden_entity) + elseif not stop.allows_all_trains then + reset_stop_layout(map_data, stop, is_station, forbidden_entity) end end return @@ -516,15 +545,15 @@ end ---@param map_data MapData ---@param pump LuaEntity ---@param forbidden_entity LuaEntity? -function update_station_from_pump(map_data, pump, forbidden_entity) +function update_stop_from_pump(map_data, pump, forbidden_entity) if pump.pump_rail_target then - update_station_from_rail(map_data, pump.pump_rail_target, forbidden_entity) + update_stop_from_rail(map_data, pump.pump_rail_target, forbidden_entity) end end ---@param map_data MapData ---@param inserter LuaEntity ---@param forbidden_entity LuaEntity? -function update_station_from_inserter(map_data, inserter, forbidden_entity) +function update_stop_from_inserter(map_data, inserter, forbidden_entity) local surface = inserter.surface --NOTE: we don't use find_entity solely for miniloader compat @@ -534,7 +563,7 @@ function update_station_from_inserter(map_data, inserter, forbidden_entity) radius = 1, }) if rails[1] then - update_station_from_rail(map_data, rails[1], forbidden_entity) + update_stop_from_rail(map_data, rails[1], forbidden_entity) end rails = surface.find_entities_filtered({ type = "straight-rail", @@ -542,6 +571,6 @@ function update_station_from_inserter(map_data, inserter, forbidden_entity) radius = 1, }) if rails[1] then - update_station_from_rail(map_data, rails[1], forbidden_entity) + update_stop_from_rail(map_data, rails[1], forbidden_entity) end end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index c92213c..bca4d35 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -4,138 +4,6 @@ local ceil = math.ceil local table_insert = table.insert ----@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 - 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 - set_combinator_output(map_data, comb, nil) - end - end -end - ----@param map_data MapData ----@param train_id uint ----@param train Train -function on_failed_delivery(map_data, train_id, train) - --NOTE: must either change this train's status or remove it after this call - local p_station_id = train.p_station_id - local r_station_id = train.r_station_id - local manifest = train.manifest - local is_p_in_progress = train.status == STATUS_D_TO_P or train.status == STATUS_P - local is_r_in_progress = is_p_in_progress or train.status == STATUS_P_TO_R or train.status == STATUS_R - if is_p_in_progress then - local station = map_data.stations[p_station_id] - remove_manifest(map_data, station, manifest, 1) - if train.status == STATUS_P then - set_comb1(map_data, station, nil) - unset_wagon_combs(map_data, station) - end - end - if is_r_in_progress then - local station = map_data.stations[r_station_id] - remove_manifest(map_data, station, manifest, -1) - if train.status == STATUS_R then - set_comb1(map_data, station, nil) - unset_wagon_combs(map_data, station) - end - end - train.r_station_id = 0 - train.p_station_id = 0 - train.manifest = nil - interface_raise_train_failed_delivery(train_id, is_p_in_progress, p_station_id, is_r_in_progress, r_station_id, manifest) -end - - - ----@param map_data MapData ----@param train_id uint ----@param train Train -function add_available_train(map_data, train_id, train) - local network_name = train.network_name - if network_name then - local network = map_data.available_trains[network_name] - if not network then - network = {} - map_data.available_trains[network_name] = network - end - network[train_id] = true - train.is_available = true - interface_raise_train_available(train_id) - end -end ----@param map_data MapData ----@param mod_settings CybersynModSettings ----@param depot_id uint ----@param depot Depot ----@param train_id uint ----@param train Train -function add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot) - 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.available_trains[network_name] = network - end - network[train_id] = true - train.is_available = true - end - depot.available_train_id = train_id - train.status = STATUS_D - train.parked_at_depot_id = depot_id - train.depot_name = depot.entity_stop.backer_name - train.se_depot_surface_i = depot.entity_stop.surface.index - 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 - interface_raise_train_available(train_id) - end -end ----@param map_data MapData ----@param train_id uint ----@param train Train -function remove_available_train(map_data, train_id, train) - ---@type uint - if train.is_available and train.network_name then - local network = map_data.available_trains[train.network_name--[[@as string]]] - if network then - network[train_id] = nil - if next(network) == nil then - map_data.available_trains[train.network_name] = nil - end - end - train.is_available = nil - end -end - - ---@param map_data MapData ---@param stop LuaEntity ---@param comb LuaEntity @@ -152,8 +20,9 @@ local function on_depot_built(map_data, stop, comb) end ---@param map_data MapData +---@param depot_id uint ---@param depot Depot -local function on_depot_broken(map_data, depot) +local function on_depot_broken(map_data, depot_id, depot) local train_id = depot.available_train_id if train_id then local train = map_data.trains[train_id] @@ -161,11 +30,62 @@ local function on_depot_broken(map_data, depot) send_lost_train_alert(train.entity, depot.entity_stop.backer_name) remove_train(map_data, train_id, train) end - local depot_id = depot.entity_stop.unit_number--[[@as uint]] map_data.depots[depot_id] = nil interface_raise_depot_removed(depot_id, depot) end +---@param map_data MapData +---@param stop LuaEntity +---@param comb LuaEntity +local function on_refueler_built(map_data, stop, comb) + --NOTE: only place where new Depot + local refueler = { + entity_stop = stop, + entity_comb = comb, + trains_total = 0, + accepted_layouts = {}, + layout_pattern = {}, + --allows_all_trains = set_refueler_from_comb, + --priority = set_refueler_from_comb, + --network_name = set_refueler_from_comb, + --network_flag = set_refueler_from_comb, + } + set_refueler_from_comb(mod_settings, refueler) + local id = stop.unit_number--[[@as uint]] + map_data.refuelers[id] = refueler + update_stop_if_auto(map_data, refueler, false) + interface_raise_refueler_created(depot_id) +end +---@param map_data MapData +---@param refueler_id uint +---@param refueler Refueler +local function on_refueler_broken(map_data, refueler_id, refueler) + if refueler.trains_total > 0 then + --search for trains coming to the destroyed refueler + for train_id, train in pairs(map_data.trains) do + local is_f = train.refueler_id == refueler_id + if is_f then + if not train.se_is_being_teleported then + remove_train(map_data, train_id, train) + lock_train(train.entity) + send_lost_train_alert(train.entity, train.depot_name) + else + train.se_awaiting_removal = train_id + end + end + end + end + if refueler.network_name then + local network = map_data.to_refueler[refueler.network_name] + network[refueler_id] = nil + if next(network) == nil then + map_data.to_refueler[refueler.network_name] = nil + end + end + map_data.stations[refueler_id] = nil + interface_raise_refueler_removed(refueler_id, refueler) +end + ---@param map_data MapData ---@param stop LuaEntity ---@param comb1 LuaEntity @@ -201,7 +121,7 @@ local function on_station_built(map_data, stop, comb1, comb2) map_data.stations[id] = station map_data.warmup_station_ids[#map_data.warmup_station_ids + 1] = id - update_station_if_auto(map_data, station, nil) + update_stop_if_auto(map_data, station, true) interface_raise_station_created(id) end ---@param map_data MapData @@ -215,8 +135,8 @@ local function on_station_broken(map_data, station_id, station) local is_p = train.p_station_id == station_id if is_p or is_r then - local is_p_in_progress = train.status == STATUS_D_TO_P or train.status == STATUS_P - local is_r_in_progress = is_p_in_progress or train.status == STATUS_P_TO_R or train.status == STATUS_R + local is_p_in_progress = train.status == STATUS_TO_P or train.status == STATUS_P + local is_r_in_progress = is_p_in_progress or train.status == STATUS_TO_R or train.status == STATUS_R if (is_p and is_p_in_progress) or (is_r and is_r_in_progress) then --train is attempting delivery to a stop that was destroyed, stop it on_failed_delivery(map_data, train_id, train) @@ -331,41 +251,37 @@ local function on_combinator_built(map_data, comb) if op == OPERATION_WAGON_MANIFEST then if rail then - update_station_from_rail(map_data, rail, nil, true) + update_stop_from_rail(map_data, rail, nil, true) end - elseif op == OPERATION_DEPOT then - if stop then - local station = map_data.stations[stop.unit_number] - ---@type Depot - local depot = map_data.depots[stop.unit_number] - if depot or station then - --NOTE: repeated combinators are ignored - else + elseif stop then + local id = stop.unit_number--[[@as uint]] + local station = map_data.stations[id] + local depot = map_data.depots[id] + local refueler = map_data.refuelers[id] + if op == OPERATION_DEPOT then + if refueler then + on_refueler_broken(map_data, id, refueler) + end + if not station and not depot then on_depot_built(map_data, stop, comb) end - end - elseif op == OPERATION_SECONDARY_IO then - if stop then - local station = map_data.stations[stop.unit_number] + elseif op == OPERATION_REFUELER then + if not station and not depot and not refueler then + on_depot_built(map_data, stop, comb) + end + elseif op == OPERATION_SECONDARY_IO then if station and not station.entity_comb2 then station.entity_comb2 = comb end - end - 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 - + elseif op == OPERATION_PRIMARY_IO then + if refueler then + on_refueler_broken(map_data, id, refueler) + end + if depot then + on_depot_broken(map_data, id, depot) + end + if not station then local comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) - on_station_built(map_data, stop, comb, comb2) end end @@ -378,20 +294,42 @@ 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] + local id = stop.unit_number + local station = map_data.stations[id] if station then if station.entity_comb1 == comb then station.network_name = network_name end else - 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_id, train) - add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot) + local depot = map_data.depots[id] + if depot then + if 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_id, train) + add_available_train_to_depot(map_data, mod_settings, train_id, train, id, depot) + end + end + else + local refueler = map_data.refuelers[id] + if refueler and refueler.entity_comb == comb then + if refueler.network_name then + local network = map_data.to_refueler[refueler.network_name] + network[id] = nil + if next(network) == nil then + map_data.to_refueler[refueler.network_name] = nil + end + end + refueler.network_name = network_name + if network_name then + local network = map_data.to_refueler[network_name] + if network == nil then + network = {} + map_data.to_refueler[network_name] = network + end + network[id] = true + end end end end @@ -407,33 +345,27 @@ function on_combinator_broken(map_data, comb) local stop = map_data.to_stop[comb_id] if stop and stop.valid then - local station = map_data.stations[stop.unit_number] + local id = stop.unit_number--[[@as uint]] + local station = map_data.stations[id] if station then if station.entity_comb1 == comb then - local comb1 = search_for_station_combinator(map_data, stop, OPERATION_PRIMARY_IO, 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) - if depot_comb then - on_depot_built(map_data, stop, depot_comb) - end - end + on_station_broken(map_data, id, station) + on_stop_built(map_data, stop, comb) elseif station.entity_comb2 == comb then station.entity_comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb) end else - local depot = map_data.depots[stop.unit_number] - 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 - depot.entity_comb = depot_comb - else - on_depot_broken(map_data, depot) + local depot = map_data.depots[id] + if depot then + if depot.entity_comb == comb then + on_depot_broken(map_data, id, depot) + on_stop_built(map_data, stop, comb) + end + else + local refueler = map_data.refuelers[id] + if refueler and refueler.entity_comb == comb then + on_refueler_broken(map_data, id, depot) + on_stop_built(map_data, stop, comb) end end end @@ -451,8 +383,7 @@ end ---@param map_data MapData ---@param comb LuaEntity function combinator_update(map_data, comb) - ---@type uint - local unit_number = comb.unit_number + local unit_number = comb.unit_number--[[@as uint]] local control = get_comb_control(comb) local params = control.parameters local old_params = map_data.to_comb_params[unit_number] @@ -480,18 +411,24 @@ function combinator_update(map_data, comb) end if params.second_constant ~= old_params.second_constant then has_changed = true - local stop = global.to_stop[comb.unit_number] + local stop = map_data.to_stop[comb.unit_number] if stop then - local station = global.stations[stop.unit_number] + local id = stop.unit_number + local station = map_data.stations[id] if station then - 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 - 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) + local pre = station.allows_all_trains + set_station_from_comb_state(station) + if station.allows_all_trains ~= pre then + update_stop_if_auto(map_data, station, true) + end + else + local refueler = map_data.refuelers[id] + if refueler then + local pre = refueler.allows_all_trains + set_refueler_from_comb(mod_settings, refueler) + if refueler.allows_all_trains ~= pre then + update_stop_if_auto(map_data, refueler, false) + end end end end @@ -504,7 +441,8 @@ end ---@param map_data MapData ---@param stop LuaEntity -local function on_stop_built(map_data, stop) +---@param comb_forbidden LuaEntity? +function on_stop_built(map_data, stop, comb_forbidden) local pos_x = stop.position.x local pos_y = stop.position.y @@ -515,9 +453,10 @@ local function on_stop_built(map_data, stop) local comb2 = nil local comb1 = nil local depot_comb = nil + local refueler_comb = nil local entities = stop.surface.find_entities(search_area) for _, entity in pairs(entities) do - if entity.valid and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then + if entity.valid and entity ~= comb_forbidden and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then map_data.to_stop[entity.unit_number] = stop local param = get_comb_params(entity) local op = param.operation @@ -527,6 +466,8 @@ local function on_stop_built(map_data, stop) comb2 = entity elseif op == OPERATION_DEPOT then depot_comb = entity + elseif op == OPERATION_REFUELER then + refueler_comb = entity end end end @@ -534,6 +475,8 @@ local function on_stop_built(map_data, stop) on_station_built(map_data, stop, comb1, comb2) elseif depot_comb then on_depot_built(map_data, stop, depot_comb) + elseif refueler_comb then + on_refueler_built(map_data, stop, refueler_comb) end end ---@param map_data MapData @@ -553,20 +496,26 @@ local function on_stop_broken(map_data, stop) end end - local station = map_data.stations[stop.unit_number] + local id = stop.unit_number--[[@as uint]] + local station = map_data.stations[id] if station then - on_station_broken(map_data, stop.unit_number, station) + on_station_broken(map_data, id, station) else - local depot = map_data.depots[stop.unit_number] + local depot = map_data.depots[id] if depot then - on_depot_broken(map_data, depot) + on_depot_broken(map_data, id, depot) + else + local refueler = map_data.refuelers[id] + if refueler then + on_refueler_broken(map_data, id, refueler) + end end end end ---@param map_data MapData ---@param stop LuaEntity ---@param old_name string -local function on_station_rename(map_data, stop, old_name) +local function on_stop_rename(map_data, stop, old_name) --search for trains coming to the renamed station local station_id = stop.unit_number--[[@as uint]] local station = map_data.stations[station_id] @@ -575,8 +524,8 @@ local function on_station_rename(map_data, stop, old_name) local is_p = train.p_station_id == station_id local is_r = train.r_station_id == station_id if is_p or is_r then - local is_p_in_progress = train.status == STATUS_D_TO_P or train.status == STATUS_P - local is_r_in_progress = is_p_in_progress or train.status == STATUS_P_TO_R or train.status == STATUS_R + local is_p_in_progress = train.status == STATUS_TO_P or train.status == STATUS_P + local is_r_in_progress = is_p_in_progress or train.status == STATUS_TO_R or train.status == STATUS_R if is_r and is_r_in_progress then local r_station = map_data.stations[train.r_station_id] if not train.se_is_being_teleported then @@ -618,209 +567,6 @@ local function find_and_add_all_stations_from_nothing(map_data) end end ----@param map_data MapData ----@param depot_id uint ----@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 - if train.manifest then - if train.status == STATUS_R_TO_D then - --succeeded delivery - train.p_station_id = 0 - train.r_station_id = 0 - train.manifest = nil - elseif mod_settings.react_to_train_early_to_depot then - on_failed_delivery(map_data, train_id, train) - send_unexpected_train_alert(train.entity) - else - return - end - end - if is_train_empty then - remove_available_train(map_data, train_id, train) - add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id]) - set_depot_schedule(train_entity, train.depot_name) - interface_raise_train_parked_at_depot(train_id, depot_id) - else - --train still has cargo - if mod_settings.react_to_nonempty_train_in_depot then - lock_train(train_entity) - remove_train(map_data, train_id, train) - send_nonempty_train_in_depot_alert(train_entity) - end - interface_raise_train_nonempty_in_depot(depot_id, train_entity, train_id) - end - elseif is_train_empty then - --NOTE: only place where new Train - train = { - entity = train_entity, - --layout_id = set_train_layout, - --item_slot_capacity = set_train_layout, - --fluid_capacity = set_train_layout, - --status = add_available_train_to_depot, - p_station_id = 0, - r_station_id = 0, - manifest = nil, - last_manifest_tick = map_data.total_ticks, - has_filtered_wagon = nil, - --is_available = add_available_train_to_depot, - --parked_at_depot_id = add_available_train_to_depot, - --depot_name = add_available_train_to_depot, - --network_name = add_available_train_to_depot, - --network_flag = add_available_train_to_depot, - --priority = add_available_train_to_depot, - } - set_train_layout(map_data, train) - map_data.trains[train_id] = train - add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id]) - - set_depot_schedule(train_entity, train.depot_name) - interface_raise_train_created(train_id, depot_id) - else - if mod_settings.react_to_nonempty_train_in_depot then - lock_train(train_entity) - send_nonempty_train_in_depot_alert(train_entity) - end - interface_raise_train_nonempty_in_depot(depot_id, train_entity) - end -end ----@param map_data MapData ----@param stop LuaEntity ----@param train_id uint ----@param train Train -local function on_train_arrives_buffer(map_data, stop, train_id, train) - 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 - local station = map_data.stations[station_id] - 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 - local station = map_data.stations[station_id] - 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 - --this is 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 is player intervention that is considered valid - elseif mod_settings.react_to_train_at_incorrect_station then - on_failed_delivery(map_data, train_id, train) - remove_train(map_data, train_id, train) - lock_train(train.entity) - send_lost_train_alert(train.entity, train.depot_name) - end - elseif mod_settings.react_to_train_at_incorrect_station then - --train is lost somehow, probably from player intervention - remove_train(map_data, train_id, train) - send_lost_train_alert(train.entity, train.depot_name) - end -end ----@param map_data MapData ----@param mod_settings CybersynModSettings ----@param train_id uint ----@param train Train -local function on_train_leaves_station(map_data, mod_settings, train_id, 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 = nil - for carriage_i, carriage in ipairs(train.entity.cargo_wagons) do - 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 - interface_raise_train_completed_provide(train_id) - 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_comb1(map_data, station, nil) - unset_wagon_combs(map_data, station) - --add to available trains for depot bypass - local fuel_fill = 0 - local total_slots = 0 - for k, v in pairs(train.entity.locomotives) do - if v[1] then - local inv = v[1].get_fuel_inventory() - if inv then - local inv_size = #inv - total_slots = total_slots + inv_size - for i = 1, inv_size do - local item = inv[i--[[@as uint]]] - local count = item.count - if count > 0 then - fuel_fill = fuel_fill + count/get_stack_size(map_data, item.name) - end - end - end - end - end - if total_slots == 0 then - --if total_slots == 0 it's probably a modded electric train - if mod_settings.depot_bypass_threshold < 1 then - add_available_train(map_data, train_id, train) - end - elseif fuel_fill/total_slots > mod_settings.depot_bypass_threshold then - add_available_train(map_data, train_id, train) - end - interface_raise_train_completed_request(train_id) - end - elseif train.status == STATUS_D then - --The train is leaving the depot without a manifest, the player likely intervened - local depot = map_data.depots[train.parked_at_depot_id--[[@as uint]]] - remove_train(map_data, train_id, train) - send_lost_train_alert(train.entity, depot.entity_stop.backer_name) - end -end - - ----@param map_data MapData ----@param train_id uint ----@param train Train -local function on_train_broken(map_data, train_id, train) - --NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0 - if not train.se_is_being_teleported then - if train.manifest then - on_failed_delivery(map_data, train_id, train) - end - remove_train(map_data, train_id, train) - end -end ----@param map_data MapData ----@param pre_train_id uint -local function on_train_modified(map_data, pre_train_id) - local train = map_data.trains[pre_train_id] - --NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0 - if train and not train.se_is_being_teleported then - if train.manifest then - on_failed_delivery(map_data, pre_train_id, train) - end - remove_train(map_data, pre_train_id, train) - end -end - local function on_built(event) local entity = event.entity or event.created_entity @@ -831,11 +577,11 @@ local function on_built(event) elseif entity.name == COMBINATOR_NAME then on_combinator_built(global, entity) elseif entity.type == "inserter" then - update_station_from_inserter(global, entity) + update_stop_from_inserter(global, entity) elseif entity.type == "pump" then - update_station_from_pump(global, entity) + update_stop_from_pump(global, entity) elseif entity.type == "straight-rail" then - update_station_from_rail(global, entity) + update_stop_from_rail(global, entity) end end local function on_broken(event) @@ -847,11 +593,11 @@ local function on_broken(event) elseif entity.name == COMBINATOR_NAME then on_combinator_broken(global, entity) elseif entity.type == "inserter" then - update_station_from_inserter(global, entity, entity) + update_stop_from_inserter(global, entity, entity) elseif entity.type == "pump" then - update_station_from_pump(global, entity, entity) + update_stop_from_pump(global, entity, entity) elseif entity.type == "straight-rail" then - update_station_from_rail(global, entity, nil) + update_stop_from_rail(global, entity, nil) elseif entity.train then local train_id = entity.train.id local train = global.trains[train_id] @@ -865,47 +611,7 @@ local function on_rotate(event) 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, event.old_name) - end -end - -local function on_train_built(event) - local train_e = event.train - if event.old_train_id_1 then - on_train_modified(global, event.old_train_id_1) - end - if event.old_train_id_2 then - on_train_modified(global, event.old_train_id_2) - end -end -local function on_train_changed(event) - local train_e = event.train--[[@as LuaTrain]] - if not train_e.valid then return end - local train_id = train_e.id - local train = global.trains[train_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_id, train) - end - else - local depot_id = stop.unit_number--[[@as uint]] - if global.depots[depot_id] then - on_train_arrives_depot(global, depot_id, train_e) - end - end - end - elseif event.old_state == defines.train_state.wait_station then - if train then - on_train_leaves_station(global, mod_settings, train_id, train) - end + update_stop_from_inserter(global, entity) end end @@ -931,6 +637,12 @@ local function on_paste(event) end end +local function on_rename(event) + if event.entity.name == "train-stop" then + on_stop_rename(global, event.entity, event.old_name) + end +end + local function on_settings_changed(event) mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as double]] @@ -1016,29 +728,45 @@ local function setup_se_compat() train.se_awaiting_rename = nil end - if not (train.status == STATUS_D_TO_P or train.status == STATUS_P_TO_R) then return end + if not (train.status == STATUS_TO_P or train.status == STATUS_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)) + if train.status == STATUS_TO_P or train.status == STATUS_TO_R 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 - i = i + 1 + train_entity.schedule = schedule + elseif train.status == STATUS_TO_F then + local refueler = map_data.refuelers[train.refueler_id] + local f_name = refueler.entity_stop.backer_name + local f_surface_i = refueler.entity_stop.surface.index + local records = schedule.records + local i = schedule.current + while i <= #records do + if records[i].station == f_name and f_surface_i ~= old_surface_index then + table_insert(records, i, create_direct_to_station_order(refueler.entity_stop)) + i = i + 1 + end + i = i + 1 + end + train_entity.schedule = schedule end - train_entity.schedule = schedule end interface_raise_train_teleported(new_id, old_id) end) diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index f866716..5573110 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -2,122 +2,6 @@ local flib_migration = require("__flib__.migration") local migrations_table = { - ["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, - ["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, - ["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, - ["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, - ["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.parked_at_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, - ["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, - ["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 - 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 - end, ["1.0.3"] = function() ---@type MapData local map_data = global @@ -194,6 +78,7 @@ local migrations_table = { end end, } +--STATUS_R_TO_D = 5 ---@param data ConfigurationChangedData function on_config_changed(data) diff --git a/cybersyn/scripts/remote-interface.lua b/cybersyn/scripts/remote-interface.lua index 33f427f..f8cf2a5 100644 --- a/cybersyn/scripts/remote-interface.lua +++ b/cybersyn/scripts/remote-interface.lua @@ -330,21 +330,27 @@ end function interface.is_layout_accepted(layout_pattern, layout) return is_layout_accepted(layout_pattern, layout) end ----@param station_id uint +---@param stop_id uint ---@param forbidden_entity LuaEntity? ---@param force_update boolean? -function interface.reset_station_layout(station_id, forbidden_entity, force_update) - local station = global.stations[station_id] - assert(station) - if force_update or not station.allows_all_trains then - reset_station_layout(global, station, forbidden_entity) +function interface.reset_stop_layout(stop_id, forbidden_entity, force_update) + local is_station = true + ---@type Refueler|Station + local stop = global.stations[stop_id] + if not stop then + is_station = false + stop = global.refuelers[stop_id] + end + assert(stop) + if force_update or not stop.allows_all_trains then + reset_stop_layout(global, stop, is_station, forbidden_entity) end end ---@param rail LuaEntity ---@param forbidden_entity LuaEntity? ---@param force_update boolean? -function interface.update_station_from_rail(rail, forbidden_entity, force_update) - update_station_from_rail(global, rail, forbidden_entity, force_update) +function interface.update_stop_from_rail(rail, forbidden_entity, force_update) + update_stop_from_rail(global, rail, forbidden_entity, force_update) end ------------------------------------------------------------------ diff --git a/cybersyn/scripts/train-events.lua b/cybersyn/scripts/train-events.lua new file mode 100644 index 0000000..cb40ef7 --- /dev/null +++ b/cybersyn/scripts/train-events.lua @@ -0,0 +1,432 @@ +--By Mami +local INF = math.huge + +---@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 + 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 + set_combinator_output(map_data, comb, nil) + end + end +end + +---@param map_data MapData +---@param train_id uint +---@param train Train +function on_failed_delivery(map_data, train_id, train) + --NOTE: must either change this train's status or remove it after this call + local p_station_id = train.p_station_id--[[@as uint]] + local r_station_id = train.r_station_id--[[@as uint]] + local manifest = train.manifest--[[@as Manifest]] + local is_p_in_progress = train.status == STATUS_TO_P or train.status == STATUS_P + local is_r_in_progress = is_p_in_progress or train.status == STATUS_TO_R or train.status == STATUS_R + if is_p_in_progress then + local station = map_data.stations[p_station_id] + remove_manifest(map_data, station, manifest, 1) + if train.status == STATUS_P then + set_comb1(map_data, station, nil) + unset_wagon_combs(map_data, station) + end + end + if is_r_in_progress then + local station = map_data.stations[r_station_id] + remove_manifest(map_data, station, manifest, -1) + if train.status == STATUS_R then + set_comb1(map_data, station, nil) + unset_wagon_combs(map_data, station) + end + end + train.r_station_id = nil + train.p_station_id = nil + train.manifest = nil + interface_raise_train_failed_delivery(train_id, is_p_in_progress, p_station_id, is_r_in_progress, r_station_id, manifest) +end + + + +---@param map_data MapData +---@param train_id uint +---@param train Train +function add_available_train(map_data, train_id, train) + local network_name = train.network_name + if network_name then + local network = map_data.available_trains[network_name] + if not network then + network = {} + map_data.available_trains[network_name] = network + end + network[train_id] = true + train.is_available = true + interface_raise_train_available(train_id) + end +end +---@param map_data MapData +---@param mod_settings CybersynModSettings +---@param depot_id uint +---@param depot Depot +---@param train_id uint +---@param train Train +function add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot) + 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.available_trains[network_name] = network + end + network[train_id] = true + train.is_available = true + end + depot.available_train_id = train_id + train.status = STATUS_D + train.parked_at_depot_id = depot_id + train.depot_name = depot.entity_stop.backer_name + train.se_depot_surface_i = depot.entity_stop.surface.index + 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 + interface_raise_train_available(train_id) + end +end +---@param map_data MapData +---@param train_id uint +---@param train Train +function remove_available_train(map_data, train_id, train) + ---@type uint + if train.is_available and train.network_name then + local network = map_data.available_trains[train.network_name--[[@as string]]] + if network then + network[train_id] = nil + if next(network) == nil then + map_data.available_trains[train.network_name] = nil + end + end + train.is_available = nil + end +end + + + + + +---@param map_data MapData +---@param depot_id uint +---@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 + if train.status == STATUS_TO_D then + elseif train.status == STATUS_TO_D_BYPASS then + remove_available_train(map_data, train_id, train) + elseif mod_settings.react_to_train_early_to_depot then + if train.manifest then + on_failed_delivery(map_data, train_id, train) + end + send_unexpected_train_alert(train.entity) + else + return + end + if is_train_empty then + add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id]) + set_depot_schedule(train_entity, train.depot_name) + interface_raise_train_parked_at_depot(train_id, depot_id) + else + --train still has cargo + if mod_settings.react_to_nonempty_train_in_depot then + lock_train(train_entity) + remove_train(map_data, train_id, train) + send_nonempty_train_in_depot_alert(train_entity) + end + interface_raise_train_nonempty_in_depot(depot_id, train_entity, train_id) + end + elseif is_train_empty then + --NOTE: only place where new Train + train = { + entity = train_entity, + --layout_id = set_train_layout, + --item_slot_capacity = set_train_layout, + --fluid_capacity = set_train_layout, + --status = add_available_train_to_depot, + p_station_id = 0, + r_station_id = 0, + manifest = nil, + last_manifest_tick = map_data.total_ticks, + has_filtered_wagon = nil, + --is_available = add_available_train_to_depot, + --parked_at_depot_id = add_available_train_to_depot, + --depot_name = add_available_train_to_depot, + --network_name = add_available_train_to_depot, + --network_flag = add_available_train_to_depot, + --priority = add_available_train_to_depot, + } + set_train_layout(map_data, train) + map_data.trains[train_id] = train + add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id]) + + set_depot_schedule(train_entity, train.depot_name) + interface_raise_train_created(train_id, depot_id) + else + if mod_settings.react_to_nonempty_train_in_depot then + lock_train(train_entity) + send_nonempty_train_in_depot_alert(train_entity) + end + interface_raise_train_nonempty_in_depot(depot_id, train_entity) + end +end +---@param map_data MapData +---@param station_id uint +---@param train_id uint +---@param train Train +local function on_train_arrives_station(map_data, station_id, train_id, train) + if train.manifest then + ---@type uint + if train.status == STATUS_TO_P then + if train.p_station_id == station_id then + train.status = STATUS_P + local station = map_data.stations[station_id] + set_comb1(map_data, station, train.manifest, 1) + set_p_wagon_combs(map_data, station, train) + end + elseif train.status == STATUS_TO_R then + if train.r_station_id == station_id then + train.status = STATUS_R + local station = map_data.stations[station_id] + 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 + --this is player intervention that is considered valid + elseif (train.status == STATUS_R or train.status == STATUS_TO_D or train.status == STATUS_TO_D_BYPASS) and train.r_station_id == station_id then + --this is player intervention that is considered valid + elseif mod_settings.react_to_train_at_incorrect_station then + on_failed_delivery(map_data, train_id, train) + remove_train(map_data, train_id, train) + lock_train(train.entity) + send_lost_train_alert(train.entity, train.depot_name) + end + elseif mod_settings.react_to_train_at_incorrect_station then + --train is lost somehow, probably from player intervention + remove_train(map_data, train_id, train) + send_lost_train_alert(train.entity, train.depot_name) + end +end + +---@param map_data MapData +---@param refueler_id uint +---@param train_id uint +---@param train Train +local function on_train_arrives_refueler(map_data, refueler_id, train_id, train) + if train.status == STATUS_TO_F then + --local refueler = map_data.refuelers[refueler_id] + train.status = STATUS_F + end +end + +---@param map_data MapData +---@param mod_settings CybersynModSettings +---@param train_id uint +---@param train Train +local function on_train_leaves_stop(map_data, mod_settings, train_id, train) + if train.status == STATUS_P then + train.status = STATUS_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 = nil + for carriage_i, carriage in ipairs(train.entity.cargo_wagons) do + 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 + interface_raise_train_completed_provide(train_id) + elseif train.status == STATUS_R then + local station = map_data.stations[train.r_station_id] + remove_manifest(map_data, station, train.manifest, -1) + set_comb1(map_data, station, nil) + unset_wagon_combs(map_data, station) + --complete delivery + train.p_station_id = nil + train.r_station_id = nil + train.manifest = nil + --add to available trains for depot bypass + local fuel_fill = 0 + local total_slots = 0 + for k, v in pairs(train.entity.locomotives) do + if v[1] then + local inv = v[1].get_fuel_inventory() + if inv then + local inv_size = #inv + total_slots = total_slots + inv_size + for i = 1, inv_size do + local item = inv[i--[[@as uint]]] + local count = item.count + if count > 0 then + fuel_fill = fuel_fill + count/get_stack_size(map_data, item.name) + end + end + end + end + end + if total_slots == 0 then + --if total_slots == 0 it's probably a modded electric train + if mod_settings.depot_bypass_threshold < 1 then + train.status = STATUS_TO_D_BYPASS + add_available_train(map_data, train_id, train) + end + elseif fuel_fill/total_slots > mod_settings.depot_bypass_threshold then + train.status = STATUS_TO_D_BYPASS + add_available_train(map_data, train_id, train) + else + local refuelers = map_data.to_refueler[train.network_name] + if refuelers then + local best_refueler_id = nil + local best_dist = INF + local best_prior = -INF + for i, id in ipairs(refuelers) do + local refueler = map_data.refuelers[id] + set_refueler_from_comb(mod_settings, refueler) + if bit32.btest(train.network_flag, refueler.network_flag) and (refueler.allows_all_trains or refueler.accepted_layouts[train.layout_id]) and refueler.trains_total < refueler.entity_stop.trains_limit then + local accepted = false + local dist = nil + if refueler.priority == best_prior then + dist = get_stop_dist(train.entity.front_stock, refueler.entity_stop) + accepted = dist < best_dist + end + if accepted or refueler.priority > best_prior then + best_refueler_id = id + best_dist = dist or get_stop_dist(train.entity.front_stock, refueler.entity_stop) + best_prior = refueler.priority + end + end + end + if best_refueler_id then + train.status = STATUS_TO_F + train.refueler_id = best_refueler_id + local refueler = map_data.refuelers[best_refueler_id] + refueler.trains_total = refueler.trains_total + 1 + add_refueler_schedule(train.entity, refueler.entity_stop) + end + end + end + if train.status == STATUS_R then + --the train has not qualified for depot bypass nor refueling + train.status = STATUS_TO_D + end + interface_raise_train_completed_request(train_id) + elseif train.status == STATUS_F then + local refueler = map_data.refuelers[train.refueler_id] + train.status = STATUS_TO_D_BYPASS + train.refueler_id = nil + refueler.trains_total = refueler.trains_total - 1 + add_available_train(map_data, train_id, train) + elseif train.status == STATUS_D then + --The train is leaving the depot without a manifest, the player likely intervened + local depot = map_data.depots[train.parked_at_depot_id--[[@as uint]]] + remove_train(map_data, train_id, train) + send_lost_train_alert(train.entity, depot.entity_stop.backer_name) + end +end + + +---@param map_data MapData +---@param train_id uint +---@param train Train +function on_train_broken(map_data, train_id, train) + --NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0 + if not train.se_is_being_teleported then + if train.manifest then + on_failed_delivery(map_data, train_id, train) + end + remove_train(map_data, train_id, train) + end +end +---@param map_data MapData +---@param pre_train_id uint +local function on_train_modified(map_data, pre_train_id) + local train = map_data.trains[pre_train_id] + --NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0 + if train and not train.se_is_being_teleported then + if train.manifest then + on_failed_delivery(map_data, pre_train_id, train) + end + remove_train(map_data, pre_train_id, train) + end +end + + +function on_train_built(event) + local train_e = event.train + if event.old_train_id_1 then + on_train_modified(global, event.old_train_id_1) + end + if event.old_train_id_2 then + on_train_modified(global, event.old_train_id_2) + end +end +function on_train_changed(event) + local train_e = event.train--[[@as LuaTrain]] + if not train_e.valid then return end + local train_id = 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 + local id = stop.unit_number--[[@as uint]] + if global.stations[id] then + local train = global.trains[train_id] + if train then + on_train_arrives_station(global, id, train_id, train) + end + elseif global.depots[id] then + on_train_arrives_depot(global, id, train_e) + elseif global.refuelers[id] then + local train = global.trains[train_id] + if train then + on_train_arrives_refueler(global, id, train_id, train) + end + end + end + elseif event.old_state == defines.train_state.wait_station then + local train = global.trains[train_id] + if train then + on_train_leaves_stop(global, mod_settings, train_id, train) + end + end +end