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