Experimental 1.2.2 (#21)

---------------------------------------------------------------------------------------------------
Version: 1.2.2
Date: 2022-12-29
  Features:
    - Added a station combinator setting to enable or disable the inactivity condition in a train's orders, disabled by default (but not in <=1.2.1 worlds)
    - Added a depot combinator setting to enable depot bypass, enabled by default
    - Added a depot combinator setting to force trains to park at the same depot, enabled by default
    - Added network "each" for depots
    - Added a map setting to modify the default locked slots per cargo wagon value
    - Added a map setting to modify the default priority value
    - Added a map setting to allow trains with cargo at depots, disabled by default
  Changes:
    - Inverted the sign of combinator outputs, a map setting has been added to maintain backwards compatibility with <=1.2.1 worlds
    - Overhauled the wagon control combinator algorithm to spread items out between cargo wagons
    - Trains with cargo held in the depot now check if they have been emptied and reset when they have
    - Cargo capacity is now prioritized over distance when choosing trains
    - Increased the default request threshold to 2000
    - Improved English localization
  Bugfixes:
    - Fixed a bug where trains with cargo sometimes weren't getting held at depots
    - Fixed a crash caused by changing a station combinator to the "each" network during a bad tick
    - Fixed a crash when changing a refueler away from network each
    - Multiple rare bugs and crashes relating to wagon control combinators are fixed
    - Fixed a bug with refueler direct orders not being applied after moving through a space elevator
    - Fixed a bug where filtered slots sometimes weren't being removed
---------------------------------------------------------------------------------------------------
This commit is contained in:
Monica Moniot
2022-12-29 10:02:07 -06:00
committed by GitHub
parent c48d1d3025
commit 7d2f0c2ccd
19 changed files with 833 additions and 511 deletions

View File

@@ -1,8 +1,33 @@
---------------------------------------------------------------------------------------------------
Version: 1.2.2
Date: 2022-12-29
Features:
- Added a station combinator setting to enable or disable the inactivity condition in a train's orders, disabled by default (but not in <=1.2.1 worlds)
- Added a depot combinator setting to enable depot bypass, enabled by default
- Added a depot combinator setting to force trains to park at the same depot, enabled by default
- Added network "each" for depots
- Added a map setting to modify the default locked slots per cargo wagon value
- Added a map setting to modify the default priority value
- Added a map setting to allow trains with cargo at depots, disabled by default
Changes:
- Inverted the sign of combinator outputs, a map setting has been added to maintain backwards compatibility with <=1.2.1 worlds
- Overhauled the wagon control combinator algorithm to spread items out between cargo wagons
- Trains with cargo held in the depot now check if they have been emptied and reset when they have
- Cargo capacity is now prioritized over distance when choosing trains
- Increased the default request threshold to 2000
- Improved English localization
Bugfixes:
- Fixed a bug where trains with cargo sometimes weren't getting held at depots
- Fixed a crash caused by changing a station combinator to the "each" network during a bad tick
- Fixed a crash when changing a refueler away from network each
- Multiple rare bugs and crashes relating to wagon control combinators are fixed
- Fixed a bug with refueler direct orders not being applied after moving through a space elevator
- Fixed a bug where filtered slots sometimes weren't being removed
---------------------------------------------------------------------------------------------------
Version: 1.2.1
Date: 2022-12-24
Bugfixes:
- Fixed an bug where sometimes refuelers would reject trains they should accept
- Fixed a bug where sometimes refuelers would reject trains they should accept
---------------------------------------------------------------------------------------------------
Version: 1.2.0
Date: 2022-12-23
@@ -49,7 +74,7 @@ Date: 2022-12-9
Version: 1.1.3
Date: 2022-12-8
Bugfixes:
- Fixed a crash when removing a fuel loader
- Fixed a crash when removing a refueler
- Fixed a gui bug
- Fixed a crash on newly generated worlds
- Fixed a crash with breaking combinators

View File

@@ -2,6 +2,7 @@
--<goto p_continue> at line 310 jumps into the scope of local 'p_flag'
require("scripts.constants")
require("scripts.global")
require("scripts.lib")
require("scripts.factorio-api")
require("scripts.layout")
require("scripts.central-planning")

View File

@@ -1,6 +1,6 @@
{
"name": "cybersyn",
"version": "1.2.1",
"version": "1.2.2",
"title": "Project Cybersyn",
"author": "Mami",
"factorio_version": "1.1",

View File

@@ -2,21 +2,29 @@
cybersyn-ticks-per-second=Central planning updates per second
cybersyn-update-rate=Central planning update rate
cybersyn-request-threshold=Default request threshold
cybersyn-priority=Default priority
cybersyn-locked-slots=Default locked slots per cargo wagon
cybersyn-network-flag=Default network mask
cybersyn-fuel-threshold=Fuel threshold
cybersyn-depot-bypass-enabled=Depot bypass enabled
cybersyn-warmup-time=Station warmup time (sec)
cybersyn-stuck-train-time=Stuck train timeout (sec)
cybersyn-allow-cargo-in-depot=Allow cargo in depots
cybersyn-invert-sign=Invert combinator output (deprecated)
[mod-setting-description]
cybersyn-ticks-per-second=How many times per second the central planner should update the state of the network and schedule deliveries. This value will be rounded up to a divisor of 60. Setting this to 0 will stop all dispatches.
cybersyn-update-rate=How many stations per tick can be polled at once or can have deliveries scheduled at once. Larger number allow the central planner to keep more up to date on the current state of the network, but at the cost of performance.
cybersyn-request-threshold=The default request threshold when a request threshold signal is not given to a station. When a station receives a negative item signal that surpasses its request threshold, so long as any station exists with a positive signal greater than the request threshold, a delivery of that item will be scheduled between the two stations.
cybersyn-priority=The default priority when a priority signal is not given to a station, depot or refueler. Stations with higher priorities will receive deliveries before stations with lower priorities.
cybersyn-locked-slots=The default number of locked slots per cargo wagon when a "locked slots per cargo wagon" signal is not given to a station. When a provider station has locked slots per cargo wagon of X, any train attempting to make a delivery from it will have its orders modified so every cargo wagon will have at least X item slots left empty. This is necessary to make multi-item provider stations function correctly.
cybersyn-network-flag=The default set of sub-networks a station will service when no network signal is given to a station. This integer is interpretted bit-wise to give 32 possible sub-networks to choose from.
cybersyn-fuel-threshold=What percentage of a train's fuel inventory must be full to skip refueling. If this is set to 1, trains will always visit a fuel loader after completing a delivery.
cybersyn-fuel-threshold=What percentage of a train's fuel inventory must be full to skip refueling. If this is set to 1, trains will always visit a refueler after completing a delivery.
cybersyn-depot-bypass-enabled=If checked, when a train completes a delivery and refueling, it may take a new order from the network before having to return to its depot.
cybersyn-warmup-time=How many seconds a cybernetic combinator will wait before connecting to the Cybersyn network. This is a grace period to modify or correct the circuit network before trains start dispatching to a newly blueprinted station.
cybersyn-stuck-train-time=After this many seconds from a train's dispatch, an alert will be sent to let you know a train is probably stuck and has not completed its delivery. The player will likely have to debug their network to get the train unstuck.
cybersyn-allow-cargo-in-depot=If checked, trains will be allowed to have cargo in depots; no alerts will be generated and the train will not be held. In addition, trains with orders to visit requester stations with "Inactivity condition" checked will wait for inactivity instead of waiting for empty cargo. Useful for creating train systems where depots handle excess cargo. For advanced users only.
cybersyn-invert-sign=Flip the sign of the output of cybernetic combinators to be the same as it is in LTN or in earlier versions of Project Cybersyn.
[item-name]
cybersyn-combinator=Cybernetic combinator
@@ -48,7 +56,7 @@ unexpected-train=A train has unexpectedly returned to the depot before completin
stuck-train=A train is stuck
cannot-path-between-surfaces=A train is attempting to make a delivery between two unconnected surfaces, perhaps put them on separate networks
depot-broken=A train is lost because its depot was broken
refueler-broken=A train is lost because its fuel loader was broken
refueler-broken=A train is lost because its refueler was broken
station-broken=A train is lost because one of its delivery stations was broken
train-at-incorrect=A train parked at a station it was not scheduled to delivered to
missing-train=Could not find any train on the correct network to make a delivery from __2__ to __1__
@@ -61,16 +69,22 @@ combinator-title=Cybernetic combinator
operation=Mode
comb1=Station
depot=Depot
refueler=Fuel loader
refueler=Refueler
comb2=Station control
wagon-manifest=Wagon control
switch-provide=Provide only
switch-request=Request only
switch-provide-tooltip=Lock this station to only provide items to the network. By default it both requests and provides.
switch-request-tooltip=Lock this station to only request items from the network. By default it both requests and provides.
network=Network
network-tooltip=A signal is used to identify which network this combinator is a member of. Trains will only be dispatched from depots to provider and requester stations if they are all identified with the same signal.
allow-list-description=Automatic allow-list
allow-list-tooltip=When checked trains in the network are automatically added to the allow-list if every wagon of the train is able to be loaded or unloaded by this station. When unchecked the allow-list is not used and all trains are allowed to park here.
is-stack-description=Stack thresholds
is-stack-tooltip=When checked all request thresholds for this station are interpretted as a count of item stacks rather than a count of total items. Thresholds for fluids are unaffected.
switch-provide=Provide only
switch-request=Request only
switch-provide-tooltip=Lock this station to only provide items to the network. By default it both requests and provides.
switch-request-tooltip=Lock this station to only request items from the network. By default it both requests and provides.
enable-inactive-description=Inactivity condition
enable-inactive-tooltip=When checked a train at a provider is required to wait for inactivity even if its order has been fulfilled. This is frequently useful for preventing inserters from getting items stuck in their hands.
use-same-depot-description=Require same depot
use-same-depot-tooltip=When checked trains from this depot always return to this depot. When unchecked the train is allowed to return to any depot with the same name as this one.
depot-bypass-description=Depot bypass
depot-bypass-tooltip=When checked trains from this depot do not have to be parked at this depot to receive new orders, they just have to have no current active orders. They will still return to this depot if they are low on fuel and no refuelers are available.

View File

@@ -40,15 +40,13 @@ function create_delivery(map_data, r_station_id, p_station_id, train_id, manifes
local r_station = map_data.stations[r_station_id]
local p_station = map_data.stations[p_station_id]
local train = map_data.trains[train_id]
local depot = map_data.depots[train.depot_id]
local is_at_depot = remove_available_train(map_data, train_id, train)
remove_available_train(map_data, train_id, train)
local depot_id = train.parked_at_depot_id
if depot_id then
map_data.depots[depot_id].available_train_id = nil
train.parked_at_depot_id = nil
end
--NOTE: we assume that the train is not being teleported at this time
if set_manifest_schedule(map_data, train.entity, train.depot_name, train.se_depot_surface_i, p_station.entity_stop, r_station.entity_stop, manifest, depot_id ~= nil) then
--NOTE: set_manifest_schedule is allowed to cancel the delivery at the last second if applying the schedule to the train makes it lost
if set_manifest_schedule(map_data, train.entity, depot.entity_stop, not train.use_any_depot, p_station.entity_stop, p_station.enable_inactive, r_station.entity_stop, mod_settings.allow_cargo_in_depot and r_station.enable_inactive--[[@as boolean]], manifest, is_at_depot) then
local old_status = train.status
train.status = STATUS_TO_P
train.p_station_id = p_station_id
@@ -155,10 +153,12 @@ function create_manifest(map_data, r_station_id, p_station_id, train_id, primary
--locked slots is only taken into account after the train is already approved for dispatch
local locked_slots = p_station.locked_slots
local total_slots_left = train.item_slot_capacity
local total_item_slots
if locked_slots > 0 then
local total_cw = #train.entity.cargo_wagons
total_slots_left = min(total_slots_left, max(total_slots_left - total_cw*locked_slots, total_cw))
local total_cargo_wagons = #train.entity.cargo_wagons
total_item_slots = max(train.item_slot_capacity - total_cargo_wagons*locked_slots, 1)
else
total_item_slots = train.item_slot_capacity
end
local total_liquid_left = train.fluid_capacity
@@ -174,13 +174,13 @@ function create_manifest(map_data, r_station_id, p_station_id, train_id, primary
total_liquid_left = 0--no liquid merging
keep_item = true
end
elseif total_slots_left > 0 then
elseif total_item_slots > 0 then
local stack_size = get_stack_size(map_data, item.name)
local slots = ceil(item.count/stack_size)
if slots > total_slots_left then
item.count = total_slots_left*stack_size
if slots > total_item_slots then
item.count = total_item_slots*stack_size
end
total_slots_left = total_slots_left - slots
total_item_slots = total_item_slots - slots
keep_item = true
end
if keep_item then
@@ -329,7 +329,7 @@ local function tick_dispatch(map_data, mod_settings)
---@type uint
local j = 1
while j <= #p_stations do
local p_flag, r_flag, netand, best_p_train_id, best_t_prior, best_t_to_p_dist, effective_count, override_threshold, p_prior, best_p_dist
local p_flag, r_flag, netand, best_p_train_id, best_t_prior, best_capacity, best_t_to_p_dist, effective_count, override_threshold, p_prior, best_p_dist
local p_station_id = p_stations[j]
local p_station = stations[p_station_id]
@@ -337,8 +337,8 @@ local function tick_dispatch(map_data, mod_settings)
goto p_continue
end
p_flag = p_station.network_name == NETWORK_EACH and (p_station.network_flag[network_name] or 0) or p_station.network_flag
r_flag = r_station.network_name == NETWORK_EACH and (r_station.network_flag[network_name] or 0) or r_station.network_flag
p_flag = get_network_flag(p_station, network_name)
r_flag = get_network_flag(r_station, network_name)
netand = band(p_flag, r_flag)
if netand == 0 then
goto p_continue
@@ -353,11 +353,13 @@ local function tick_dispatch(map_data, mod_settings)
---@type uint?
best_p_train_id = nil
best_t_prior = -INF
best_capacity = 0
best_t_to_p_dist = INF
if trains then
for train_id, _ in pairs(trains) do
local train = map_data.trains[train_id]
if not btest(netand, train.network_flag) or train.se_is_being_teleported then
local train_flag = get_network_flag(train, network_name)
if not btest(netand, train_flag) or train.se_is_being_teleported then
goto train_continue
end
if correctness < 2 then
@@ -397,14 +399,19 @@ local function tick_dispatch(map_data, mod_settings)
if train.priority < best_t_prior then
goto train_continue
end
if train.priority == best_t_prior and capacity < best_capacity then
goto train_continue
end
--check if path is shortest so we prioritize locality
local t_to_p_dist = get_stop_dist(train.entity.front_stock, p_station.entity_stop) - DEPOT_PRIORITY_MULT*train.priority
if train.priority == best_t_prior and t_to_p_dist > best_t_to_p_dist then
if capacity == best_capacity and t_to_p_dist > best_t_to_p_dist then
goto train_continue
end
best_p_train_id = train_id
best_capacity = capacity
best_t_prior = train.priority
best_t_to_p_dist = t_to_p_dist
::train_continue::
@@ -507,10 +514,11 @@ local function tick_poll_station(map_data, mod_settings)
end
end
station.r_threshold = mod_settings.r_threshold
station.priority = 0
station.priority = mod_settings.priority
station.item_priority = nil
station.locked_slots = 0
if station.network_name == NETWORK_EACH then
station.locked_slots = mod_settings.locked_slots
local is_each = station.network_name == NETWORK_EACH
if is_each then
station.network_flag = {}
else
station.network_flag = mod_settings.network_flag
@@ -548,12 +556,12 @@ local function tick_poll_station(map_data, mod_settings)
if item_type == "virtual" then
if item_name == SIGNAL_PRIORITY then
station.priority = item_count
elseif item_name == REQUEST_THRESHOLD and item_count ~= 0 then
elseif item_name == REQUEST_THRESHOLD then
--NOTE: thresholds must be >0 or they can cause a crash
station.r_threshold = abs(item_count)
elseif item_name == LOCKED_SLOTS then
station.locked_slots = max(item_count, 0)
elseif station.network_name == NETWORK_EACH then
elseif is_each then
station.network_flag[item_name] = item_count
end
comb1_signals[k] = nil
@@ -645,8 +653,38 @@ local function tick_poll_station(map_data, mod_settings)
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
local function tick_poll_train(map_data, mod_settings)
function tick_init(map_data, mod_settings)
local tick_data = map_data.tick_data
map_data.economy.all_p_stations = {}
map_data.economy.all_r_stations = {}
map_data.economy.all_names = {}
for i, id in pairs(map_data.warmup_station_ids) do
local station = map_data.stations[id]
if station then
if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps < map_data.total_ticks then
map_data.active_station_ids[#map_data.active_station_ids + 1] = id
map_data.warmup_station_ids[i] = nil
end
else
map_data.warmup_station_ids[i] = nil
end
end
if map_data.queue_station_update then
for id, _ in pairs(map_data.queue_station_update) do
local station = map_data.stations[id]
if station then
local pre = station.allows_all_trains
set_station_from_comb(station)
if station.allows_all_trains ~= pre then
update_stop_if_auto(map_data, station, true)
end
end
end
map_data.queue_station_update = nil
end
--NOTE: the following has undefined behavior if last_train is deleted, this should be ok since the following doesn't care how inconsistent our access pattern is
local train_id, train = next(map_data.trains, tick_data.last_train)
tick_data.last_train = train_id
@@ -657,10 +695,7 @@ local function tick_poll_train(map_data, mod_settings)
end
interface_raise_train_stuck(train_id)
end
end
---@param map_data MapData
local function tick_poll_comb(map_data, mod_settings)
local tick_data = map_data.tick_data
--NOTE: the following has undefined behavior if last_comb is deleted
local comb_id, comb = next(map_data.to_comb, tick_data.last_comb)
tick_data.last_comb = comb_id
@@ -673,6 +708,9 @@ local function tick_poll_comb(map_data, mod_settings)
if refueler_id then
set_refueler_from_comb(map_data, mod_settings, refueler_id)
end
map_data.tick_state = STATE_POLL_STATIONS
interface_raise_tick_init()
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
@@ -680,30 +718,13 @@ function tick(map_data, mod_settings)
map_data.total_ticks = map_data.total_ticks + 1
if map_data.active_alerts then
if map_data.total_ticks%(10*mod_settings.tps) < 1 then
if map_data.total_ticks%(8*mod_settings.tps) < 1 then
process_active_alerts(map_data)
end
end
if map_data.tick_state == STATE_INIT then
map_data.economy.all_p_stations = {}
map_data.economy.all_r_stations = {}
map_data.economy.all_names = {}
for i, id in pairs(map_data.warmup_station_ids) do
local station = map_data.stations[id]
if station then
if station.last_delivery_tick + mod_settings.warmup_time*mod_settings.tps < map_data.total_ticks then
map_data.active_station_ids[#map_data.active_station_ids + 1] = id
map_data.warmup_station_ids[i] = nil
end
else
map_data.warmup_station_ids[i] = nil
end
end
map_data.tick_state = STATE_POLL_STATIONS
interface_raise_tick_init()
tick_poll_train(map_data, mod_settings)
tick_poll_comb(map_data)
tick_init(map_data, mod_settings)
elseif map_data.tick_state == STATE_POLL_STATIONS then
for i = 1, mod_settings.update_rate do
if tick_poll_station(map_data, mod_settings) then break end

View File

@@ -22,6 +22,12 @@ MODE_DEPOT = "+"
MODE_WAGON = "-"
MODE_REFUELER = ">>"
SETTING_DISABLE_ALLOW_LIST = 2
SETTING_IS_STACK = 3
SETTING_ENABLE_INACTIVE = 4
SETTING_USE_ANY_DEPOT = 5
SETTING_DISABLE_DEPOT_BYPASS = 6
NETWORK_SIGNAL_DEFAULT = {name="signal-A", type="virtual"}
NETWORK_EACH = "signal-each"
INACTIVITY_TIME = 100

View File

@@ -39,15 +39,32 @@ function se_get_space_elevator_name(surface)
end
---@param train LuaTrain
function get_any_train_entity(train)
return train.front_stock or train.back_stock or train.carriages[1]
end
---@param e Station|Refueler|Train
---@param network_name string
function get_network_flag(e, network_name)
return e.network_name == NETWORK_EACH and (e.network_flag[network_name] or 0) or e.network_flag
end
------------------------------------------------------------------------------
--[[train schedules]]--
------------------------------------------------------------------------------
local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = INACTIVITY_TIME}
local condition_wait_inactive = {type = "inactivity", compare_type = "and", ticks = INACTIVITY_TIME}
local condition_only_inactive = {condition_wait_inactive}
local condition_unloading_order = {{type = "empty", compare_type = "and"}}
local condition_direct_to_station = {{type = "time", compare_type = "and", ticks = 1}}
---@param stop LuaEntity
---@param manifest Manifest
function create_loading_order(stop, manifest)
---@param enable_inactive boolean
function create_loading_order(stop, manifest, enable_inactive)
local condition = {}
for _, item in ipairs(manifest) do
local cond_type
@@ -63,26 +80,30 @@ function create_loading_order(stop, manifest)
condition = {comparator = "", first_signal = {type = item.type, name = item.name}, constant = item.count}
}
end
condition[#condition + 1] = create_loading_order_condition
if enable_inactive then
condition[#condition + 1] = condition_wait_inactive
end
return {station = stop.backer_name, wait_conditions = condition}
end
local create_unloading_order_condition = {{type = "empty", compare_type = "and"}}
---@param stop LuaEntity
function create_unloading_order(stop)
return {station = stop.backer_name, wait_conditions = create_unloading_order_condition}
---@param enable_inactive boolean
function create_unloading_order(stop, enable_inactive)
if enable_inactive then
return {station = stop.backer_name, wait_conditions = condition_only_inactive}
else
return {station = stop.backer_name, wait_conditions = condition_unloading_order}
end
end
local create_inactivity_order_condition = {{type = "inactivity", compare_type = "and", ticks = INACTIVITY_TIME}}
---@param depot_name string
function create_inactivity_order(depot_name)
return {station = depot_name, wait_conditions = create_inactivity_order_condition}
return {station = depot_name, wait_conditions = condition_only_inactive}
end
local create_direct_to_station_order_condition = {{type = "time", compare_type = "and", ticks = 1}}
---@param stop LuaEntity
function create_direct_to_station_order(stop)
return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction, wait_conditions = create_direct_to_station_order_condition}
return {rail = stop.connected_rail, rail_direction = stop.connected_rail_direction, wait_conditions = condition_direct_to_station}
end
---@param train LuaTrain
@@ -138,13 +159,15 @@ function se_create_elevator_order(elevator_name, is_train_in_orbit)
end
---@param map_data MapData
---@param train LuaTrain
---@param depot_name string
---@param d_surface_i int
---@param depot_stop LuaEntity
---@param same_depot boolean
---@param p_stop LuaEntity
---@param p_enable_inactive boolean
---@param r_stop LuaEntity
---@param r_enable_inactive boolean
---@param manifest Manifest
---@param start_at_depot boolean?
function set_manifest_schedule(map_data, train, depot_name, d_surface_i, p_stop, r_stop, manifest, start_at_depot)
function set_manifest_schedule(map_data, train, depot_stop, same_depot, p_stop, p_enable_inactive, r_stop, r_enable_inactive, manifest, start_at_depot)
--NOTE: can only return false if start_at_depot is false, it should be incredibly rare that this function returns false
local old_schedule
if not start_at_depot then
@@ -153,6 +176,7 @@ function set_manifest_schedule(map_data, train, depot_name, d_surface_i, p_stop,
local t_surface = train.front_stock.surface
local p_surface = p_stop.surface
local r_surface = r_stop.surface
local d_surface_i = depot_stop.surface.index
local t_surface_i = t_surface.index
local p_surface_i = p_surface.index
local r_surface_i = r_surface.index
@@ -160,15 +184,19 @@ function set_manifest_schedule(map_data, train, depot_name, d_surface_i, p_stop,
local is_r_on_t = t_surface_i == r_surface_i
local is_d_on_t = t_surface_i == d_surface_i
if is_p_on_t and is_r_on_t and is_d_on_t then
local records = {
create_inactivity_order(depot_stop.backer_name),
create_direct_to_station_order(p_stop),
create_loading_order(p_stop, manifest, p_enable_inactive),
create_direct_to_station_order(r_stop),
create_unloading_order(r_stop, r_enable_inactive),
}
if same_depot then
records[6] = create_direct_to_station_order(depot_stop)
end
train.schedule = {
current = start_at_depot and 1 or 2--[[@as uint]],
records = {
create_inactivity_order(depot_name),
create_direct_to_station_order(p_stop),
create_loading_order(p_stop, manifest),
create_direct_to_station_order(r_stop),
create_unloading_order(r_stop),
}
records = records
}
if old_schedule and not train.has_path then
train.schedule = old_schedule
@@ -186,14 +214,14 @@ function set_manifest_schedule(map_data, train, depot_name, d_surface_i, p_stop,
if is_train_in_orbit or t_zone.orbit_index == other_zone.index then
local elevator_name = se_get_space_elevator_name(t_surface)
if elevator_name then
local records = {create_inactivity_order(depot_name)}
local records = {create_inactivity_order(depot_stop.backer_name)}
if t_surface_i == p_surface_i then
records[#records + 1] = create_direct_to_station_order(p_stop)
else
records[#records + 1] = se_create_elevator_order(elevator_name, is_train_in_orbit)
is_train_in_orbit = not is_train_in_orbit
end
records[#records + 1] = create_loading_order(p_stop, manifest)
records[#records + 1] = create_loading_order(p_stop, manifest, p_enable_inactive)
if p_surface_i ~= r_surface_i then
records[#records + 1] = se_create_elevator_order(elevator_name, is_train_in_orbit)
@@ -201,7 +229,7 @@ function set_manifest_schedule(map_data, train, depot_name, d_surface_i, p_stop,
elseif t_surface_i == r_surface_i then
records[#records + 1] = create_direct_to_station_order(r_stop)
end
records[#records + 1] = create_unloading_order(r_stop)
records[#records + 1] = create_unloading_order(r_stop, r_enable_inactive)
if r_surface_i ~= d_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
@@ -221,9 +249,9 @@ function set_manifest_schedule(map_data, train, depot_name, d_surface_i, p_stop,
end
--NOTE: create a schedule that cannot be fulfilled, the train will be stuck but it will give the player information what went wrong
train.schedule = {current = 1, records = {
create_inactivity_order(depot_name),
create_loading_order(p_stop, manifest),
create_unloading_order(r_stop),
create_inactivity_order(depot_stop.backer_name),
create_loading_order(p_stop, manifest, p_enable_inactive),
create_unloading_order(r_stop, r_enable_inactive),
}}
lock_train(train)
send_alert_cannot_path_between_surfaces(map_data, train)
@@ -306,6 +334,78 @@ function get_comb_network_name(comb)
return signal and signal.name or nil
end
---@param station Station
function set_station_from_comb(station)
--NOTE: this does nothing to update currently active deliveries
--NOTE: this can only be called at the tick init boundary
local params = get_comb_params(station.entity_comb1)
local signal = params.first_signal
local bits = params.second_constant or 0
local is_pr_state = bit_extract(bits, 0, 2)
local allows_all_trains = bit_extract(bits, SETTING_DISABLE_ALLOW_LIST) > 0
local is_stack = bit_extract(bits, SETTING_IS_STACK) > 0
local enable_inactive = bit_extract(bits, SETTING_ENABLE_INACTIVE) > 0
station.network_name = signal and signal.name or nil
station.allows_all_trains = allows_all_trains
station.is_stack = is_stack
station.enable_inactive = enable_inactive
station.is_p = (is_pr_state == 0 or is_pr_state == 1) or nil
station.is_r = (is_pr_state == 0 or is_pr_state == 2) or nil
if station.network_name == NETWORK_EACH then
station.network_flag = {}
end
end
---@param mod_settings CybersynModSettings
---@param train Train
---@param comb LuaEntity
function set_train_from_comb(mod_settings, train, comb)
--NOTE: this does nothing to update currently active deliveries
local params = get_comb_params(comb)
local signal = params.first_signal
local network_name = signal and signal.name or nil
local bits = params.second_constant or 0
local disable_bypass = bit_extract(bits, SETTING_DISABLE_DEPOT_BYPASS) > 0
local use_any_depot = bit_extract(bits, SETTING_USE_ANY_DEPOT) > 0
train.network_name = network_name
train.disable_bypass = disable_bypass
train.use_any_depot = use_any_depot
local is_each = train.network_name == NETWORK_EACH
if is_each then
train.network_flag = {}
else
train.network_flag = mod_settings.network_flag
end
train.priority = mod_settings.priority
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_type = v.signal.type
local item_count = v.count
if item_name then
if item_type == "virtual" then
if item_name == SIGNAL_PRIORITY then
train.priority = item_count
elseif is_each then
if item_name ~= REQUEST_THRESHOLD and item_name ~= LOCKED_SLOTS then
train.network_flag[item_name] = item_count
end
end
end
if item_name == network_name then
train.network_flag = item_count
end
end
end
end
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
---@param id uint
@@ -316,12 +416,14 @@ function set_refueler_from_comb(map_data, mod_settings, id)
local bits = params.second_constant or 0
local signal = params.first_signal
local old_network = refueler.network_name
local old_network_flag = refueler.network_flag
refueler.network_name = signal and signal.name or nil
refueler.allows_all_trains = bit_extract(bits, 2) > 0
refueler.priority = 0
refueler.allows_all_trains = bit_extract(bits, SETTING_DISABLE_ALLOW_LIST) > 0
refueler.priority = mod_settings.priority
if refueler.network_name == NETWORK_EACH then
local is_each = refueler.network_name == NETWORK_EACH
if is_each then
map_data.each_refuelers[id] = true
refueler.network_flag = {}
else
@@ -339,8 +441,10 @@ function set_refueler_from_comb(map_data, mod_settings, id)
if item_type == "virtual" then
if item_name == SIGNAL_PRIORITY then
refueler.priority = item_count
elseif refueler.network_name == NETWORK_EACH then
refueler.network_flag[item_name] = item_count
elseif is_each then
if item_name ~= REQUEST_THRESHOLD and item_name ~= LOCKED_SLOTS then
refueler.network_flag[item_name] = item_count
end
end
end
if item_name == refueler.network_name then
@@ -352,7 +456,7 @@ function set_refueler_from_comb(map_data, mod_settings, id)
local f, a
if old_network == NETWORK_EACH then
f, a = pairs(refueler.network_flag--[[@as {[string]: int}]])
f, a = pairs(old_network_flag--[[@as {[string]: int}]])
elseif old_network ~= refueler.network_name then
f, a = once, old_network
else
@@ -406,24 +510,6 @@ function update_display(map_data, station)
end
end
---@param station Station
function set_station_from_comb_state(station)
--NOTE: this does nothing to update currently active deliveries
local params = get_comb_params(station.entity_comb1)
local signal = params.first_signal
local bits = params.second_constant or 0
local is_pr_state = bit_extract(bits, 0, 2)
local allows_all_trains = bit_extract(bits, 2) > 0
local is_stack = bit_extract(bits, 3) > 0
station.network_name = signal and signal.name or nil
station.allows_all_trains = allows_all_trains
station.is_stack = is_stack
station.is_p = (is_pr_state == 0 or is_pr_state == 1) or nil
station.is_r = (is_pr_state == 0 or is_pr_state == 2) or nil
end
---@param comb LuaEntity
function get_comb_gui_settings(comb)
local params = get_comb_params(comb)
@@ -433,8 +519,6 @@ function get_comb_gui_settings(comb)
local switch_state = "none"
local bits = params.second_constant or 0
local is_pr_state = bit_extract(bits, 0, 2)
local allows_all_trains = bit_extract(bits, 2) > 0
local is_stack = bit_extract(bits, 3) > 0
if is_pr_state == 0 then
switch_state = "none"
elseif is_pr_state == 1 then
@@ -454,7 +538,7 @@ function get_comb_gui_settings(comb)
elseif op == MODE_WAGON then
selected_index = 5
end
return selected_index, params.first_signal, switch_state, not allows_all_trains, is_stack
return selected_index, params.first_signal, switch_state, bits
end
---@param comb LuaEntity
---@param is_pr_state 0|1|2
@@ -467,23 +551,14 @@ function set_comb_is_pr_state(comb, is_pr_state)
control.parameters = param
end
---@param comb LuaEntity
---@param allows_all_trains boolean
function set_comb_allows_all_trains(comb, allows_all_trains)
---@param n int
---@param bit boolean
function set_comb_setting(comb, n, bit)
local control = get_comb_control(comb)
local param = control.parameters
local bits = param.second_constant or 0
param.second_constant = bit_replace(bits, allows_all_trains and 1 or 0, 2)
control.parameters = param
end
---@param comb LuaEntity
---@param is_stack boolean
function set_comb_is_stack(comb, is_stack)
local control = get_comb_control(comb)
local param = control.parameters
local bits = param.second_constant or 0
param.second_constant = bit_replace(bits, is_stack and 1 or 0, 3)
param.second_constant = bit_replace(bits, bit and 1 or 0, n)
control.parameters = param
end
---@param comb LuaEntity
@@ -540,22 +615,25 @@ end
---@param map_data MapData
---@param station Station
function set_comb2(map_data, station)
local sign = mod_settings.invert_sign and -1 or 1
if station.entity_comb2 then
local deliveries = station.deliveries
local signals = {}
for item_name, count in pairs(deliveries) do
local i = #signals + 1
local is_fluid = game.item_prototypes[item_name] == nil--NOTE: this is expensive
signals[i] = {index = i, signal = {type = is_fluid and "fluid" or "item", name = item_name}, count = -count}
signals[i] = {index = i, signal = {type = is_fluid and "fluid" or "item", name = item_name}, count = sign*count}
end
set_combinator_output(map_data, station.entity_comb2, signals)
end
end
------------------------------------------------------------------------------
--[[alerts]]--
------------------------------------------------------------------------------
---@param train LuaTrain
---@param icon {}
---@param message string
@@ -694,7 +772,14 @@ function process_active_alerts(map_data)
if id == 1 then
send_alert_for_train(train, send_stuck_train_alert_icon, "cybersyn-messages.stuck-train")
elseif id == 2 then
send_alert_for_train(train, send_nonempty_train_in_depot_alert_icon, "cybersyn-messages.nonempty-train")
--this is an alert that we have to actively check if we can clear
local is_train_empty = next(train.get_contents()) == nil and next(train.get_fluid_contents()) == nil
if is_train_empty then
--NOTE: this function could get confused being called internally, be sure it can handle that
on_train_changed({train = train})
else
send_alert_for_train(train, send_nonempty_train_in_depot_alert_icon, "cybersyn-messages.nonempty-train")
end
elseif id == 3 then
send_alert_for_train(train, send_lost_train_alert_icon, "cybersyn-messages.depot-broken")
elseif id == 4 then

View File

@@ -9,6 +9,7 @@
---@field public stations {[uint]: Station}
---@field public active_station_ids uint[]
---@field public warmup_station_ids uint[]
---@field public queue_station_update {[uint]: true?}?
---@field public depots {[uint]: Depot}
---@field public refuelers {[uint]: Refueler}
---@field public trains {[uint]: Train}
@@ -29,6 +30,7 @@
---@field public is_p true?
---@field public is_r true?
---@field public is_stack true?
---@field public enable_inactive true?
---@field public allows_all_trains true?
---@field public deliveries_total int
---@field public last_delivery_tick int
@@ -50,7 +52,7 @@
---@class Depot
---@field public entity_stop LuaEntity
---@field public entity_comb LuaEntity
---@field public available_train_id uint?--train_id
---@field public available_train_id uint?--train_id, only present when a train is parked here
---@class Refueler
---@field public entity_stop LuaEntity
@@ -76,13 +78,13 @@
---@field public last_manifest_tick int
---@field public has_filtered_wagon true?
---@field public is_available true?
---@field public parked_at_depot_id uint?
---@field public depot_name string
---@field public depot_id uint
---@field public use_any_depot true?
---@field public disable_bypass true?
---@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
---@field public se_awaiting_rename any? --se only
@@ -105,17 +107,21 @@
---@field public tps double
---@field public update_rate int
---@field public r_threshold int
---@field public priority int
---@field public locked_slots int
---@field public network_flag int
---@field public warmup_time double
---@field public stuck_train_time double
---@field public fuel_threshold double
---@field public depot_bypass_enabled boolean
---@field public invert_sign boolean
---@field public allow_cargo_in_depot boolean
---@field public missing_train_alert_enabled boolean --interface setting
---@field public stuck_train_alert_enabled boolean --interface setting
---@field public react_to_nonempty_train_in_depot boolean --interface setting
---@field public react_to_train_at_incorrect_station boolean --interface setting
---@field public react_to_train_early_to_depot boolean --interface setting
--if this is uncommented it means there are migrations to write
---@alias cybersyn.global MapData
---@type CybersynModSettings
mod_settings = {}
@@ -150,9 +156,3 @@ function init_global()
IS_SE_PRESENT = remote.interfaces["space-exploration"] ~= nil
end
---@param v string
---@param h string?
function once(v, h)
return not h and v or nil--[[@as string|nil]]
end

View File

@@ -26,25 +26,37 @@ STATUS_NAMES[defines.entity_status.disabled_by_script] = "entity-status.disabled
STATUS_NAMES[defines.entity_status.marked_for_deconstruction] = "entity-status.marked-for-deconstruction"
STATUS_NAMES_DEFAULT = "entity-status.disabled"
local bit_extract = bit32.extract
local function setting(bits, n)
return bit_extract(bits, n) > 0
end
local function setting_flip(bits, n)
return bit_extract(bits, n) == 0
end
---@param main_window LuaGuiElement
---@param selected_index int
local function set_visibility(main_window, selected_index)
local uses_network = selected_index == 1 or selected_index == 2 or selected_index == 3
local uses_allow_list = selected_index == 1 or selected_index == 3
local is_station = selected_index == 1
local is_depot = selected_index == 2
local uses_network = is_station or is_depot or selected_index == 3
local uses_allow_list = is_station or selected_index == 3
local vflow = main_window.frame.vflow--[[@as LuaGuiElement]]
local top_flow = vflow.top--[[@as LuaGuiElement]]
local bottom_flow = vflow.bottom--[[@as LuaGuiElement]]
local right_flow = bottom_flow.right--[[@as LuaGuiElement]]
local first_settings = bottom_flow.first--[[@as LuaGuiElement]]
local depot_settings = bottom_flow.depot--[[@as LuaGuiElement]]
top_flow.is_pr_switch.visible = is_station
vflow.network_label.visible = uses_network
bottom_flow.network.visible = uses_network
right_flow.allow_list.visible = uses_allow_list
--right_flow.allow_list_label.visible = uses_allow_list
right_flow.is_stack.visible = is_station
--right_flow.is_stack_label.visible = is_station
first_settings.allow_list.visible = uses_allow_list
first_settings.is_stack.visible = is_station
bottom_flow.enable_inactive.visible = is_station
depot_settings.visible = is_depot
end
---@param comb LuaEntity
@@ -53,7 +65,7 @@ function gui_opened(comb, player)
combinator_update(global, comb, true)
local rootgui = player.gui.screen
local selected_index, signal, switch_state, allow_list, is_stack = get_comb_gui_settings(comb)
local selected_index, signal, switch_state, bits = get_comb_gui_settings(comb)
local window = flib_gui.build(rootgui, {
{type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={
@@ -69,8 +81,8 @@ function gui_opened(comb, player)
{type="flow", name="vflow", direction="vertical", style_mods={horizontal_align="left"}, children={
--status
{type="flow", style="status_flow", direction="horizontal", style_mods={vertical_align="center", horizontally_stretchable=true, bottom_padding=4}, children={
{type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}},
{type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}, ref={"status_label"}}
{type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", style_mods={stretch_image_to_widget_size=true}},
{type="label", caption={STATUS_NAMES[comb.status] or STATUS_NAMES_DEFAULT}}
}},
--preview
{type="frame", style="deep_frame_in_shallow_frame", style_mods={minimal_width=0, horizontally_stretchable=true, padding=0}, children={
@@ -94,25 +106,45 @@ function gui_opened(comb, player)
}},
---choose-elem-button
{type="line", style_mods={top_padding=10}},
{type="label", name="network_label", ref={"network_label"}, style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=8}},
{type="flow", name="bottom", direction="horizontal", style_mods={vertical_align="center"}, children={
{type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=signal, style_mods={bottom_margin=1, right_margin=6, top_margin=2}, actions={
{type="label", name="network_label", style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=8}},
{type="flow", name="bottom", direction="horizontal", style_mods={vertical_align="top"}, children={
{type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=signal, style_mods={bottom_margin=1, right_margin=6, top_margin=2}, actions={
on_elem_changed={"choose-elem-button", comb.unit_number}
}},
{type="flow", name="right", direction="vertical", style_mods={horizontal_align="left"}, children={
{type="flow", name="allow_list", direction="horizontal", style_mods={vertical_align="center"}, children={
{type="checkbox", name="allow_list", ref={"allow_list"}, state=allow_list, tooltip={"cybersyn-gui.allow-list-tooltip"}, actions={
on_checked_state_changed={"allow_list", comb.unit_number}
{type="flow", name="depot", direction="vertical", style_mods={horizontal_align="left"}, children={
{type="flow", name="use_any_depot", direction="horizontal", style_mods={vertical_align="center"}, children={
{type="checkbox", name="use_same_depot", state=setting_flip(bits, SETTING_USE_ANY_DEPOT), tooltip={"cybersyn-gui.use-same-depot-tooltip"}, actions={
on_checked_state_changed={"setting-flip", comb.unit_number, SETTING_USE_ANY_DEPOT}
}},
{type="label", name="allow_list_label", style_mods={left_padding=3}, ref={"allow_list_label"}, caption={"cybersyn-gui.allow-list-description"}},
{type="label", name="use_same_depot_label", style_mods={left_padding=3}, caption={"cybersyn-gui.use-same-depot-description"}},
}},
{type="flow", name="depot_bypass", direction="horizontal", style_mods={vertical_align="center"}, children={
{type="checkbox", name="depot_bypass", state=setting_flip(bits, SETTING_DISABLE_DEPOT_BYPASS), tooltip={"cybersyn-gui.depot-bypass-tooltip"}, actions={
on_checked_state_changed={"setting-flip", comb.unit_number, SETTING_DISABLE_DEPOT_BYPASS}
}},
{type="label", name="depot_bypass_label", style_mods={left_padding=3}, caption={"cybersyn-gui.depot-bypass-description"}},
}},
}},
{type="flow", name="first", direction="vertical", style_mods={horizontal_align="left", right_margin=8}, children={
{type="flow", name="allow_list", direction="horizontal", style_mods={vertical_align="center"}, children={
{type="checkbox", name="allow_list", state=setting_flip(bits, SETTING_DISABLE_ALLOW_LIST), tooltip={"cybersyn-gui.allow-list-tooltip"}, actions={
on_checked_state_changed={"setting-flip", comb.unit_number, SETTING_DISABLE_ALLOW_LIST}
}},
{type="label", name="allow_list_label", style_mods={left_padding=3}, caption={"cybersyn-gui.allow-list-description"}},
}},
{type="flow", name="is_stack", direction="horizontal", style_mods={vertical_align="center"}, children={
{type="checkbox", name="is_stack", ref={"is_stack"}, state=is_stack, tooltip={"cybersyn-gui.is-stack-tooltip"}, actions={
on_checked_state_changed={"is_stack", comb.unit_number}
{type="checkbox", name="is_stack", state=setting(bits, SETTING_IS_STACK), tooltip={"cybersyn-gui.is-stack-tooltip"}, actions={
on_checked_state_changed={"setting", comb.unit_number, SETTING_IS_STACK}
}},
{type="label", name="is_stack_label", style_mods={left_padding=3}, ref={"is_stack_label"}, caption={"cybersyn-gui.is-stack-description"}},
{type="label", name="is_stack_label", style_mods={left_padding=3}, caption={"cybersyn-gui.is-stack-description"}},
}},
}}
}},
{type="flow", name="enable_inactive", direction="horizontal", style_mods={vertical_align="center"}, children={
{type="checkbox", name="enable_inactive", state=setting(bits, SETTING_ENABLE_INACTIVE), tooltip={"cybersyn-gui.enable-inactive-tooltip"}, actions={
on_checked_state_changed={"setting", comb.unit_number, SETTING_ENABLE_INACTIVE}
}},
{type="label", name="enable_inactive_label", style_mods={left_padding=3}, caption={"cybersyn-gui.enable-inactive-description"}},
}},
}}
}}
}}
@@ -197,38 +229,30 @@ function register_gui_actions()
local comb = global.to_comb[msg[2]]
if not comb or not comb.valid then return end
local param = get_comb_params(comb)
local signal = element.elem_value
if signal and (signal.name == "signal-everything" or signal.name == "signal-anything" or signal.name == "signal-each") then
if param.operation == MODE_PRIMARY_IO or param.operation == MODE_PRIMARY_IO_ACTIVE or param.operation == MODE_PRIMARY_IO_FAILED_REQUEST or param.operation == MODE_REFUELER then
signal.name = NETWORK_EACH
else
signal = nil
end
signal.name = NETWORK_EACH
element.elem_value = signal
end
set_comb_network_name(comb, signal)
combinator_update(global, comb)
elseif msg[1] == "allow_list" then
elseif msg[1] == "setting" then
local element = event.element
if not element then return end
local comb = global.to_comb[msg[2]]
if not comb or not comb.valid then return end
local allows_all_trains = not element.state
set_comb_allows_all_trains(comb, allows_all_trains)
set_comb_setting(comb, msg[3], element.state)
combinator_update(global, comb)
elseif msg[1] == "is_stack" then
elseif msg[1] == "setting-flip" then
local element = event.element
if not element then return end
local comb = global.to_comb[msg[2]]
if not comb or not comb.valid then return end
local is_stack = element.state
set_comb_is_stack(comb, is_stack)
set_comb_setting(comb, msg[3], not element.state)
combinator_update(global, comb)
elseif msg[1] == "is_pr_switch" then

View File

@@ -3,37 +3,10 @@ local area = require("__flib__.area")
local abs = math.abs
local floor = math.floor
local ceil = math.ceil
local min = math.min
local max = math.max
local function table_compare(t0, t1)
if #t0 ~= #t1 then
return false
end
for i = 0, #t0 do
if t0[i] ~= t1[i] then
return false
end
end
return true
end
---@param a any[]
---@param i uint
local function iterr(a, i)
i = i + 1
if i <= #a then
local r = a[#a - i + 1]
return i, r
else
return nil, nil
end
end
---@param a any[]
local function irpairs(a)
return iterr, a, 0
end
---@param layout_pattern (0|1|2|3)[]
---@param layout (0|1|2)[]
function is_refuel_layout_accepted(layout_pattern, layout)
@@ -81,11 +54,6 @@ end
---@param train_id uint
---@param train Train
function remove_train(map_data, train_id, train)
local parked_at_depot_id = train.parked_at_depot_id
if parked_at_depot_id then
local depot = map_data.depots[parked_at_depot_id]
depot.available_train_id = nil
end
remove_available_train(map_data, train_id, train)
local layout_id = train.layout_id
@@ -191,9 +159,31 @@ 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--[[@as Manifest]]
if not manifest[1] then return end
local sign = mod_settings.invert_sign and 1 or -1
local is_reversed = get_train_direction(station.entity_stop, train.entity)
local locked_slots = station.locked_slots
local percent_slots_to_use_per_wagon = 1.0
if train.item_slot_capacity > 0 then
local total_item_slots
if locked_slots > 0 then
local total_cargo_wagons = #train.entity.cargo_wagons
total_item_slots = max(train.item_slot_capacity - total_cargo_wagons*locked_slots, 1)
else
total_item_slots = train.item_slot_capacity
end
local to_be_used_item_slots = 0
for i, item in ipairs(train.manifest) do
if item.type == "item" then
to_be_used_item_slots = to_be_used_item_slots + ceil(item.count/get_stack_size(map_data, item.name))
end
end
percent_slots_to_use_per_wagon = min(to_be_used_item_slots/total_item_slots, 1.0)
end
local item_i = 1
local item = manifest[item_i]
local item_count = item.count
@@ -215,39 +205,29 @@ function set_p_wagon_combs(map_data, station, train)
end
end
if carriage.type == "cargo-wagon" and item_i <= #manifest then
local signals = {}
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
if inv then
local signals = {}
local inv_filter_i = 1
local item_slots_capacity = #inv - station.locked_slots
local item_slots_capacity = max(ceil((#inv - locked_slots)*percent_slots_to_use_per_wagon), 1)
while item_slots_capacity > 0 do
local do_inc = false
local do_inc
if item.type == "item" then
local stack_size = get_stack_size(map_data, item.name)
local item_slots = ceil(item_count/stack_size)
local i = #signals + 1
local slots_to_filter
if item_slots > item_slots_capacity then
if comb then
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_slots_capacity*stack_size}
end
item_slots_capacity = 0
item_count = item_count - item_slots_capacity*stack_size
slots_to_filter = item_slots_capacity
else
if comb then
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_count}
end
item_slots_capacity = item_slots_capacity - item_slots
do_inc = true
slots_to_filter = item_slots
end
for j = 1, slots_to_filter do
local count_to_fill = min(item_slots_capacity*stack_size, item_count)
local slots_to_fill = ceil(count_to_fill/stack_size)
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = sign*count_to_fill}
item_count = item_count - count_to_fill
item_slots_capacity = item_slots_capacity - slots_to_fill
for j = 1, slots_to_fill do
inv.set_filter(inv_filter_i, item.name)
inv_filter_i = inv_filter_i + 1
end
train.has_filtered_wagon = true
do_inc = item_count == 0
else
do_inc = true
end
@@ -271,27 +251,18 @@ function set_p_wagon_combs(map_data, station, train)
local signals = {}
while fluid_capacity > 0 do
local do_inc = false
local do_inc
if fluid.type == "fluid" then
if fluid_count > fluid_capacity then
if comb then
signals[1] = {index = 1, signal = {type = fluid.type, name = fluid.name}, count = fluid_capacity}
end
fluid_capacity = 0
fluid_count = fluid_count - fluid_capacity
else
if comb then
signals[1] = {index = 1, signal = {type = fluid.type, name = fluid.name}, count = item_count}
end
fluid_capacity = fluid_capacity - fluid_count
fluid_i = fluid_i + 1
if fluid_i <= #manifest then
fluid = manifest[fluid_i]
fluid_count = fluid.count
end
end
break
local count_to_fill = min(fluid_count, fluid_capacity)
signals[1] = {index = 1, signal = {type = fluid.type, name = fluid.name}, count = sign*count_to_fill}
fluid_count = fluid_count - count_to_fill
fluid_capacity = 0
do_inc = fluid_count == 0
else
do_inc = true
end
if do_inc then
fluid_i = fluid_i + 1
if fluid_i <= #manifest then
fluid = manifest[fluid_i]
@@ -315,6 +286,7 @@ function set_r_wagon_combs(map_data, station, train)
local carriages = train.entity.carriages
local is_reversed = get_train_direction(station.entity_stop, train.entity)
local sign = mod_settings.invert_sign and -1 or 1
local ivpairs = is_reversed and irpairs or ipairs
for carriage_i, carriage in ivpairs(carriages) do
@@ -336,7 +308,7 @@ function set_r_wagon_combs(map_data, station, train)
local stack = inv[stack_i]
if stack.valid_for_read then
local i = #signals + 1
signals[i] = {index = i, signal = {type = "item", name = stack.name}, count = -stack.count}
signals[i] = {index = i, signal = {type = "item", name = stack.name}, count = sign*stack.count}
end
end
set_combinator_output(map_data, comb, signals)
@@ -347,7 +319,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 = -floor(count)}
signals[i] = {index = i, signal = {type = "fluid", name = fluid_name}, count = sign*floor(count)}
end
set_combinator_output(map_data, comb, signals)
end

51
cybersyn/scripts/lib.lua Normal file
View File

@@ -0,0 +1,51 @@
--By Mami
---@param v string
---@param h string?
function once(v, h)
return not h and v or nil--[[@as string|nil]]
end
---@param t any[]
---@return any
function rnext_consume(t)
local len = #t
if len > 1 then
local i = math.random(1, len)
local v = t[i]
t[i] = t[len]
t[len] = nil
return v
else
local v = t[1]
t[1] = nil
return v
end
end
function table_compare(t0, t1)
if #t0 ~= #t1 then
return false
end
for i = 0, #t0 do
if t0[i] ~= t1[i] then
return false
end
end
return true
end
---@param a any[]
---@param i uint
function irnext(a, i)
i = i + 1
if i <= #a then
local r = a[#a - i + 1]
return i, r
else
return nil, nil
end
end
---@param a any[]
function irpairs(a)
return irnext, a, 0
end

View File

@@ -1,9 +0,0 @@
function log_dispatch(network_name, r_stop, p_stop)
end
function log_refuel(network_name, refuel_stop)
end

View File

@@ -1,5 +1,4 @@
--By Mami
local floor = math.floor
local ceil = math.ceil
local table_insert = table.insert
@@ -23,12 +22,24 @@ end
---@param depot_id uint
---@param depot 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]
lock_train(train.entity)
send_alert_depot_of_train_broken(map_data, train.entity)
remove_train(map_data, train_id, train)
for train_id, train in pairs(map_data.trains) do
if train.depot_id == depot_id then
if train.use_any_depot then
local e = get_any_train_entity(train.entity)
local stops = e.force.get_train_stops({name = depot.entity_stop.backer_name, surface = e.surface})
for stop in rnext_consume, stops do
local new_depot_id = stop.unit_number
if map_data.depots[new_depot_id] then
train.depot_id = new_depot_id--[[@as uint]]
goto continue
end
end
end
lock_train(train.entity)
send_alert_depot_of_train_broken(map_data, train.entity)
remove_train(map_data, train_id, train)
end
::continue::
end
map_data.depots[depot_id] = nil
interface_raise_depot_removed(depot_id, depot)
@@ -105,17 +116,17 @@ local function on_station_built(map_data, stop, comb1, comb2)
entity_stop = stop,
entity_comb1 = comb1,
entity_comb2 = comb2,
--is_p = set_station_from_comb_state,
--is_r = set_station_from_comb_state,
--allows_all_trains = set_station_from_comb_state,
--is_p = set_station_from_comb,
--is_r = set_station_from_comb,
--allows_all_trains = set_station_from_comb,
deliveries_total = 0,
last_delivery_tick = map_data.total_ticks,
priority = 0,
item_priotity = nil,
r_threshold = 0,
locked_slots = 0,
--network_name = set_station_from_comb_state,
network_flag = 0,
--network_name = set_station_from_comb,
--network_flag = set_station_from_comb,
wagon_combs = nil,
deliveries = {},
accepted_layouts = {},
@@ -125,7 +136,6 @@ local function on_station_built(map_data, stop, comb1, comb2)
item_thresholds = nil,
display_state = 0,
}
set_station_from_comb_state(station)
local id = stop.unit_number--[[@as uint]]
map_data.stations[id] = station
map_data.warmup_station_ids[#map_data.warmup_station_ids + 1] = id
@@ -350,8 +360,8 @@ function combinator_update(map_data, comb, reset_display)
local params = control.parameters
local old_params = map_data.to_comb_params[unit_number]
local has_changed = false
local station
local id
local station = nil
local id = nil
if params.operation == MODE_PRIMARY_IO_ACTIVE or params.operation == MODE_PRIMARY_IO_FAILED_REQUEST or params.operation == MODE_PRIMARY_IO then
@@ -361,7 +371,10 @@ function combinator_update(map_data, comb, reset_display)
if stop then
id = stop.unit_number--[[@as uint]]
station = map_data.stations[id]
if should_reset and station and station.entity_comb1 == comb then
if station.entity_comb1 ~= comb then
station = nil
end
if should_reset and station then
--make sure only MODE_PRIMARY_IO gets stored on map_data.to_comb_params
if station.display_state == 0 then
params.operation = MODE_PRIMARY_IO
@@ -396,12 +409,12 @@ function combinator_update(map_data, comb, reset_display)
local stop = map_data.to_stop[comb.unit_number]
if stop and stop.valid then
id = stop.unit_number
station = map_data.stations[id]
if station then
if station.entity_comb1 == comb then
station.network_name = new_network
--NOTE: these updates have to be queued to occur at tick init since central planning is expecting them not to change between ticks
if not map_data.queue_station_update then
map_data.queue_station_update = {}
end
map_data.queue_station_update[id] = true
else
local depot = map_data.depots[id]
if depot then
@@ -426,11 +439,11 @@ function combinator_update(map_data, comb, reset_display)
if params.second_constant ~= old_params.second_constant then
has_changed = true
if station then
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)
--NOTE: these updates have to be queued to occur at tick init since central planning is expecting them not to change between ticks
if not map_data.queue_station_update then
map_data.queue_station_update = {}
end
map_data.queue_station_update[id] = true
else
local refueler = map_data.refuelers[id]
if refueler then
@@ -553,13 +566,6 @@ local function on_stop_rename(map_data, stop, old_name)
end
end
end
else
local depot = map_data.depots[station_id]
if depot and depot.available_train_id then
local train = map_data.trains[depot.available_train_id--[[@as uint]]]
train.depot_name = stop.backer_name
--train.se_depot_surface_i = stop.surface.index
end
end
end
@@ -653,28 +659,28 @@ local function on_rename(event)
end
local function on_settings_changed(event)
mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as double]]
mod_settings.update_rate = settings.global["cybersyn-update-rate"].value --[[@as int]]
mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]]
mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]]
mod_settings.fuel_threshold = settings.global["cybersyn-fuel-threshold"].value--[[@as double]]
mod_settings.depot_bypass_enabled = settings.global["cybersyn-depot-bypass-enabled"].value--[[@as boolean]]
mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as double]]
mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as double]]
if event.setting == "cybersyn-ticks-per-second" then
if mod_settings.tps > DELTA then
local nth_tick = ceil(60/mod_settings.tps)--[[@as uint]];
script.on_nth_tick(nth_tick, function()
tick(global, mod_settings)
end)
else
script.on_nth_tick(nil)
---@param schedule TrainSchedule
---@param stop LuaEntity
---@param old_surface_index uint
local function se_add_direct_to_station_order(schedule, stop, old_surface_index)
local surface_i = stop.surface.index
if surface_i ~= old_surface_index then
local name = stop.backer_name
local records = schedule.records
for i = schedule.current, #records do
if records[i].station == name then
if i == 1 then
--we are assuming this is the depot order
records[#records + 1] = create_direct_to_station_order(stop)
schedule.current = #records--[[@as uint]]
else
table_insert(records, i, create_direct_to_station_order(stop))
end
break
end
end
end
interface_raise_on_mod_settings_changed(event)
end
local function setup_se_compat()
IS_SE_PRESENT = remote.interfaces["space-exploration"] ~= nil
if not IS_SE_PRESENT then return end
@@ -687,9 +693,6 @@ local function setup_se_compat()
---@type MapData
local map_data = global
local old_id = event.old_train_id_1
--NOTE: this is not guaranteed to be unique, it should be fine since the window of time for another train to mistakenly steal this train's event data is miniscule
--NOTE: please SE dev if you read this fix the issue where se_on_train_teleport_finished_event is returning the wrong old train id
local train_unique_identifier = event.train.front_stock.backer_name
local train = map_data.trains[old_id]
if not train then return end
@@ -706,19 +709,27 @@ local function setup_se_compat()
---@type uint
local new_id = train_entity.id
local old_surface_index = event.old_surface_index
local train_unique_identifier = event.train.front_stock.backer_name
--NOTE: event.old_train_id_1 from this event is useless, it's for one of the many transient trains SE spawns while teleporting the old train, only se_on_train_teleport_started_event returns the correct old train id
--NOTE: please SE dev if you read this fix the issue where se_on_train_teleport_finished_event is returning the wrong old train id
local old_id = event.old_train_id_1
local train = map_data.trains[old_id]
if not train then return end
if train.is_available then
local network = map_data.available_trains[train.network_name--[[@as string]]]
if network then
network[new_id] = true
network[old_id] = nil
local f, a
if train.network_name == NETWORK_EACH then
f, a = next, train.network_flag
else
f, a = once, train.network_name
end
for network_name in f, a do
local network = map_data.available_trains[network_name]
if network then
network[new_id] = true
network[old_id] = nil
if next(network) == nil then
map_data.available_trains[network_name] = nil
end
end
end
end
@@ -737,51 +748,60 @@ local function setup_se_compat()
train.se_awaiting_rename = nil
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
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
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
if train.status == STATUS_TO_P then
local stop = map_data.stations[train.p_station_id].entity_stop
se_add_direct_to_station_order(schedule, stop, old_surface_index)
end
if train.status == STATUS_TO_P or train.status == STATUS_TO_R then
local stop = map_data.stations[train.r_station_id].entity_stop
se_add_direct_to_station_order(schedule, stop, old_surface_index)
end
if train.status == STATUS_TO_F then
local stop = map_data.refuelers[train.refueler_id].entity_stop
se_add_direct_to_station_order(schedule, stop, old_surface_index)
end
if not train.use_any_depot then
local depot = map_data.depots[train.depot_id]
se_add_direct_to_station_order(schedule, depot.entity_stop, old_surface_index)
end
train_entity.schedule = schedule
end
interface_raise_train_teleported(new_id, old_id)
end)
end
local function grab_all_settings()
mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as double]]
mod_settings.update_rate = settings.global["cybersyn-update-rate"].value --[[@as int]]
mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]]
mod_settings.priority = settings.global["cybersyn-priority"].value--[[@as int]]
mod_settings.locked_slots = settings.global["cybersyn-locked-slots"].value--[[@as int]]
mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]]
mod_settings.fuel_threshold = settings.global["cybersyn-fuel-threshold"].value--[[@as double]]
mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as double]]
mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as double]]
mod_settings.allow_cargo_in_depot = settings.global["cybersyn-allow-cargo-in-depot"].value--[[@as boolean]]
mod_settings.invert_sign = settings.global["cybersyn-invert-sign"].value--[[@as boolean]]
end
local function on_settings_changed(event)
grab_all_settings()
if event.setting == "cybersyn-ticks-per-second" then
if mod_settings.tps > DELTA then
local nth_tick = ceil(60/mod_settings.tps)--[[@as uint]];
script.on_nth_tick(nth_tick, function()
tick(global, mod_settings)
end)
else
script.on_nth_tick(nil)
end
end
interface_raise_on_mod_settings_changed(event)
end
local filter_built = {
{filter = "name", name = "train-stop"},
{filter = "name", name = COMBINATOR_NAME},
@@ -798,18 +818,10 @@ local filter_broken = {
{filter = "rolling-stock"},
}
local function main()
mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as double]]
mod_settings.update_rate = settings.global["cybersyn-update-rate"].value --[[@as int]]
mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]]
mod_settings.network_flag = settings.global["cybersyn-network-flag"].value--[[@as int]]
mod_settings.fuel_threshold = settings.global["cybersyn-fuel-threshold"].value--[[@as double]]
mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as double]]
mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as double]]
mod_settings.depot_bypass_enabled = settings.global["cybersyn-depot-bypass-enabled"].value--[[@as boolean]]
grab_all_settings()
mod_settings.missing_train_alert_enabled = true
mod_settings.stuck_train_alert_enabled = true
mod_settings.react_to_nonempty_train_in_depot = true
mod_settings.react_to_train_at_incorrect_station = true
mod_settings.react_to_train_early_to_depot = true
@@ -848,6 +860,10 @@ local function main()
register_gui_actions()
script.on_init(function()
local setting = settings.global["cybersyn-invert-sign"]
setting.value = false
settings.global["cybersyn-invert-sign"] = setting
mod_settings.invert_sign = false
init_global()
setup_se_compat()
end)

View File

@@ -1,3 +1,4 @@
--By Mami
local flib_migration = require("__flib__.migration")
@@ -103,23 +104,40 @@ local migrations_table = {
map_data.each_refuelers = {}
map_data.se_tele_old_id = nil
for k, comb in pairs(map_data.to_comb) do
for id, comb in pairs(map_data.to_comb) do
local control = get_comb_control(comb)
local params = control.parameters
local params_old = map_data.to_comb_params[id]
local bits = params.second_constant or 0
local bits_old = params_old.second_constant or 0
local allows_all_trains = bits%2
local is_pr_state = math.floor(bits/2)%3
local allows_all_trains_old = bits_old%2
local is_pr_state_old = math.floor(bits_old/2)%3
local new_bits = bit32.bor(is_pr_state, allows_all_trains*4)
params.second_constant = new_bits
bits = bit32.bor(is_pr_state, allows_all_trains*4)
bits_old = bit32.bor(is_pr_state_old, allows_all_trains_old*4)
params.second_constant = bits
params_old.second_constant = bits_old
control.parameters = params
map_data.to_comb_params[id] = params_old
end
for id, station in pairs(map_data.stations) do
station.display_state = (station.display_state >= 2 and 1 or 0) + (station.display_state%2)*2
set_station_from_comb_state(station)
update_stop_if_auto(map_data, station, true)
local params = get_comb_params(station.entity_comb1)
local bits = params.second_constant or 0
local is_pr_state = bit32.extract(bits, 0, 2)
local allows_all_trains = bit32.extract(bits, SETTING_DISABLE_ALLOW_LIST) > 0
local is_stack = bit32.extract(bits, SETTING_IS_STACK) > 0
station.allows_all_trains = allows_all_trains
station.is_stack = is_stack
station.is_p = (is_pr_state == 0 or is_pr_state == 1) or nil
station.is_r = (is_pr_state == 0 or is_pr_state == 2) or nil
end
map_data.layout_train_count = {}
@@ -135,6 +153,77 @@ local migrations_table = {
end
end
end,
["1.2.2"] = function()
---@type MapData
local map_data = global
local setting = settings.global["cybersyn-invert-sign"]
setting.value = true
settings.global["cybersyn-invert-sign"] = setting
for id, comb in pairs(map_data.to_comb) do
local control = get_comb_control(comb)
local params = control.parameters
local params_old = map_data.to_comb_params[id]
local bits = params.second_constant or 0
local bits_old = params_old.second_constant or 0
bits = bit32.replace(bits, 1, SETTING_ENABLE_INACTIVE)--[[@as int]]
bits = bit32.replace(bits, 1, SETTING_ENABLE_INACTIVE)--[[@as int]]
bits_old = bit32.replace(bits_old, 1, SETTING_USE_ANY_DEPOT)--[[@as int]]
bits_old = bit32.replace(bits_old, 1, SETTING_USE_ANY_DEPOT)--[[@as int]]
params.second_constant = bits
params_old.second_constant = bits_old
control.parameters = params
map_data.to_comb_params[id] = params_old
end
for _, station in pairs(map_data.stations) do
station.enable_inactive = true
end
for train_id, train in pairs(map_data.trains) do
train.depot_id = train.parked_at_depot_id
if not train.depot_id then
local e = get_any_train_entity(train.entity)
local stops = e.force.get_train_stops({name = train.depot_name, surface = e.surface})
for stop in rnext_consume, stops do
local new_depot_id = stop.unit_number
if map_data.depots[new_depot_id] then
train.depot_id = new_depot_id--[[@as uint]]
break
end
end
end
if not train.depot_id then
train.depot_id = next(map_data.depots)
end
if not train.depot_id then
train.entity.manual_mode = true
send_alert_depot_of_train_broken(map_data, train.entity)
local layout_id = train.layout_id
local count = global.layout_train_count[layout_id]
if count <= 1 then
global.layout_train_count[layout_id] = nil
global.layouts[layout_id] = nil
for _, stop in pairs(global.stations) do
stop.accepted_layouts[layout_id] = nil
end
for _, stop in pairs(global.refuelers) do
stop.accepted_layouts[layout_id] = nil
end
else
global.layout_train_count[layout_id] = count - 1
end
map_data.trains[train_id] = nil
end
train.use_any_depot = true
train.disable_bypass = nil
train.depot_name = nil
train.se_depot_surface_i = nil
train.parked_at_depot_id = nil
end
end
}
--STATUS_R_TO_D = 5

View File

@@ -48,6 +48,18 @@ function on_failed_delivery(map_data, train_id, train)
unset_wagon_combs(map_data, station)
end
end
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
train.r_station_id = nil
train.p_station_id = nil
train.manifest = nil
@@ -60,14 +72,21 @@ end
---@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
if train.network_name then
local f, a
if train.network_name == NETWORK_EACH then
f, a = next, train.network_flag
else
f, a = once, train.network_name
end
for network_name in f, a do
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
end
network[train_id] = true
train.is_available = true
interface_raise_train_available(train_id)
end
@@ -80,77 +99,56 @@ end
---@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
set_train_from_comb(mod_settings, train, comb)
depot.available_train_id = train_id
train.depot_id = depot_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
add_available_train(map_data, train_id, train)
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
if train.is_available then
train.is_available = nil
local f, a
if train.network_name == NETWORK_EACH then
f, a = next, train.network_flag
else
f, a = once, train.network_name
end
for network_name in f, a do
local network = map_data.available_trains[network_name]
if network then
network[train_id] = nil
if next(network) == nil then
map_data.available_trains[network_name] = nil
end
end
end
train.is_available = nil
local depot = map_data.depots[train.depot_id]
if depot.available_train_id == train_id then
depot.available_train_id = nil
return true
end
end
return false
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 is_train_empty = next(train_entity.get_contents()) == nil and next(train_entity.get_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 or train.status == STATUS_D then
--shouldn't be possible to get train.status == STATUS_D
elseif train.status == STATUS_TO_D_BYPASS or train.status == STATUS_D then
remove_available_train(map_data, train_id, train)
elseif mod_settings.react_to_train_early_to_depot then
if train.manifest then
@@ -160,21 +158,19 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity)
else
return
end
if is_train_empty then
if is_train_empty or mod_settings.allow_cargo_in_depot then
local old_status = train.status
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)
local depot = map_data.depots[depot_id]
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
set_depot_schedule(train_entity, depot.entity_stop.backer_name)
interface_raise_train_status_changed(train_id, old_status, STATUS_D)
else
--train still has cargo
if mod_settings.react_to_nonempty_train_in_depot then
lock_train_to_depot(train_entity)
remove_train(map_data, train_id, train)
send_alert_nonempty_train_in_depot(map_data, train_entity)
end
interface_raise_train_nonempty_in_depot(depot_id, train_entity, train_id)
lock_train_to_depot(train_entity)
remove_train(map_data, train_id, train)
send_alert_nonempty_train_in_depot(map_data, train_entity)
end
elseif is_train_empty then
elseif is_train_empty or mod_settings.allow_cargo_in_depot then
--NOTE: only place where new Train
train = {
entity = train_entity,
@@ -188,22 +184,25 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity)
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,
--depot_id = add_available_train_to_depot,
--use_any_depot = add_available_train_to_depot,
--disable_bypass = 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,
}
}--[[@as Train]]
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])
local depot = map_data.depots[depot_id]
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
set_depot_schedule(train_entity, train.depot_name)
set_depot_schedule(train_entity, depot.entity_stop.backer_name)
interface_raise_train_created(train_id, depot_id)
else
if mod_settings.react_to_nonempty_train_in_depot then
send_alert_nonempty_train_in_depot(map_data, train_entity)
end
lock_train_to_depot(train_entity)
send_alert_nonempty_train_in_depot(map_data, train_entity)
end
if not is_train_empty then
interface_raise_train_nonempty_in_depot(depot_id, train_entity)
end
end
@@ -218,7 +217,7 @@ local function on_train_arrives_station(map_data, station_id, train_id, train)
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_comb1(map_data, station, train.manifest, mod_settings.invert_sign and 1 or -1)
set_p_wagon_combs(map_data, station, train)
interface_raise_train_status_changed(train_id, STATUS_TO_P, STATUS_P)
end
@@ -226,7 +225,7 @@ local function on_train_arrives_station(map_data, station_id, train_id, train)
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_comb1(map_data, station, train.manifest, mod_settings.invert_sign and -1 or 1)
set_r_wagon_combs(map_data, station, train)
interface_raise_train_status_changed(train_id, STATUS_TO_R, STATUS_R)
end
@@ -316,45 +315,54 @@ local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
end
if fuel_fill > mod_settings.fuel_threshold then
--if fuel_fill == INF, it's probably a modded electric train
if mod_settings.depot_bypass_enabled then
if not train.disable_bypass then
train.status = STATUS_TO_D_BYPASS
add_available_train(map_data, train_id, train)
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D_BYPASS)
return
end
else
local refuelers = map_data.to_refuelers[train.network_name]
if refuelers then
local best_refueler_id = nil
local best_dist = INF
local best_prior = -INF
for id, _ in pairs(refuelers) do
local refueler = map_data.refuelers[id]
set_refueler_from_comb(map_data, mod_settings, id)
local f, a
if train.network_name == NETWORK_EACH then
f, a = next, train.network_flag
else
f, a = once, train.network_name
end
for network_name in f, a do
local refuelers = map_data.to_refuelers[network_name]
if refuelers then
local best_refueler_id = nil
local best_dist = INF
local best_prior = -INF
for id, _ in pairs(refuelers) do
local refueler = map_data.refuelers[id]
set_refueler_from_comb(map_data, mod_settings, id)
local refueler_network_flag = refueler.network_name == NETWORK_EACH and (refueler.network_flag[train.network_name] or 0) or refueler.network_flag
if 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
local refueler_network_flag = get_network_flag(refueler, network_name)
local train_network_flag = get_network_flag(train, network_name)
if 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
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(map_data, train.entity, refueler.entity_stop)
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_F)
return
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(map_data, train.entity, refueler.entity_stop)
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_F)
return
end
end
end
end
@@ -367,7 +375,7 @@ local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
refueler.trains_total = refueler.trains_total - 1
unset_wagon_combs(map_data, refueler)
set_combinator_output(map_data, refueler.entity_comb, nil)
if mod_settings.depot_bypass_enabled then
if not train.disable_bypass then
train.status = STATUS_TO_D_BYPASS
add_available_train(map_data, train_id, train)
else

View File

@@ -18,28 +18,37 @@ data:extend({
minimum_value = 1,
maximum_value = 2147483647,
},
--{
-- type = "int-setting",
-- name = "cybersyn-wait-time",
-- order = "ab",
-- setting_type = "runtime-global",
-- default_value = 2000,
-- minimum_value = 1,
-- maximum_value = 2147483647,
--},
{
type = "double-setting",
type = "int-setting",
name = "cybersyn-request-threshold",
order = "ac",
order = "ba",
setting_type = "runtime-global",
default_value = 1,
minimum_value = 0,
default_value = 2000,
minimum_value = 1,
maximum_value = 2147483647,
},
{
type = "int-setting",
name = "cybersyn-priority",
order = "bb",
setting_type = "runtime-global",
default_value = 0,
minimum_value = -2147483648,
maximum_value = 2147483647,
},
{
type = "int-setting",
name = "cybersyn-locked-slots",
order = "bc",
setting_type = "runtime-global",
default_value = 0,
minimum_value = 0,
maximum_value = 1000,
},
{
type = "int-setting",
name = "cybersyn-network-flag",
order = "ad",
order = "bd",
setting_type = "runtime-global",
default_value = -1,
minimum_value = -2147483648,
@@ -48,23 +57,16 @@ data:extend({
{
type = "double-setting",
name = "cybersyn-fuel-threshold",
order = "ae",
order = "be",
setting_type = "runtime-global",
default_value = .5,
minimum_value = 0,
maximum_value = 1,
},
{
type = "bool-setting",
name = "cybersyn-depot-bypass-enabled",
order = "af",
setting_type = "runtime-global",
default_value = true,
},
{
type = "double-setting",
name = "cybersyn-warmup-time",
order = "ag",
order = "ca",
setting_type = "runtime-global",
default_value = 20,
minimum_value = 0,
@@ -73,10 +75,24 @@ data:extend({
{
type = "double-setting",
name = "cybersyn-stuck-train-time",
order = "ah",
order = "cb",
setting_type = "runtime-global",
default_value = 600,
minimum_value = 0,
maximum_value = 2147483647,
},
{
type = "bool-setting",
name = "cybersyn-allow-cargo-in-depot",
order = "cc",
setting_type = "runtime-global",
default_value = false,
},
{
type = "bool-setting",
name = "cybersyn-invert-sign",
order = "da",
setting_type = "runtime-global",
default_value = false,
},
})