Merge pull request #8 from mamoniot/experimental

version 1.1.0
This commit is contained in:
Monica Moniot
2022-12-08 16:11:24 -05:00
committed by GitHub
23 changed files with 1658 additions and 1177 deletions
+39 -8
View File
@@ -4,6 +4,19 @@ Behold one of the most feature-rich and performant train logistics network mods
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/outpost-resupply-station.png)
## Quick Start Guide
Within Project Cybersyn, you can think of requester stations as requester chests, provider stations as passive provider chests, depots as roboports and trains as the logistics bots. There is a direct correspondence between the Cybersyn train network and Factorio's robot logistics network.
A bare minimum Cybersyn train network consists of 2 components: depots and stations. Both are created by placing a cybernetic combinator adjacent to a train stop. Select the "Control Mode" of the combinator to "Station" to create a station, and to "Depot" to create a depot. Create a basic train and order it to park at the depot you just created, it is now controlled by the Cybersyn network. Depots and stations can have any train stop name, names do not impact their function. The circuit network input of a station's cybernetic combinator determines what items that station will request or provide to the Cybersyn network. A positive item signal is interpreted as that station providing that item to the network; A negative item signal is interpreted as that station requesting that item from the network.
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/basic-provider.png)
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/basic-requester.png)
To make a basic provider station, create an item buffer of chests or tanks adjacent to the station's tracks, and connect that buffer by wire to the input of the cybernetic combinator. To make a basic requester station, repeat the same, except reverse the direction of the inserters or pumps so they are *unloading* instead of loading; then connect a constant combinator to the same circuit network, set it to output the number of item you want this requester station to keep loaded in its item buffer, and flip the sign of each so the signal strength is negative. Once the provider station contains the item being requested, a train will automatically be sent to deliver that item from the provider station to the requester station. The requester station's buffer will automatically be topped up on the item being requested.
Follow the above directions and you have set up a bare minimum Cybersyn network! You may continue adding onto it with more stations and depots and Cybersyn will automatically manage all of them for you. At some point you may notice small hiccups within this network, like trains attempting to deliver a tiny amount of an item instead of a full cargo load, or you may want to extend your network with things like multi-item stations or centralized train refueling. In either case refer to **Mod Details** below for a in depth explanation of every feature within this mod.
## Features
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/gui-modes.png)
@@ -24,6 +37,10 @@ These all combine to make it possible to **create "universal" stations**; statio
Stations can **automatically build allow-lists for trains** they can load or unload. Inserters or pumps adjacent to the station's tracks are auto-detected. No more deadlocks caused by trains mistakenly attempting to fulfill a delivery to a station that cannot unload it. This feature is compatible with miniloaders.
Trains can automatically visit **centralized fuel loaders**. It is not required that every single depot loads trains with fuel.
Trains can **bypass visiting the depot** if they have enough fuel. Trains spend far more time productively making deliveries rather than travelling to and from their depot. Fewer reserve trains are needed as a result.
**Easy and versatile ways to define separate train networks.** Bitwise network masks are now optional! Networks are identified by signal id first, then by signal strength.
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/fault-alert.png)
@@ -48,25 +65,35 @@ If you like my work, consider supporting me on [ko-fi](https://ko-fi.com/lesbian
This mod adds a single new entity to the game, the cybernetic combinator. This combinator can be in one of 4 different possible control modes. While each mode has a purpose, the only modes you have to use are primary station control and depot control.
### Primary station control combinator
### Station mode
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/multi-item.png)
When placed adjacent to a vanilla train stop, a Cybersyn station is created. This station can provide or request items to your train network. Connect the input of the combinator to a circuit network; When a positive item signal is received, this station will provide that item to the network, when a negative signal is received, this station will request that item from the network. When a station is providing an item that another station is requesting, a train order will automatically be generated to transfer those items from the providing station to the requesting station. When a train arrives to fulfill this order, the output of the combinator will give the full list of items expected to be loaded (positive) or unloaded (negative) from the train.
### Depot control combinator
Stations can automatically build allow-lists. When this option is enabled, only trains that can be loaded or unloaded by this station will be allowed to make deliveries to it. Stations determine this based on what inserters or pumps are present at this station along its tracks. When disabled, all trains within the network are allowed.
Stations can be set to provide only or request only. By default stations can both provide and request, but when one of these options is chosen either requesting is disabled or providing is disabled.
### Depot mode
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/big-depot.png)
When placed adjacent to a vanilla train stop, a Cybersyn depot is created. Any train which parks at this depot will automatically be added to the train network. Whenever a train order is generated, if this train has the cargo capacity to fulfill it, and is allow-listed by both stations, then it will automatically be dispatched to fulfill the order. When the order is completed, the train will return to any train stop with the same name as the depot it first parked in. This almost always means it returns to a Cybersyn depot where it will again await to fulfill a new order. To save on UPS the input of a depot control combinator is only read when a train parks at the depot; this only matters for networks which make extensive use of network masks on depots.
When placed adjacent to a vanilla train stop, a Cybersyn depot is created. Any train which parks at this depot will automatically be added to the train network. Whenever a train order is generated, if this train has the cargo capacity to fulfill it, and is allow-listed by both stations, then it will automatically be dispatched to fulfill the order. When the order is completed, the train will return to any train stop with the same name as the depot it first parked in. This almost always means it returns to a Cybersyn depot where it will again await to fulfill a new order. To save on UPS the input of a depot control combinator is only read when a train parks at the depot; this is only relevant for networks which make extensive use of network masks on depots.
### Optional station control combinator
### Fuel loader mode
When placed adjacent to a vanilla train stop, a Cybersyn fuel loader is created. Whenever a train completes a delivery, if it is running low on fuel (configurable in mod settings), it will attempt to visit a fuel loader before returning to the depot. The train will search for a fuel loader within its network that has not exceeded its train limit and that it is allow-listed for. If one is found it will schedule a detour to it to stock back up on fuel.
Fuel loaders can automatically build allow-lists. When this option is enabled, trains will be prevented from parking at this station if one of their cargo wagons would be filled with fuel.
### Station info mode
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/science.png)
When placed adjacent to the train stop of an already existing Cybersyn station, this combinator will provide a second set of inputs and outputs that can be used to more precisely control this station. The combinator input allows for request thresholds to be set per-item. Any non-zero item signal given on the input circuit network will override the station's request thresholds for just that item. The output of the combinator gives the sum total of all item loading or unloading orders in progress for the station. The very tick a train is dispatched for a new order to the station, that order is added to the output of this combinator, and it is removed as soon as the train leaves the station. The primary use case for this is to prevent duplicate orders from being generated for stations that provide the same pool of items. Only one train can be dispatched per-tick per-item specifically to accommodate this.
### Wagon control combinator
### Wagon info mode
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/filtered-slots.png)
@@ -76,7 +103,9 @@ When placed adjacent to the tracks of an already existing Cybersyn station, this
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/gui-network.png)
Stations and depots can be set to belong to a particular network by setting that network on the control combinator. By default all combinators belong to the "signal-A" network. By setting a different signal Id, the combinator will belong to that different network. Networks identified with different signal Ids do not share any trains or items; Orders will never be generated to transfer items between separate networks. In addition, if the combinator receives as input a signal of the same Id as its network signal Id, then the value of this signal will be interpreted as a bitmask to give 32 "sub-networks" to choose from. Each station can belong to any set of sub-networks based on its mask signal. A delivery will only be made between two stations if any bit matches between the two masks, i.e. if `mask1 & mask2 > 0`. When a network id is an item, that item will be ignored by stations, its signal will only ever be interpreted as the network mask.
Stations and depots can be set to belong to a particular network by setting that network on the control combinator. By default all combinators belong to the "signal-A" network. By setting a different signal Id, the combinator will belong to that different network. Networks identified with different signal Ids do not share any trains or items; Orders will never be generated to transfer items between separate networks.
In addition, if the combinator receives as input a signal of the same Id as its network signal Id, then the value of this signal will be interpreted as a bitmask to give 32 "sub-networks" to choose from. Each station can belong to any set of sub-networks based on its mask. A delivery will only be made between two stations if any two bits match between the two masks, i.e. if `mask1 & mask2 > 0`. When a network Id is an item, that item will be ignored by stations, its signal will only ever be interpreted as the network mask.
### Request threshold
@@ -90,8 +119,10 @@ After an order has been generated, enough items will be subtracted from that ord
### Priority
Orders will be generated first for stations and depots which are receiving a higher priority signal than the others. If stations have the same priority, the least recently used request station will be prioritized, and the provide station closest to the request station will be prioritized. So in times of item shortage (front-pressure), round robin distribution will be used, and in times of item surplus (back-pressure), minimum travel distance distribution will be used.
Orders will be generated first for stations, depots and fuel loaders which are receiving a higher priority signal than the others. If stations have the same priority, the least recently used requester station will be prioritized, and the provider station closest to the requester station will be prioritized. So in times of item shortage (front-pressure), round robin distribution will be used, and in times of item surplus (back-pressure), minimum travel distance distribution will be used.
If a combinator set to station info mode receives a priority signal, for each item signal input to the combinator, items of that type will have its priority overridden in addition to its request threshold. This effectively allows you to choose one of two possible priorities for each item that a station processes.
### Train limits
Works based off of the train limit set on the train stop in the same way it does in vanilla Factorio. Only a number of trains up to the train limit will be allowed to dispatch to the station by the central planner. Useful to reduce the need for train stackers and prevent deadlocks.
Works based off of the train limit set on the train stop in the same way it does in vanilla Factorio. Only a number of trains up to the train limit will be allowed to dispatch to the station by the central planner. Useful to reduce the need for train stackers and to prevent deadlocks.
+9
View File
@@ -111,3 +111,12 @@ Version: 1.0.9
Date: 2022-12-3
Features:
- Fixed a bug with SE compat preventing players from joining multiplayer games
---------------------------------------------------------------------------------------------------
Version: 1.1.0
Date: 2022-12-5
Features:
- Added the ability to use the priority signal as input to optional station control so one can override priority on items with optional station control thresholds
- Added refueler stations
- Slightly more permissive allow-list logic
- Fixed a crash relating to wagon control combinators on request stations
- Made non-backwards compatible improvements and bugfixes to the modding interface
+2 -1
View File
@@ -3,8 +3,9 @@
require("scripts.constants")
require("scripts.global")
require("scripts.factorio-api")
require("scripts.central-planning")
require("scripts.layout")
require("scripts.central-planning")
require("scripts.train-events")
require("scripts.gui")
require("scripts.migrations")
require("scripts.main")
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 19 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cybersyn",
"version": "1.0.9",
"version": "1.1.0",
"title": "Project Cybersyn",
"author": "Mami",
"factorio_version": "1.1",
+11 -8
View File
@@ -3,16 +3,18 @@ cybersyn-ticks-per-second=Central planning updates per second
cybersyn-update-rate=Central planning update rate
cybersyn-request-threshold=Default requester threshold
cybersyn-network-flag=Default network mask
cybersyn-depot-bypass-threshold=Depot bypass fuel threshold
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)
[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.
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-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-depot-bypass-threshold=What percentage of the fuel inventory of the a train must be full to activate depot bypass. When a train qualifies for depot bypass, it may take a new order from the network before having to return to its depot, dramatically reducing travel time. If this is set to 1, depot bypass will be disabled.
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-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.
@@ -49,11 +51,12 @@ stuck-train=A train from depot __1__ is stuck
[cybersyn-gui]
combinator-title=Cybernetic combinator
operation=Mode
comb1=Primary station control
comb2=Optional station control
depot=Depot control
wagon-manifest=Wagon control
operation=Control Mode
comb1=Station
depot=Depot
refueler=Fuel loader
comb2=Station info
wagon-manifest=Wagon info
network=Network
network-tooltip=A signal is used to identify which network this combinator is a member of. Trains will only be dispatched from depots to provide and request stations if they are all identified with the same signal.
auto-tooltip=When checked trains in the network are automatically 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.
+1
View File
@@ -212,6 +212,7 @@ combinator_entity.divide_symbol_sprites = create_combinator_display(30, 0, { 0,
combinator_entity.modulo_symbol_sprites = create_combinator_display(45, 0, { 0, -0.140625, }, { 0, -0.328125, })
combinator_entity.power_symbol_sprites = create_combinator_display(0, 11, { 0, -0.140625, }, { 0, -0.328125, })
combinator_entity.left_shift_symbol_sprites = create_combinator_display(15, 11, { 0, -0.140625, }, { 0, -0.328125, })
combinator_entity.right_shift_symbol_sprites = create_combinator_display(30, 11, { 0, -0.140625, }, { 0, -0.328125, })
combinator_entity.multiply_symbol_sprites = combinator_entity.divide_symbol_sprites
+120 -72
View File
@@ -95,9 +95,8 @@ end
---@param r_station_id uint
---@param p_station_id uint
---@param train_id uint
---@param primary_item_name string?
function send_train_between(map_data, r_station_id, p_station_id, train_id, primary_item_name)
--trains and stations expected to be of the same network
---@param manifest Manifest
function create_delivery(map_data, r_station_id, p_station_id, train_id, manifest)
local economy = map_data.economy
local r_station = map_data.stations[r_station_id]
local p_station = map_data.stations[p_station_id]
@@ -105,6 +104,69 @@ function send_train_between(map_data, r_station_id, p_station_id, train_id, prim
---@type string
local network_name = r_station.network_name
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(train.entity, train.depot_name, train.se_depot_surface_i, p_station.entity_stop, r_station.entity_stop, manifest, depot_id ~= nil) then
local old_status = train.status
train.status = STATUS_TO_P
train.p_station_id = p_station_id
train.r_station_id = r_station_id
train.manifest = manifest
train.last_manifest_tick = map_data.total_ticks
r_station.last_delivery_tick = map_data.total_ticks
p_station.last_delivery_tick = map_data.total_ticks
r_station.deliveries_total = r_station.deliveries_total + 1
p_station.deliveries_total = p_station.deliveries_total + 1
for item_i, item in ipairs(manifest) do
assert(item.count > 0, "main.lua error, transfer amount was not positive")
r_station.deliveries[item.name] = (r_station.deliveries[item.name] or 0) + item.count
p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count
if item_i > 1 then
--prevent deliveries from being processed for these items until their stations are re-polled
local item_network_name = network_name..":"..item.name
economy.all_r_stations[item_network_name] = nil
economy.all_p_stations[item_network_name] = nil
end
end
set_comb2(map_data, p_station)
set_comb2(map_data, r_station)
if p_station.display_state < 2 then
p_station.display_state = 2
update_display(map_data, p_station)
end
if r_station.display_state < 2 then
r_station.display_state = 2
update_display(map_data, r_station)
end
interface_raise_train_status_changed(train_id, old_status, STATUS_TO_P)
else
interface_raise_train_dispatch_failed(train_id)
end
end
---@param map_data MapData
---@param r_station_id uint
---@param p_station_id uint
---@param train_id uint
---@param primary_item_name string?
function create_manifest(map_data, r_station_id, p_station_id, train_id, primary_item_name)
--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[train_id]
---@type Manifest
local manifest = {}
for k, v in pairs(r_station.tick_signals) do
@@ -114,8 +176,8 @@ function send_train_between(map_data, r_station_id, p_station_id, train_id, prim
local r_item_count = v.count
local r_effective_item_count = r_item_count + (r_station.deliveries[item_name] or 0)
if r_effective_item_count < 0 and r_item_count < 0 then
local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name]
local p_effective_item_count = p_station.p_count_or_r_threshold_per_item[item_name]
local r_threshold = r_station.item_thresholds and r_station.item_thresholds[item_name] or r_station.r_threshold
local p_effective_item_count = p_station.item_p_counts[item_name]
--could be an item that is not present at the station
if p_effective_item_count and p_effective_item_count >= r_threshold then
local item = {name = item_name, type = item_type, count = min(-r_effective_item_count, p_effective_item_count)}
@@ -167,55 +229,7 @@ function send_train_between(map_data, r_station_id, p_station_id, train_id, prim
end
end
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(train.entity, train.depot_name, train.se_depot_surface_i, p_station.entity_stop, r_station.entity_stop, manifest, depot_id ~= nil) then
train.status = STATUS_D_TO_P
train.p_station_id = p_station_id
train.r_station_id = r_station_id
train.manifest = manifest
train.last_manifest_tick = map_data.total_ticks
r_station.last_delivery_tick = map_data.total_ticks
p_station.last_delivery_tick = map_data.total_ticks
r_station.deliveries_total = r_station.deliveries_total + 1
p_station.deliveries_total = p_station.deliveries_total + 1
for item_i, item in ipairs(manifest) do
assert(item.count > 0, "main.lua error, transfer amount was not positive")
r_station.deliveries[item.name] = (r_station.deliveries[item.name] or 0) + item.count
p_station.deliveries[item.name] = (p_station.deliveries[item.name] or 0) - item.count
if item_i > 1 then
--prevent deliveries from being processed for these items until their stations are re-polled
local item_network_name = network_name..":"..item.name
economy.all_r_stations[item_network_name] = nil
economy.all_p_stations[item_network_name] = nil
end
end
set_comb2(map_data, p_station)
set_comb2(map_data, r_station)
if p_station.display_state < 2 then
p_station.display_state = 2
update_display(map_data, p_station)
end
if r_station.display_state < 2 then
r_station.display_state = 2
update_display(map_data, r_station)
end
interface_raise_train_dispatched(train_id)
else
interface_raise_train_dispatch_failed(train_id)
end
return manifest
end
---@param map_data MapData
@@ -282,11 +296,19 @@ local function tick_dispatch(map_data, mod_settings)
local station = stations[id]
--NOTE: the station at r_station_id could have been deleted and reregistered since last poll, this check here prevents it from being processed for a delivery in that case
if station and station.deliveries_total < station.entity_stop.trains_limit then
local threshold = station.p_count_or_r_threshold_per_item[item_name]
if threshold <= max_threshold and (station.priority > best_prior or (station.priority == best_prior and station.last_delivery_tick < best_lru)) then
local item_threshold = station.item_thresholds and station.item_thresholds[item_name] or nil
local threshold = station.r_threshold
local prior = station.priority
if item_threshold then
threshold = item_threshold
if station.item_priority then
prior = station.item_priority--[[@as int]]
end
end
if threshold <= max_threshold and (prior > best_prior or (prior == best_prior and station.last_delivery_tick < best_lru)) then
r_station_i = i
r_threshold = threshold
best_prior = station.priority
best_prior = prior
best_lru = station.last_delivery_tick
end
end
@@ -314,9 +336,13 @@ local function tick_dispatch(map_data, mod_settings)
for j, p_station_id in ipairs(p_stations) do
local p_station = stations[p_station_id]
if p_station and p_station.deliveries_total < p_station.entity_stop.trains_limit then
local effective_count = p_station.p_count_or_r_threshold_per_item[item_name]
local effective_count = p_station.item_p_counts[item_name]
if effective_count >= r_threshold then
local item_threshold = p_station.item_thresholds and p_station.item_thresholds[item_name] or nil
local prior = p_station.priority
if item_threshold then
prior = p_station.item_priority--[[@as int]]
end
local slot_threshold = item_type == "fluid" and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name))
local train, d = get_valid_train(map_data, r_station_id, p_station_id, item_type, slot_threshold)
if prior > best_prior or (prior == best_prior and d < best_dist) then
@@ -338,7 +364,9 @@ local function tick_dispatch(map_data, mod_settings)
end
end
if best_train then
send_train_between(map_data, r_station_id, table_remove(p_stations, best_i), best_train, item_name)
local p_station_id = table_remove(p_stations, best_i)
local manifest = create_manifest(map_data, r_station_id, p_station_id, best_train, item_name)
create_delivery(map_data, r_station_id, p_station_id, best_train, manifest)
return false
else
if can_be_serviced and mod_settings.missing_train_alert_enabled then
@@ -385,13 +413,34 @@ local function tick_poll_station(map_data, mod_settings)
end
station.r_threshold = mod_settings.r_threshold
station.priority = 0
station.item_priority = nil
station.locked_slots = 0
station.network_flag = mod_settings.network_flag
local signals = get_signals(station)
station.tick_signals = signals
station.p_count_or_r_threshold_per_item = {}
if signals then
for k, v in pairs(signals) do
local comb1_signals, comb2_signals = get_signals(station)
station.tick_signals = comb1_signals
station.item_p_counts = {}
if comb1_signals then
if comb2_signals then
station.item_thresholds = {}
for k, v in pairs(comb2_signals) do
local item_name = v.signal.name
local item_count = v.count
local item_type = v.signal.type
if item_name then
if item_type == "virtual" then
if item_name == SIGNAL_PRIORITY then
station.item_priority = item_count
end
else
station.item_thresholds[item_name] = abs(item_count)
end
end
end
else
station.item_thresholds = nil
end
for k, v in pairs(comb1_signals) do
local item_name = v.signal.name
local item_count = v.count
local item_type = v.signal.type
@@ -405,18 +454,18 @@ local function tick_poll_station(map_data, mod_settings)
elseif item_name == LOCKED_SLOTS then
station.locked_slots = max(item_count, 0)
end
signals[k] = nil
comb1_signals[k] = nil
end
if item_name == station.network_name then
station.network_flag = item_count
signals[k] = nil
comb1_signals[k] = nil
end
else
signals[k] = nil
comb1_signals[k] = nil
end
end
local is_requesting_nothing = true
for k, v in pairs(signals) do
for k, v in pairs(comb1_signals) do
---@type string
local item_name = v.signal.name
local item_count = v.count
@@ -424,7 +473,7 @@ local function tick_poll_station(map_data, mod_settings)
local is_not_requesting = true
if station.is_r then
local r_threshold = get_threshold(map_data, station, v.signal)
local r_threshold = station.item_thresholds and station.item_thresholds[item_name] or station.r_threshold
if -effective_item_count >= r_threshold and -item_count >= r_threshold then
is_not_requesting = false
is_requesting_nothing = false
@@ -437,7 +486,6 @@ local function tick_poll_station(map_data, mod_settings)
all_names[#all_names + 1] = v.signal
end
stations[#stations + 1] = station_id
station.p_count_or_r_threshold_per_item[item_name] = r_threshold
end
end
if is_not_requesting then
@@ -449,9 +497,9 @@ local function tick_poll_station(map_data, mod_settings)
all_p_stations[item_network_name] = stations
end
stations[#stations + 1] = station_id
station.p_count_or_r_threshold_per_item[item_name] = effective_item_count
station.item_p_counts[item_name] = effective_item_count
else
signals[k] = nil
comb1_signals[k] = nil
end
end
end
+14 -10
View File
@@ -13,13 +13,14 @@ COMBINATOR_OUT_NAME = "cybersyn-combinator-output"
COMBINATOR_CLOSE_SOUND = "entity-close/cybersyn-combinator"
ALERT_SOUND = "utility/console_message"
OPERATION_DEFAULT = "*"
OPERATION_PRIMARY_IO = "/"
OPERATION_PRIMARY_IO_FAILED_REQUEST = "^"
OPERATION_PRIMARY_IO_ACTIVE = "<<"
OPERATION_SECONDARY_IO = "%"
OPERATION_DEPOT = "+"
OPERATION_WAGON_MANIFEST = "-"
MODE_DEFAULT = "*"
MODE_PRIMARY_IO = "/"
MODE_PRIMARY_IO_FAILED_REQUEST = "^"
MODE_PRIMARY_IO_ACTIVE = "<<"
MODE_SECONDARY_IO = "%"
MODE_DEPOT = "+"
MODE_WAGON_MANIFEST = "-"
MODE_REFUELER = ">>"
NETWORK_SIGNAL_DEFAULT = {name="signal-A", type="virtual"}
INACTIVITY_TIME = 100
@@ -29,11 +30,14 @@ DELTA = 1/2048
DEPOT_PRIORITY_MULT = 2048
STATUS_D = 0
STATUS_D_TO_P = 1
STATUS_TO_P = 1
STATUS_P = 2
STATUS_P_TO_R = 3
STATUS_TO_R = 3
STATUS_R = 4
STATUS_R_TO_D = 5
STATUS_TO_D = 5
STATUS_TO_D_BYPASS = 6
STATUS_TO_F = 7
STATUS_F = 8
STATUS_CUSTOM = 256 --this status and any status greater than it can be used by other mods (I've reserved the lower integers for myself in case I want to add more statuses)
LONGEST_INSERTER_REACH = 2
+149 -85
View File
@@ -1,7 +1,10 @@
--By Mami
local get_distance = require("__flib__.misc").get_distance
local abs = math.abs
local floor = math.floor
local table_insert = table.insert
local DEFINES_WORKING = defines.entity_status.working
local DEFINES_LOW_POWER = defines.entity_status.low_power
local DEFINES_COMBINATOR_INPUT = defines.circuit_connector_id.combinator_input
---@param map_data MapData
@@ -130,7 +133,10 @@ function set_manifest_schedule(train, depot_name, d_surface_i, p_stop, r_stop, m
local t_surface_i = t_surface.index
local p_surface_i = p_surface.index
local r_surface_i = r_surface.index
if t_surface_i == p_surface_i and p_surface_i == r_surface_i then
local is_p_on_t = t_surface_i == p_surface_i
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
train.schedule = {current = start_at_depot and 1 or 2, records = {
create_inactivity_order(depot_name),
create_direct_to_station_order(p_stop),
@@ -144,37 +150,43 @@ function set_manifest_schedule(train, depot_name, d_surface_i, p_stop, r_stop, m
else
return true
end
elseif IS_SE_PRESENT and (t_surface_i == p_surface_i or p_surface_i == r_surface_i or r_surface_i == t_surface_i) then
local t_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = t_surface_i})
local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = (t_surface_i == p_surface_i) and r_surface_i or p_surface_i})
local is_train_in_orbit = other_zone.orbit_index == t_zone.index
if is_train_in_orbit or t_zone.orbit_index == other_zone.index then
local elevator_name = se_get_space_elevator_name(t_surface)
if elevator_name then
local records = {create_inactivity_order(depot_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)
if p_surface_i ~= r_surface_i then
records[#records + 1] = se_create_elevator_order(elevator_name, is_train_in_orbit)
is_train_in_orbit = not is_train_in_orbit
end
records[#records + 1] = create_unloading_order(r_stop)
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
end
elseif IS_SE_PRESENT then
local other_surface_i = (not is_p_on_t and p_surface_i) or (not is_r_on_t and r_surface_i) or d_surface_i
if (is_p_on_t or p_surface_i == other_surface_i) and (is_r_on_t or r_surface_i == other_surface_i) and (is_d_on_t or d_surface_i == other_surface_i) then
local t_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = t_surface_i})
local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = other_surface_i})
local is_train_in_orbit = other_zone.orbit_index == t_zone.index
if is_train_in_orbit or t_zone.orbit_index == other_zone.index then
local elevator_name = se_get_space_elevator_name(t_surface)
if elevator_name then
local records = {create_inactivity_order(depot_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)
train.schedule = {current = start_at_depot and 1 or 2, records = records}
if old_schedule and not train.has_path then
train.schedule = old_schedule
return false
else
return true
if p_surface_i ~= r_surface_i then
records[#records + 1] = se_create_elevator_order(elevator_name, is_train_in_orbit)
is_train_in_orbit = not is_train_in_orbit
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)
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
end
train.schedule = {current = start_at_depot and 1 or 2, records = records}
if old_schedule and not train.has_path then
train.schedule = old_schedule
return false
else
return true
end
end
end
end
@@ -190,6 +202,60 @@ function set_manifest_schedule(train, depot_name, d_surface_i, p_stop, r_stop, m
return true
end
---@param train LuaTrain
---@param stop LuaEntity
---@param depot_name string
function add_refueler_schedule(train, stop, depot_name)
local schedule = train.schedule or {current = 1, records = {}}
local i = schedule.current
if i == 1 then
i = #schedule.records + 1--[[@as uint]]
schedule.current = i
end
local t_surface = train.front_stock.surface
local f_surface = stop.surface
local t_surface_i = t_surface.index
local f_surface_i = f_surface.index
if t_surface_i == f_surface_i then
table_insert(schedule.records, i, create_direct_to_station_order(stop))
i = i + 1
table_insert(schedule.records, i, create_inactivity_order(stop.backer_name))
train.schedule = schedule
return
elseif IS_SE_PRESENT then
local t_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = t_surface_i})
local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = f_surface_i})
local is_train_in_orbit = other_zone.orbit_index == t_zone.index
if is_train_in_orbit or t_zone.orbit_index == other_zone.index then
local elevator_name = se_get_space_elevator_name(t_surface)
local cur_order = schedule.records[i]
local is_elevator_in_orders_already = cur_order and cur_order.station == elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)
if not is_elevator_in_orders_already then
table_insert(schedule.records, i, se_create_elevator_order(elevator_name, is_train_in_orbit))
end
i = i + 1
is_train_in_orbit = not is_train_in_orbit
table_insert(schedule.records, i, create_inactivity_order(stop.backer_name))
i = i + 1
if not is_elevator_in_orders_already then
table_insert(schedule.records, i, se_create_elevator_order(elevator_name, is_train_in_orbit))
i = i + 1
is_train_in_orbit = not is_train_in_orbit
end
train.schedule = schedule
return
end
end
--create an order that probably cannot be fulfilled and alert the player
table_insert(schedule.records, i, create_inactivity_order(stop.backer_name))
lock_train(train)
send_lost_train_alert(train, depot_name)
train.schedule = schedule
end
------------------------------------------------------------------------------
--[[combinators]]--
@@ -223,14 +289,16 @@ function get_comb_gui_settings(comb)
switch_state = "right"
end
if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then
if op == MODE_PRIMARY_IO or op == MODE_PRIMARY_IO_ACTIVE or op == MODE_PRIMARY_IO_FAILED_REQUEST then
selected_index = 1
elseif op == OPERATION_SECONDARY_IO then
elseif op == MODE_DEPOT then
selected_index = 2
elseif op == OPERATION_DEPOT then
elseif op == MODE_REFUELER then
selected_index = 3
elseif op == OPERATION_WAGON_MANIFEST then
elseif op == MODE_SECONDARY_IO then
selected_index = 4
elseif op == MODE_WAGON_MANIFEST then
selected_index = 5
end
return selected_index, params.first_signal, not allows_all_trains, switch_state
end
@@ -253,45 +321,49 @@ function set_station_from_comb_state(station)
station.is_p = is_pr_state == 0 or is_pr_state == 1
station.is_r = is_pr_state == 0 or is_pr_state == 2
end
---@param map_data MapData
---@param unit_number uint
---@param params ArithmeticCombinatorParameters
local function has_comb_params_changed(map_data, unit_number, params)
local old_params = map_data.to_comb_params[unit_number]
---@param mod_settings CybersynModSettings
---@param refueler Refueler
function set_refueler_from_comb(mod_settings, refueler)
--NOTE: this does nothing to update currently active deliveries
local params = get_comb_params(refueler.entity_comb)
local bits = params.second_constant or 0
local signal = params.first_signal
refueler.network_name = signal and signal.name or nil
refueler.allows_all_trains = bits%2 == 1
if params.operation ~= old_params.operation then
if (old_params.operation == OPERATION_PRIMARY_IO) and (params.operation == OPERATION_PRIMARY_IO_ACTIVE or params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST) then
else
return true
local signals = refueler.entity_comb.get_merged_signals(DEFINES_COMBINATOR_INPUT)
refueler.priority = 0
refueler.network_flag = mod_settings.network_flag
if not signals then return end
for k, v in pairs(signals) do
local item_name = v.signal.name
local item_count = v.count
if item_name then
if item_name == SIGNAL_PRIORITY then
refueler.priority = item_count
end
if item_name == refueler.network_name then
refueler.network_flag = item_count
end
end
end
local new_signal = params.first_signal
local old_signal = old_params.first_signal
local new_network = new_signal and new_signal.name or nil
local old_network = old_signal and old_signal.name or nil
if new_network ~= old_network then
return true
end
if params.second_constant ~= old_params.second_constant then
return true
end
return false
end
---@param map_data MapData
---@param station Station
function update_display(map_data, station)
local comb = station.entity_comb1
if comb.valid then
local unit_number = comb.unit_number--[[@as uint]]
local control = get_comb_control(comb)
local params = control.parameters
if not has_comb_params_changed(map_data, unit_number, params) then
--NOTE: the following check can cause a bug where the display desyncs if the player changes the operation of the combinator and then changes it back before the mod can notice, however removing it causes a bug where the user's change is overwritten and ignored. Everything's bad we need an event to catch copy-paste by blueprint.
if params.operation == MODE_PRIMARY_IO or params.operation == MODE_PRIMARY_IO_ACTIVE or params.operation == MODE_PRIMARY_IO_FAILED_REQUEST then
if station.display_state >= 2 then
params.operation = OPERATION_PRIMARY_IO_ACTIVE
params.operation = MODE_PRIMARY_IO_ACTIVE
elseif station.display_state == 1 then
params.operation = OPERATION_PRIMARY_IO_FAILED_REQUEST
params.operation = MODE_PRIMARY_IO_FAILED_REQUEST
else
params.operation = OPERATION_PRIMARY_IO
params.operation = MODE_PRIMARY_IO
end
control.parameters = params
end
@@ -344,18 +416,26 @@ function set_combinator_output(map_data, comb, signals)
end
end
local DEFINES_WORKING = defines.entity_status.working
local DEFINES_LOW_POWER = defines.entity_status.low_power
local DEFINES_COMBINATOR_INPUT = defines.circuit_connector_id.combinator_input
---@param station Station
function get_signals(station)
--NOTE: the combinator must be valid, but checking for valid every time is too slow
local comb = station.entity_comb1
local status = comb.status
if status == DEFINES_WORKING or status == DEFINES_LOW_POWER then
return comb.get_merged_signals(DEFINES_COMBINATOR_INPUT)
local comb1 = station.entity_comb1
local status1 = comb1.status
---@type Signal[]?
local comb1_signals = nil
---@type Signal[]?
local comb2_signals = nil
if status1 == DEFINES_WORKING or status1 == DEFINES_LOW_POWER then
comb1_signals = comb1.get_merged_signals(DEFINES_COMBINATOR_INPUT)
end
return nil
local comb2 = station.entity_comb2
if comb2 then
local status2 = comb2.status
if status2 == DEFINES_WORKING or status2 == DEFINES_LOW_POWER then
comb2_signals = comb2.get_merged_signals(DEFINES_COMBINATOR_INPUT)
end
end
return comb1_signals, comb2_signals
end
---@param map_data MapData
@@ -373,22 +453,6 @@ function set_comb2(map_data, station)
end
end
---@param map_data MapData
---@param station Station
---@param signal SignalID
function get_threshold(map_data, station, signal)
local comb2 = station.entity_comb2
if comb2 then
local count = comb2.get_merged_signal(signal, defines.circuit_connector_id.combinator_input)
if count ~= 0 then
return abs(count)
end
end
return station.r_threshold
end
------------------------------------------------------------------------------
--[[alerts]]--
------------------------------------------------------------------------------
+29 -10
View File
@@ -10,14 +10,16 @@
---@field public active_station_ids uint[]
---@field public warmup_station_ids uint[]
---@field public depots {[uint]: Depot}
---@field public refuelers {[uint]: Refueler}
---@field public trains {[uint]: Train}
---@field public available_trains {[string]: {[uint]: true?}} --{[network_name]: {[train_id]: depot_id}}
---@field public available_trains {[string]: {[uint]: true?}} --{[network_name]: {[train_id]: true}}
---@field public to_refuelers {[string]: {[uint]: true?}} --{[network_name]: {[refeuler_id]: true}}
---@field public layouts {[uint]: (0|1|2)[]}
---@field public layout_train_count {[uint]: int}
---@field public tick_state uint
---@field public tick_data {}
---@field public economy Economy
---@field public se_tele_old_id {[any]: uint}
---@field public se_tele_old_id {[string]: uint}
---@class Station
---@field public entity_stop LuaEntity
@@ -29,6 +31,7 @@
---@field public deliveries_total int
---@field public last_delivery_tick int
---@field public priority int --transient
---@field public item_priority int? --transient
---@field public r_threshold int >= 0 --transient
---@field public locked_slots int >= 0 --transient
---@field public network_name string?
@@ -38,7 +41,8 @@
---@field public accepted_layouts {[uint]: true?}
---@field public layout_pattern (0|1|2|3)[]?
---@field public tick_signals {[uint]: Signal}? --transient
---@field public p_count_or_r_threshold_per_item {[string]: int} --transient
---@field public item_p_counts {[string]: int} --transient
---@field public item_thresholds {[string]: int}? --transient
---@field public display_state 0|1|2|3 --low bit is if this station's request has failed, high bit is if a train is heading to this station
---@class Depot
@@ -46,15 +50,27 @@
---@field public entity_comb LuaEntity
---@field public available_train_id uint?--train_id
---@class Refueler
---@field public entity_stop LuaEntity
---@field public entity_comb LuaEntity
---@field public trains_total int
---@field public accepted_layouts {[uint]: true?}
---@field public layout_pattern (0|1|2|3)[]?
---@field public wagon_combs {[int]: LuaEntity}?--NOTE: allowed to be invalid entities or combinators with the wrong operation, these must be checked and lazy deleted when found
---@field public allows_all_trains boolean
---@field public priority int
---@field public network_name string?
---@field public network_flag int
---@class Train
---@field public entity LuaTrain --should only be invalid if se_is_being_teleported is true
---@field public layout_id uint
---@field public item_slot_capacity int
---@field public fluid_capacity int
---@field public status int
---@field public p_station_id uint
---@field public r_station_id uint
---@field public manifest Manifest
---@field public status uint
---@field public p_station_id uint?
---@field public r_station_id uint?
---@field public manifest Manifest?
---@field public last_manifest_tick int
---@field public has_filtered_wagon true?
---@field public is_available true?
@@ -63,6 +79,7 @@
---@field public network_name string? --can only be nil when the train is parked at a depot
---@field public network_flag int
---@field public priority int
---@field public refueler_id uint?
---@field public se_depot_surface_i uint --se only
---@field public se_is_being_teleported true? --se only
---@field public se_awaiting_removal any? --se only
@@ -72,7 +89,7 @@
---@class ManifestEntry
---@field public type string
---@field public name string
---@field public count uint
---@field public count int
---@class Economy
---could contain invalid stations or stations with modified settings from when they were first appended
@@ -80,7 +97,8 @@
---@field public all_p_stations {[string]: uint[]} --{["network_name:item_name"]: station_id}
---@field public all_names (string|SignalID)[]
--NOTE: any setting labeled as an interface setting can only be changed through the remote-interface, these settings are not save and have to be set at initialization
--NOTE: any setting labeled as an "interface setting" can only be changed through the remote-interface, these settings are not save and have to be set at initialization
--As a modder using the remote-interface, you may override any of these settings, including user settings. They will have to be overriden at initialization and whenever a user tries to change one.
---@class CybersynModSettings
---@field public tps double
---@field public update_rate int
@@ -88,7 +106,8 @@
---@field public network_flag int
---@field public warmup_time double
---@field public stuck_train_time double
---@field public depot_bypass_threshold double
---@field public fuel_threshold double
---@field public depot_bypass_enabled 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
+25 -15
View File
@@ -60,8 +60,9 @@ function gui_opened(comb, player)
on_selection_state_changed={"drop-down", comb.unit_number}
}, selected_index=selected_index, items={
{"cybersyn-gui.comb1"},
{"cybersyn-gui.comb2"},
{"cybersyn-gui.depot"},
{"cybersyn-gui.refueler"},
{"cybersyn-gui.comb2"},
{"cybersyn-gui.wagon-manifest"},
}},
{type="switch", name="switch", ref={"switch"}, allow_none_state=true, switch_state=switch_state, left_label_caption={"cybersyn-gui.switch-provide"}, right_label_caption={"cybersyn-gui.switch-request"}, left_label_tooltip={"cybersyn-gui.switch-provide-tooltip"}, right_label_tooltip={"cybersyn-gui.switch-request-tooltip"}, actions={
@@ -88,10 +89,12 @@ function gui_opened(comb, player)
window.preview.entity = comb
window.titlebar.drag_target = window.main_window
window.main_window.force_auto_center()
window.network.visible = selected_index == 1 or selected_index == 3
window.network_label.visible = selected_index == 1 or selected_index == 3
window.radio_button.visible = selected_index == 1
window.radio_label.visible = selected_index == 1
local uses_network = selected_index == 1 or selected_index == 3 or selected_index == 4
local uses_allow_list = selected_index == 1 or selected_index == 4
window.network.visible = uses_network
window.network_label.visible = uses_network
window.radio_button.visible = uses_allow_list
window.radio_label.visible = uses_allow_list
window.switch.visible = selected_index == 1
player.opened = window.main_window
@@ -142,28 +145,35 @@ function register_gui_actions()
local bottom_flow = all_flow.bottom
local param
if element.selected_index == 1 then
set_comb_operation(comb, OPERATION_PRIMARY_IO)
set_comb_operation(comb, MODE_PRIMARY_IO)
top_flow["switch"].visible = true
all_flow["network_label"].visible = true
bottom_flow["network"].visible = true
bottom_flow["radio_button"].visible = true
bottom_flow["radio_label"].visible = true
elseif element.selected_index == 2 then
set_comb_operation(comb, OPERATION_SECONDARY_IO)
top_flow["switch"].visible = false
all_flow["network_label"].visible = false
bottom_flow["network"].visible = false
bottom_flow["radio_button"].visible = false
bottom_flow["radio_label"].visible = false
elseif element.selected_index == 3 then
set_comb_operation(comb, OPERATION_DEPOT)
set_comb_operation(comb, MODE_DEPOT)
top_flow["switch"].visible = false
all_flow["network_label"].visible = true
bottom_flow["network"].visible = true
bottom_flow["radio_button"].visible = false
bottom_flow["radio_label"].visible = false
elseif element.selected_index == 3 then
set_comb_operation(comb, MODE_REFUELER)
top_flow["switch"].visible = false
all_flow["network_label"].visible = true
bottom_flow["network"].visible = true
bottom_flow["radio_button"].visible = true
bottom_flow["radio_label"].visible = true
elseif element.selected_index == 4 then
set_comb_operation(comb, OPERATION_WAGON_MANIFEST)
set_comb_operation(comb, MODE_SECONDARY_IO)
top_flow["switch"].visible = false
all_flow["network_label"].visible = false
bottom_flow["network"].visible = false
bottom_flow["radio_button"].visible = false
bottom_flow["radio_label"].visible = false
elseif element.selected_index == 5 then
set_comb_operation(comb, MODE_WAGON_MANIFEST)
top_flow["switch"].visible = false
all_flow["network_label"].visible = false
bottom_flow["network"].visible = false
+161 -57
View File
@@ -17,24 +17,30 @@ local function table_compare(t0, t1)
return true
end
---@param a any[]
---@param i uint
local function iterr(a, i)
i = i + 1
if i <= #a then
return i, a[#a - i + 1]
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_layout_accepted(layout_pattern, layout)
function is_refuel_layout_accepted(layout_pattern, layout)
local valid = true
for i, v in ipairs(layout) do
local p = layout_pattern[i] or 0
if (v == 0 and p == 2) or (v == 1 and (p == 0 or p == 2)) or (v == 2 and (p == 0 or p == 1)) then
if (v == 1 and (p == 1 or p == 3)) or (v == 2 and (p == 2 or p == 3)) then
valid = false
break
end
@@ -42,7 +48,28 @@ function is_layout_accepted(layout_pattern, layout)
if valid or not layout[0] then return valid end
for i, v in irpairs(layout) do
local p = layout_pattern[i] or 0
if (v == 0 and p == 2) or (v == 1 and (p == 0 or p == 2)) or (v == 2 and (p == 0 or p == 1)) then
if (v == 1 and (p == 1 or p == 3)) or (v == 2 and (p == 2 or p == 3)) then
valid = false
break
end
end
return valid
end
---@param layout_pattern (0|1|2|3)[]
---@param layout (0|1|2)[]
function is_layout_accepted(layout_pattern, layout)
local valid = true
for i, v in ipairs(layout) do
local p = layout_pattern[i] or 0
if (v == 1 and not (p == 1 or p == 3)) or (v == 2 and not (p == 2 or p == 3)) then
valid = false
break
end
end
if valid or not layout[0] then return valid end
for i, v in irpairs(layout) do
local p = layout_pattern[i] or 0
if (v == 1 and not (p == 1 or p == 3)) or (v == 2 and not (p == 2 or p == 3)) then
valid = false
break
end
@@ -142,7 +169,7 @@ end
function set_p_wagon_combs(map_data, station, train)
if not station.wagon_combs or not next(station.wagon_combs) then return end
local carriages = train.entity.carriages
local manifest = train.manifest
local manifest = train.manifest--[[@as Manifest]]
local is_reversed = get_train_direction(station.entity_stop, train.entity)
@@ -288,7 +315,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 = stack.type, name = stack.name}, count = -stack.count}
signals[i] = {index = i, signal = {type = "item", name = stack.name}, count = -stack.count}
end
end
set_combinator_output(map_data, comb, signals)
@@ -306,76 +333,140 @@ function set_r_wagon_combs(map_data, station, train)
end
end
---@param map_data MapData
---@param station Station
function unset_wagon_combs(map_data, station)
if not station.wagon_combs then return end
for i, comb in pairs(station.wagon_combs) do
---@param map_data MapData
---@param refueler Refueler
---@param train Train
function set_refueler_combs(map_data, refueler, train)
if not refueler.wagon_combs then return end
local carriages = train.entity.carriages
local signals = {}
local is_reversed = get_train_direction(refueler.entity_stop, train.entity)
local ivpairs = is_reversed and irpairs or ipairs
for carriage_i, carriage in ivpairs(carriages) do
---@type LuaEntity?
local comb = refueler.wagon_combs[carriage_i]
if comb and not comb.valid then
comb = nil
refueler.wagon_combs[carriage_i] = nil
if next(refueler.wagon_combs) == nil then
refueler.wagon_combs = nil
break
end
end
local inv = carriage.get_fuel_inventory()
if inv then
local wagon_signals
if comb then
wagon_signals = {}
local array = carriage.prototype.items_to_place_this
if array then
local a = array[1]
local name
if type(a) == "string" then
name = a
else
name = a.name
end
if game.item_prototypes[name] then
wagon_signals[1] = {index = 1, signal = {type = "item", name = a.name}, count = 1}
end
end
end
for stack_i = 1, #inv do
local stack = inv[stack_i]
if stack.valid_for_read then
if comb then
local i = #wagon_signals + 1
wagon_signals[i] = {index = i, signal = {type = "item", name = stack.name}, count = stack.count}
end
local j = #signals + 1
signals[j] = {index = j, signal = {type = "item", name = stack.name}, count = stack.count}
end
end
if comb then
set_combinator_output(map_data, comb, wagon_signals)
end
end
end
set_combinator_output(map_data, refueler.entity_comb, signals)
end
---@param map_data MapData
---@param stop Station|Refueler
function unset_wagon_combs(map_data, stop)
if not stop.wagon_combs then return end
for i, comb in pairs(stop.wagon_combs) do
if comb.valid then
set_combinator_output(map_data, comb, nil)
else
station.wagon_combs[i] = nil
stop.wagon_combs[i] = nil
end
end
if next(station.wagon_combs) == nil then
station.wagon_combs = nil
if next(stop.wagon_combs) == nil then
stop.wagon_combs = nil
end
end
---@param map_data MapData
---@param station Station
---@param stop Station|Refueler
---@param is_station_or_refueler boolean
---@param forbidden_entity LuaEntity?
function reset_station_layout(map_data, station, forbidden_entity)
function reset_stop_layout(map_data, stop, is_station_or_refueler, forbidden_entity)
--NOTE: station must be in auto mode
local station_rail = station.entity_stop.connected_rail
if station_rail == nil then
local stop_rail = stop.entity_stop.connected_rail
if stop_rail == nil then
--cannot accept deliveries
station.layout_pattern = nil
station.accepted_layouts = {}
stop.layout_pattern = nil
stop.accepted_layouts = {}
return
end
local rail_direction_from_station
if station.entity_stop.connected_rail_direction == defines.rail_direction.front then
rail_direction_from_station = defines.rail_direction.back
local rail_direction_from_stop
if stop.entity_stop.connected_rail_direction == defines.rail_direction.front then
rail_direction_from_stop = defines.rail_direction.back
else
rail_direction_from_station = defines.rail_direction.front
rail_direction_from_stop = defines.rail_direction.front
end
local station_direction = station.entity_stop.direction
local surface = station.entity_stop.surface
local middle_x = station_rail.position.x
local middle_y = station_rail.position.y
local stop_direction = stop.entity_stop.direction
local surface = stop.entity_stop.surface
local middle_x = stop_rail.position.x
local middle_y = stop_rail.position.y
local reach = LONGEST_INSERTER_REACH + 1
local search_area
local area_delta
local is_ver
if station_direction == defines.direction.north then
if stop_direction == defines.direction.north then
search_area = {left_top = {x = middle_x - reach, y = middle_y}, right_bottom = {x = middle_x + reach, y = middle_y + 6}}
area_delta = {x = 0, y = 7}
is_ver = true
elseif station_direction == defines.direction.east then
elseif stop_direction == defines.direction.east then
search_area = {left_top = {y = middle_y - reach, x = middle_x - 6}, right_bottom = {y = middle_y + reach, x = middle_x}}
area_delta = {x = -7, y = 0}
is_ver = false
elseif station_direction == defines.direction.south then
elseif stop_direction == defines.direction.south then
search_area = {left_top = {x = middle_x - reach, y = middle_y - 6}, right_bottom = {x = middle_x + reach, y = middle_y}}
area_delta = {x = 0, y = -7}
is_ver = true
elseif station_direction == defines.direction.west then
elseif stop_direction == defines.direction.west then
search_area = {left_top = {y = middle_y - reach, x = middle_x}, right_bottom = {y = middle_y + reach, x = middle_x + 6}}
area_delta = {x = 7, y = 0}
is_ver = false
else
assert(false, "cybersyn: invalid station direction")
assert(false, "cybersyn: invalid stop direction")
end
local length = 2
local pre_rail = station_rail
local pre_rail = stop_rail
local layout_pattern = {0}
local type_filter = {"inserter", "pump", "arithmetic-combinator"}
local wagon_number = 0
for i = 1, 112 do
local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_station, rail_connection_direction = defines.rail_connection_direction.straight})
local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_stop, rail_connection_direction = defines.rail_connection_direction.straight})
if not rail or rail_connection_direction ~= defines.rail_connection_direction.straight or not rail.valid then
is_break = true
break
@@ -428,7 +519,7 @@ function reset_station_layout(map_data, station, forbidden_entity)
end
elseif entity.name == COMBINATOR_NAME then
local param = map_data.to_comb_params[entity.unit_number]
if param.operation == OPERATION_WAGON_MANIFEST then
if param.operation == MODE_WAGON_MANIFEST then
local pos = entity.position
local is_there
if is_ver then
@@ -437,10 +528,10 @@ function reset_station_layout(map_data, station, forbidden_entity)
is_there = middle_y - 2.1 <= pos.y and pos.y <= middle_y + 2.1
end
if is_there then
if not station.wagon_combs then
station.wagon_combs = {}
if not stop.wagon_combs then
stop.wagon_combs = {}
end
station.wagon_combs[wagon_number] = entity
stop.wagon_combs[wagon_number] = entity
end
end
end
@@ -461,18 +552,25 @@ function reset_station_layout(map_data, station, forbidden_entity)
search_area = area.move(search_area, area_delta)
end
end
station.layout_pattern = layout_pattern
for id, layout in pairs(map_data.layouts) do
station.accepted_layouts[id] = is_layout_accepted(layout_pattern, layout) or nil
stop.layout_pattern = layout_pattern
if is_station_or_refueler then
for id, layout in pairs(map_data.layouts) do
stop.accepted_layouts[id] = is_layout_accepted(layout_pattern, layout) or nil
end
else
for id, layout in pairs(map_data.layouts) do
stop.accepted_layouts[id] = is_refuel_layout_accepted(layout_pattern, layout) or nil
end
end
end
---@param map_data MapData
---@param station Station
---@param stop Station|Refueler
---@param is_station_or_refueler boolean
---@param forbidden_entity LuaEntity?
function update_station_if_auto(map_data, station, forbidden_entity)
if not station.allows_all_trains then
reset_station_layout(map_data, station, forbidden_entity)
function update_stop_if_auto(map_data, stop, is_station_or_refueler, forbidden_entity)
if not stop.allows_all_trains then
reset_stop_layout(map_data, stop, is_station_or_refueler, forbidden_entity)
end
end
@@ -480,7 +578,7 @@ end
---@param rail LuaEntity
---@param forbidden_entity LuaEntity?
---@param force boolean?
function update_station_from_rail(map_data, rail, forbidden_entity, force)
function update_stop_from_rail(map_data, rail, forbidden_entity, force)
--NOTE: is this a correct way to figure out the direction?
---@type defines.rail_direction
local rail_direction = defines.rail_direction.back
@@ -494,12 +592,18 @@ function update_station_from_rail(map_data, rail, forbidden_entity, force)
return
end
if entity.name == "train-stop" then
local station = map_data.stations[entity.unit_number]
if station then
local id = entity.unit_number
local is_station = true
local stop = map_data.stations[id]
if not stop then
stop = map_data.refuelers[id]
is_station = false
end
if stop then
if force then
reset_station_layout(map_data, station, forbidden_entity)
else
update_station_if_auto(map_data, station, forbidden_entity)
reset_stop_layout(map_data, stop, is_station, forbidden_entity)
elseif not stop.allows_all_trains then
reset_stop_layout(map_data, stop, is_station, forbidden_entity)
end
end
return
@@ -516,15 +620,15 @@ end
---@param map_data MapData
---@param pump LuaEntity
---@param forbidden_entity LuaEntity?
function update_station_from_pump(map_data, pump, forbidden_entity)
function update_stop_from_pump(map_data, pump, forbidden_entity)
if pump.pump_rail_target then
update_station_from_rail(map_data, pump.pump_rail_target, forbidden_entity)
update_stop_from_rail(map_data, pump.pump_rail_target, forbidden_entity)
end
end
---@param map_data MapData
---@param inserter LuaEntity
---@param forbidden_entity LuaEntity?
function update_station_from_inserter(map_data, inserter, forbidden_entity)
function update_stop_from_inserter(map_data, inserter, forbidden_entity)
local surface = inserter.surface
--NOTE: we don't use find_entity solely for miniloader compat
@@ -534,7 +638,7 @@ function update_station_from_inserter(map_data, inserter, forbidden_entity)
radius = 1,
})
if rails[1] then
update_station_from_rail(map_data, rails[1], forbidden_entity)
update_stop_from_rail(map_data, rails[1], forbidden_entity)
end
rails = surface.find_entities_filtered({
type = "straight-rail",
@@ -542,6 +646,6 @@ function update_station_from_inserter(map_data, inserter, forbidden_entity)
radius = 1,
})
if rails[1] then
update_station_from_rail(map_data, rails[1], forbidden_entity)
update_stop_from_rail(map_data, rails[1], forbidden_entity)
end
end
+243 -499
View File
@@ -4,138 +4,6 @@ local ceil = math.ceil
local table_insert = table.insert
---@param map_data MapData
---@param station Station
---@param manifest Manifest
---@param sign int?
local function set_comb1(map_data, station, manifest, sign)
local comb = station.entity_comb1
if comb.valid then
if manifest then
local signals = {}
for i, item in ipairs(manifest) do
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = sign*item.count}
end
set_combinator_output(map_data, comb, signals)
else
set_combinator_output(map_data, comb, nil)
end
end
end
---@param map_data MapData
---@param train_id uint
---@param train Train
function on_failed_delivery(map_data, train_id, train)
--NOTE: must either change this train's status or remove it after this call
local p_station_id = train.p_station_id
local r_station_id = train.r_station_id
local manifest = train.manifest
local is_p_in_progress = train.status == STATUS_D_TO_P or train.status == STATUS_P
local is_r_in_progress = is_p_in_progress or train.status == STATUS_P_TO_R or train.status == STATUS_R
if is_p_in_progress then
local station = map_data.stations[p_station_id]
remove_manifest(map_data, station, manifest, 1)
if train.status == STATUS_P then
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
end
end
if is_r_in_progress then
local station = map_data.stations[r_station_id]
remove_manifest(map_data, station, manifest, -1)
if train.status == STATUS_R then
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
end
end
train.r_station_id = 0
train.p_station_id = 0
train.manifest = nil
interface_raise_train_failed_delivery(train_id, is_p_in_progress, p_station_id, is_r_in_progress, r_station_id, manifest)
end
---@param map_data MapData
---@param train_id uint
---@param train Train
function add_available_train(map_data, train_id, train)
local network_name = train.network_name
if network_name then
local network = map_data.available_trains[network_name]
if not network then
network = {}
map_data.available_trains[network_name] = network
end
network[train_id] = true
train.is_available = true
interface_raise_train_available(train_id)
end
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
---@param depot_id uint
---@param depot Depot
---@param train_id uint
---@param train Train
function add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
local comb = depot.entity_comb
local network_name = get_comb_network_name(comb)
if network_name then
local network = map_data.available_trains[network_name]
if not network then
network = {}
map_data.available_trains[network_name] = network
end
network[train_id] = true
train.is_available = true
end
depot.available_train_id = train_id
train.status = STATUS_D
train.parked_at_depot_id = depot_id
train.depot_name = depot.entity_stop.backer_name
train.se_depot_surface_i = depot.entity_stop.surface.index
train.network_name = network_name
train.network_flag = mod_settings.network_flag
train.priority = 0
if network_name then
local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input)
if signals then
for k, v in pairs(signals) do
local item_name = v.signal.name
local item_count = v.count
if item_name then
if item_name == SIGNAL_PRIORITY then
train.priority = item_count
end
if item_name == network_name then
train.network_flag = item_count
end
end
end
end
interface_raise_train_available(train_id)
end
end
---@param map_data MapData
---@param train_id uint
---@param train Train
function remove_available_train(map_data, train_id, train)
---@type uint
if train.is_available and train.network_name then
local network = map_data.available_trains[train.network_name--[[@as string]]]
if network then
network[train_id] = nil
if next(network) == nil then
map_data.available_trains[train.network_name] = nil
end
end
train.is_available = nil
end
end
---@param map_data MapData
---@param stop LuaEntity
---@param comb LuaEntity
@@ -152,8 +20,9 @@ local function on_depot_built(map_data, stop, comb)
end
---@param map_data MapData
---@param depot_id uint
---@param depot Depot
local function on_depot_broken(map_data, depot)
local function on_depot_broken(map_data, depot_id, depot)
local train_id = depot.available_train_id
if train_id then
local train = map_data.trains[train_id]
@@ -161,11 +30,70 @@ local function on_depot_broken(map_data, depot)
send_lost_train_alert(train.entity, depot.entity_stop.backer_name)
remove_train(map_data, train_id, train)
end
local depot_id = depot.entity_stop.unit_number--[[@as uint]]
map_data.depots[depot_id] = nil
interface_raise_depot_removed(depot_id, depot)
end
---@param map_data MapData
---@param stop LuaEntity
---@param comb LuaEntity
local function on_refueler_built(map_data, stop, comb)
--NOTE: only place where new Depot
local refueler = {
entity_stop = stop,
entity_comb = comb,
trains_total = 0,
accepted_layouts = {},
layout_pattern = {},
--allows_all_trains = set_refueler_from_comb,
--priority = set_refueler_from_comb,
--network_name = set_refueler_from_comb,
--network_flag = set_refueler_from_comb,
}
set_refueler_from_comb(mod_settings, refueler)
local id = stop.unit_number--[[@as uint]]
map_data.refuelers[id] = refueler
update_stop_if_auto(map_data, refueler, false)
if refueler.network_name then
local network = map_data.to_refuelers[refueler.network_name]
if not network then
network = {}
map_data.to_refuelers[refueler.network_name] = network
end
network[id] = true
end
interface_raise_refueler_created(id)
end
---@param map_data MapData
---@param refueler_id uint
---@param refueler Refueler
local function on_refueler_broken(map_data, refueler_id, refueler)
if refueler.trains_total > 0 then
--search for trains coming to the destroyed refueler
for train_id, train in pairs(map_data.trains) do
local is_f = train.refueler_id == refueler_id
if is_f then
if not train.se_is_being_teleported then
remove_train(map_data, train_id, train)
lock_train(train.entity)
send_lost_train_alert(train.entity, train.depot_name)
else
train.se_awaiting_removal = train_id
end
end
end
end
if refueler.network_name then
local network = map_data.to_refuelers[refueler.network_name]
network[refueler_id] = nil
if next(network) == nil then
map_data.to_refuelers[refueler.network_name] = nil
end
end
map_data.stations[refueler_id] = nil
interface_raise_refueler_removed(refueler_id, refueler)
end
---@param map_data MapData
---@param stop LuaEntity
---@param comb1 LuaEntity
@@ -182,6 +110,7 @@ local function on_station_built(map_data, stop, comb1, comb2)
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,
@@ -191,7 +120,8 @@ local function on_station_built(map_data, stop, comb1, comb2)
accepted_layouts = {},
layout_pattern = nil,
tick_signals = nil,
p_count_or_r_threshold_per_item = {},
item_p_counts = {},
item_thresholds = nil,
display_state = 0,
}
set_station_from_comb_state(station)
@@ -199,7 +129,7 @@ local function on_station_built(map_data, stop, comb1, comb2)
map_data.stations[id] = station
map_data.warmup_station_ids[#map_data.warmup_station_ids + 1] = id
update_station_if_auto(map_data, station, nil)
update_stop_if_auto(map_data, station, true)
interface_raise_station_created(id)
end
---@param map_data MapData
@@ -213,8 +143,8 @@ local function on_station_broken(map_data, station_id, station)
local is_p = train.p_station_id == station_id
if is_p or is_r then
local is_p_in_progress = train.status == STATUS_D_TO_P or train.status == STATUS_P
local is_r_in_progress = is_p_in_progress or train.status == STATUS_P_TO_R or train.status == STATUS_R
local is_p_in_progress = train.status == STATUS_TO_P or train.status == STATUS_P
local is_r_in_progress = is_p_in_progress or train.status == STATUS_TO_R or train.status == STATUS_R
if (is_p and is_p_in_progress) or (is_r and is_r_in_progress) then
--train is attempting delivery to a stop that was destroyed, stop it
on_failed_delivery(map_data, train_id, train)
@@ -311,13 +241,13 @@ local function on_combinator_built(map_data, comb)
local params = control.parameters
local op = params.operation
if op == OPERATION_DEFAULT then
op = OPERATION_PRIMARY_IO
if op == MODE_DEFAULT then
op = MODE_PRIMARY_IO
params.operation = op
params.first_signal = NETWORK_SIGNAL_DEFAULT
control.parameters = params
elseif op ~= OPERATION_PRIMARY_IO and op ~= OPERATION_SECONDARY_IO and op ~= OPERATION_DEPOT and op ~= OPERATION_WAGON_MANIFEST then
op = OPERATION_PRIMARY_IO
elseif op ~= MODE_PRIMARY_IO and op ~= MODE_SECONDARY_IO and op ~= MODE_DEPOT and op ~= MODE_REFUELER and op ~= MODE_WAGON_MANIFEST then
op = MODE_PRIMARY_IO
params.operation = op
control.parameters = params
end
@@ -327,43 +257,39 @@ local function on_combinator_built(map_data, comb)
map_data.to_output[comb.unit_number] = out
map_data.to_stop[comb.unit_number] = stop
if op == OPERATION_WAGON_MANIFEST then
if op == MODE_WAGON_MANIFEST then
if rail then
update_station_from_rail(map_data, rail, nil, true)
update_stop_from_rail(map_data, rail, nil, true)
end
elseif op == OPERATION_DEPOT then
if stop then
local station = map_data.stations[stop.unit_number]
---@type Depot
local depot = map_data.depots[stop.unit_number]
if depot or station then
--NOTE: repeated combinators are ignored
else
elseif stop then
local id = stop.unit_number--[[@as uint]]
local station = map_data.stations[id]
local depot = map_data.depots[id]
local refueler = map_data.refuelers[id]
if op == MODE_DEPOT then
if refueler then
on_refueler_broken(map_data, id, refueler)
end
if not station and not depot then
on_depot_built(map_data, stop, comb)
end
end
elseif op == OPERATION_SECONDARY_IO then
if stop then
local station = map_data.stations[stop.unit_number]
elseif op == MODE_REFUELER then
if not station and not depot and not refueler then
on_refueler_built(map_data, stop, comb)
end
elseif op == MODE_SECONDARY_IO then
if station and not station.entity_comb2 then
station.entity_comb2 = comb
end
end
elseif op == OPERATION_PRIMARY_IO then
if stop then
local station = map_data.stations[stop.unit_number]
if station then
--NOTE: repeated combinators are ignored
else
local depot = map_data.depots[stop.unit_number]
if depot then
on_depot_broken(map_data, depot)
end
--no station or depot
--add station
local comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb)
elseif op == MODE_PRIMARY_IO then
if refueler then
on_refueler_broken(map_data, id, refueler)
end
if depot then
on_depot_broken(map_data, id, depot)
end
if not station then
local comb2 = search_for_station_combinator(map_data, stop, MODE_SECONDARY_IO, comb)
on_station_built(map_data, stop, comb, comb2)
end
end
@@ -376,20 +302,43 @@ function on_combinator_network_updated(map_data, comb, network_name)
local stop = map_data.to_stop[comb.unit_number]
if stop and stop.valid then
local station = map_data.stations[stop.unit_number]
local id = stop.unit_number
local station = map_data.stations[id]
if station then
if station.entity_comb1 == comb then
station.network_name = network_name
end
else
local depot_id = stop.unit_number
local depot = map_data.depots[depot_id]
if depot and depot.entity_comb == comb then
local train_id = depot.available_train_id
if train_id then
local train = map_data.trains[train_id]
remove_available_train(map_data, train_id, train)
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
local depot = map_data.depots[id]
if depot then
if depot.entity_comb == comb then
local train_id = depot.available_train_id
if train_id then
local train = map_data.trains[train_id]
remove_available_train(map_data, train_id, train)
add_available_train_to_depot(map_data, mod_settings, train_id, train, id, depot)
interface_raise_train_status_changed(train_id, STATUS_D, STATUS_D)
end
end
else
local refueler = map_data.refuelers[id]
if refueler and refueler.entity_comb == comb then
if refueler.network_name then
local network = map_data.to_refuelers[refueler.network_name]
network[id] = nil
if next(network) == nil then
map_data.to_refuelers[refueler.network_name] = nil
end
end
refueler.network_name = network_name
if network_name then
local network = map_data.to_refuelers[network_name]
if network == nil then
network = {}
map_data.to_refuelers[network_name] = network
end
network[id] = true
end
end
end
end
@@ -405,33 +354,27 @@ function on_combinator_broken(map_data, comb)
local stop = map_data.to_stop[comb_id]
if stop and stop.valid then
local station = map_data.stations[stop.unit_number]
local id = stop.unit_number--[[@as uint]]
local station = map_data.stations[id]
if station then
if station.entity_comb1 == comb then
local comb1 = search_for_station_combinator(map_data, stop, OPERATION_PRIMARY_IO, comb)
if comb1 then
station.entity_comb1 = comb1
set_station_from_comb_state(station)
update_station_if_auto(map_data, station)
else
on_station_broken(map_data, stop.unit_number, station)
local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb)
if depot_comb then
on_depot_built(map_data, stop, depot_comb)
end
end
on_station_broken(map_data, id, station)
on_stop_built(map_data, stop, comb)
elseif station.entity_comb2 == comb then
station.entity_comb2 = search_for_station_combinator(map_data, stop, OPERATION_SECONDARY_IO, comb)
station.entity_comb2 = search_for_station_combinator(map_data, stop, MODE_SECONDARY_IO, comb)
end
else
local depot = map_data.depots[stop.unit_number]
if depot and depot.entity_comb == comb then
--NOTE: this will disrupt deliveries in progress that where dispatched from this station in a minor way
local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb)
if depot_comb then
depot.entity_comb = depot_comb
else
on_depot_broken(map_data, depot)
local depot = map_data.depots[id]
if depot then
if depot.entity_comb == comb then
on_depot_broken(map_data, id, depot)
on_stop_built(map_data, stop, comb)
end
else
local refueler = map_data.refuelers[id]
if refueler and refueler.entity_comb == comb then
on_refueler_broken(map_data, id, depot)
on_stop_built(map_data, stop, comb)
end
end
end
@@ -449,17 +392,16 @@ end
---@param map_data MapData
---@param comb LuaEntity
function combinator_update(map_data, comb)
---@type uint
local unit_number = comb.unit_number
local unit_number = comb.unit_number--[[@as uint]]
local control = get_comb_control(comb)
local params = control.parameters
local old_params = map_data.to_comb_params[unit_number]
local has_changed = false
if params.operation ~= old_params.operation then
if (old_params.operation == OPERATION_PRIMARY_IO) and (params.operation == OPERATION_PRIMARY_IO_ACTIVE or params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST) then
--make sure only OPERATION_PRIMARY_IO gets stored on map_data.to_comb_params
params.operation = OPERATION_PRIMARY_IO
if (old_params.operation == MODE_PRIMARY_IO) and (params.operation == MODE_PRIMARY_IO_ACTIVE or params.operation == MODE_PRIMARY_IO_FAILED_REQUEST) then
--make sure only MODE_PRIMARY_IO gets stored on map_data.to_comb_params
params.operation = MODE_PRIMARY_IO
else
--NOTE: This is rather dangerous, we may need to actually implement operation changing
on_combinator_broken(map_data, comb)
@@ -473,21 +415,29 @@ function combinator_update(map_data, comb)
local new_network = new_signal and new_signal.name or nil
local old_network = old_signal and old_signal.name or nil
if new_network ~= old_network then
has_changed = true
on_combinator_network_updated(map_data, comb, new_network)
end
if params.second_constant ~= old_params.second_constant then
local stop = global.to_stop[comb.unit_number]
has_changed = true
local stop = map_data.to_stop[comb.unit_number]
if stop then
local station = global.stations[stop.unit_number]
local id = stop.unit_number
local station = map_data.stations[id]
if station then
local bits = params.second_constant
local is_pr_state = floor(bits/2)%3
station.is_p = is_pr_state == 0 or is_pr_state == 1
station.is_r = is_pr_state == 0 or is_pr_state == 2
local allows_all_trains = bits%2 == 1
if station.allows_all_trains ~= allows_all_trains then
station.allows_all_trains = allows_all_trains
update_station_if_auto(map_data, station)
local pre = station.allows_all_trains
set_station_from_comb_state(station)
if station.allows_all_trains ~= pre then
update_stop_if_auto(map_data, station, true)
end
else
local refueler = map_data.refuelers[id]
if refueler then
local pre = refueler.allows_all_trains
set_refueler_from_comb(mod_settings, refueler)
if refueler.allows_all_trains ~= pre then
update_stop_if_auto(map_data, refueler, false)
end
end
end
end
@@ -500,7 +450,8 @@ end
---@param map_data MapData
---@param stop LuaEntity
local function on_stop_built(map_data, stop)
---@param comb_forbidden LuaEntity?
function on_stop_built(map_data, stop, comb_forbidden)
local pos_x = stop.position.x
local pos_y = stop.position.y
@@ -511,18 +462,21 @@ local function on_stop_built(map_data, stop)
local comb2 = nil
local comb1 = nil
local depot_comb = nil
local refueler_comb = nil
local entities = stop.surface.find_entities(search_area)
for _, entity in pairs(entities) do
if entity.valid and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then
if entity.valid and entity ~= comb_forbidden and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then
map_data.to_stop[entity.unit_number] = stop
local param = get_comb_params(entity)
local op = param.operation
if op == OPERATION_PRIMARY_IO then
if op == MODE_PRIMARY_IO then
comb1 = entity
elseif op == OPERATION_SECONDARY_IO then
elseif op == MODE_SECONDARY_IO then
comb2 = entity
elseif op == OPERATION_DEPOT then
elseif op == MODE_DEPOT then
depot_comb = entity
elseif op == MODE_REFUELER then
refueler_comb = entity
end
end
end
@@ -530,6 +484,8 @@ local function on_stop_built(map_data, stop)
on_station_built(map_data, stop, comb1, comb2)
elseif depot_comb then
on_depot_built(map_data, stop, depot_comb)
elseif refueler_comb then
on_refueler_built(map_data, stop, refueler_comb)
end
end
---@param map_data MapData
@@ -549,20 +505,26 @@ local function on_stop_broken(map_data, stop)
end
end
local station = map_data.stations[stop.unit_number]
local id = stop.unit_number--[[@as uint]]
local station = map_data.stations[id]
if station then
on_station_broken(map_data, stop.unit_number, station)
on_station_broken(map_data, id, station)
else
local depot = map_data.depots[stop.unit_number]
local depot = map_data.depots[id]
if depot then
on_depot_broken(map_data, depot)
on_depot_broken(map_data, id, depot)
else
local refueler = map_data.refuelers[id]
if refueler then
on_refueler_broken(map_data, id, refueler)
end
end
end
end
---@param map_data MapData
---@param stop LuaEntity
---@param old_name string
local function on_station_rename(map_data, stop, old_name)
local function on_stop_rename(map_data, stop, old_name)
--search for trains coming to the renamed station
local station_id = stop.unit_number--[[@as uint]]
local station = map_data.stations[station_id]
@@ -571,8 +533,8 @@ local function on_station_rename(map_data, stop, old_name)
local is_p = train.p_station_id == station_id
local is_r = train.r_station_id == station_id
if is_p or is_r then
local is_p_in_progress = train.status == STATUS_D_TO_P or train.status == STATUS_P
local is_r_in_progress = is_p_in_progress or train.status == STATUS_P_TO_R or train.status == STATUS_R
local is_p_in_progress = train.status == STATUS_TO_P or train.status == STATUS_P
local is_r_in_progress = is_p_in_progress or train.status == STATUS_TO_R or train.status == STATUS_R
if is_r and is_r_in_progress then
local r_station = map_data.stations[train.r_station_id]
if not train.se_is_being_teleported then
@@ -614,209 +576,6 @@ local function find_and_add_all_stations_from_nothing(map_data)
end
end
---@param map_data MapData
---@param depot_id uint
---@param train_entity LuaTrain
local function on_train_arrives_depot(map_data, depot_id, train_entity)
local contents = train_entity.get_contents()
local fluid_contents = train_entity.get_fluid_contents()
local is_train_empty = next(contents) == nil and next(fluid_contents) == nil
local train_id = train_entity.id
local train = map_data.trains[train_id]
if train then
if train.manifest then
if train.status == STATUS_R_TO_D then
--succeeded delivery
train.p_station_id = 0
train.r_station_id = 0
train.manifest = nil
elseif mod_settings.react_to_train_early_to_depot then
on_failed_delivery(map_data, train_id, train)
send_unexpected_train_alert(train.entity)
else
return
end
end
if is_train_empty then
remove_available_train(map_data, train_id, train)
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id])
set_depot_schedule(train_entity, train.depot_name)
interface_raise_train_parked_at_depot(train_id, depot_id)
else
--train still has cargo
if mod_settings.react_to_nonempty_train_in_depot then
lock_train(train_entity)
remove_train(map_data, train_id, train)
send_nonempty_train_in_depot_alert(train_entity)
end
interface_raise_train_nonempty_in_depot(depot_id, train_entity, train_id)
end
elseif is_train_empty then
--NOTE: only place where new Train
train = {
entity = train_entity,
--layout_id = set_train_layout,
--item_slot_capacity = set_train_layout,
--fluid_capacity = set_train_layout,
--status = add_available_train_to_depot,
p_station_id = 0,
r_station_id = 0,
manifest = nil,
last_manifest_tick = map_data.total_ticks,
has_filtered_wagon = nil,
--is_available = add_available_train_to_depot,
--parked_at_depot_id = add_available_train_to_depot,
--depot_name = add_available_train_to_depot,
--network_name = add_available_train_to_depot,
--network_flag = add_available_train_to_depot,
--priority = add_available_train_to_depot,
}
set_train_layout(map_data, train)
map_data.trains[train_id] = train
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id])
set_depot_schedule(train_entity, train.depot_name)
interface_raise_train_created(train_id, depot_id)
else
if mod_settings.react_to_nonempty_train_in_depot then
lock_train(train_entity)
send_nonempty_train_in_depot_alert(train_entity)
end
interface_raise_train_nonempty_in_depot(depot_id, train_entity)
end
end
---@param map_data MapData
---@param stop LuaEntity
---@param train_id uint
---@param train Train
local function on_train_arrives_buffer(map_data, stop, train_id, train)
if train.manifest then
---@type uint
local station_id = stop.unit_number
if train.status == STATUS_D_TO_P then
if train.p_station_id == station_id then
train.status = STATUS_P
local station = map_data.stations[station_id]
set_comb1(map_data, station, train.manifest, 1)
set_p_wagon_combs(map_data, station, train)
end
elseif train.status == STATUS_P_TO_R then
if train.r_station_id == station_id then
train.status = STATUS_R
local station = map_data.stations[station_id]
set_comb1(map_data, station, train.manifest, -1)
set_r_wagon_combs(map_data, station, train)
end
elseif train.status == STATUS_P and train.p_station_id == station_id then
--this player intervention that is considered valid
elseif (train.status == STATUS_R or train.status == STATUS_R_TO_D) and train.r_station_id == station_id then
--this player intervention that is considered valid
elseif mod_settings.react_to_train_at_incorrect_station then
on_failed_delivery(map_data, train_id, train)
remove_train(map_data, train_id, train)
lock_train(train.entity)
send_lost_train_alert(train.entity, train.depot_name)
end
elseif mod_settings.react_to_train_at_incorrect_station then
--train is lost somehow, probably from player intervention
remove_train(map_data, train_id, train)
send_lost_train_alert(train.entity, train.depot_name)
end
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
---@param train_id uint
---@param train Train
local function on_train_leaves_station(map_data, mod_settings, train_id, train)
if train.manifest then
if train.status == STATUS_P then
train.status = STATUS_P_TO_R
local station = map_data.stations[train.p_station_id]
remove_manifest(map_data, station, train.manifest, 1)
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
if train.has_filtered_wagon then
train.has_filtered_wagon = nil
for carriage_i, carriage in ipairs(train.entity.cargo_wagons) do
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
if inv and inv.is_filtered() then
---@type uint
for i = 1, #inv do
inv.set_filter(i, nil)
end
end
end
end
interface_raise_train_completed_provide(train_id)
elseif train.status == STATUS_R then
train.status = STATUS_R_TO_D
local station = map_data.stations[train.r_station_id]
remove_manifest(map_data, station, train.manifest, -1)
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
--add to available trains for depot bypass
local fuel_fill = 0
local total_slots = 0
for k, v in pairs(train.entity.locomotives) do
if v[1] then
local inv = v[1].get_fuel_inventory()
if inv then
local inv_size = #inv
total_slots = total_slots + inv_size
for i = 1, inv_size do
local item = inv[i--[[@as uint]]]
local count = item.count
if count > 0 then
fuel_fill = fuel_fill + count/get_stack_size(map_data, item.name)
end
end
end
end
end
if total_slots == 0 then
--if total_slots == 0 it's probably a modded electric train
if mod_settings.depot_bypass_threshold < 1 then
add_available_train(map_data, train_id, train)
end
elseif fuel_fill/total_slots > mod_settings.depot_bypass_threshold then
add_available_train(map_data, train_id, train)
end
interface_raise_train_completed_request(train_id)
end
elseif train.status == STATUS_D then
--The train is leaving the depot without a manifest, the player likely intervened
local depot = map_data.depots[train.parked_at_depot_id--[[@as uint]]]
remove_train(map_data, train_id, train)
send_lost_train_alert(train.entity, depot.entity_stop.backer_name)
end
end
---@param map_data MapData
---@param train_id uint
---@param train Train
local function on_train_broken(map_data, train_id, train)
--NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0
if not train.se_is_being_teleported then
if train.manifest then
on_failed_delivery(map_data, train_id, train)
end
remove_train(map_data, train_id, train)
end
end
---@param map_data MapData
---@param pre_train_id uint
local function on_train_modified(map_data, pre_train_id)
local train = map_data.trains[pre_train_id]
--NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0
if train and not train.se_is_being_teleported then
if train.manifest then
on_failed_delivery(map_data, pre_train_id, train)
end
remove_train(map_data, pre_train_id, train)
end
end
local function on_built(event)
local entity = event.entity or event.created_entity
@@ -827,11 +586,11 @@ local function on_built(event)
elseif entity.name == COMBINATOR_NAME then
on_combinator_built(global, entity)
elseif entity.type == "inserter" then
update_station_from_inserter(global, entity)
update_stop_from_inserter(global, entity)
elseif entity.type == "pump" then
update_station_from_pump(global, entity)
update_stop_from_pump(global, entity)
elseif entity.type == "straight-rail" then
update_station_from_rail(global, entity)
update_stop_from_rail(global, entity)
end
end
local function on_broken(event)
@@ -843,11 +602,11 @@ local function on_broken(event)
elseif entity.name == COMBINATOR_NAME then
on_combinator_broken(global, entity)
elseif entity.type == "inserter" then
update_station_from_inserter(global, entity, entity)
update_stop_from_inserter(global, entity, entity)
elseif entity.type == "pump" then
update_station_from_pump(global, entity, entity)
update_stop_from_pump(global, entity, entity)
elseif entity.type == "straight-rail" then
update_station_from_rail(global, entity, nil)
update_stop_from_rail(global, entity, nil)
elseif entity.train then
local train_id = entity.train.id
local train = global.trains[train_id]
@@ -861,47 +620,7 @@ local function on_rotate(event)
if not entity or not entity.valid then return end
if entity.type == "inserter" then
update_station_from_inserter(global, entity)
end
end
local function on_rename(event)
if event.entity.name == "train-stop" then
on_station_rename(global, event.entity, event.old_name)
end
end
local function on_train_built(event)
local train_e = event.train
if event.old_train_id_1 then
on_train_modified(global, event.old_train_id_1)
end
if event.old_train_id_2 then
on_train_modified(global, event.old_train_id_2)
end
end
local function on_train_changed(event)
local train_e = event.train--[[@as LuaTrain]]
if not train_e.valid then return end
local train_id = train_e.id
local train = global.trains[train_id]
if train_e.state == defines.train_state.wait_station then
local stop = train_e.station
if stop and stop.valid and stop.name == "train-stop" then
if global.stations[stop.unit_number] then
if train then
on_train_arrives_buffer(global, stop, train_id, train)
end
else
local depot_id = stop.unit_number--[[@as uint]]
if global.depots[depot_id] then
on_train_arrives_depot(global, depot_id, train_e)
end
end
end
elseif event.old_state == defines.train_state.wait_station then
if train then
on_train_leaves_station(global, mod_settings, train_id, train)
end
update_stop_from_inserter(global, entity)
end
end
@@ -927,13 +646,20 @@ local function on_paste(event)
end
end
local function on_rename(event)
if event.entity.name == "train-stop" then
on_stop_rename(global, event.entity, event.old_name)
end
end
local function on_settings_changed(event)
mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as double]]
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.depot_bypass_threshold = settings.global["cybersyn-depot-bypass-threshold"].value--[[@as double]]
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
@@ -946,6 +672,7 @@ local function on_settings_changed(event)
script.on_nth_tick(nil)
end
end
interface_raise_on_mod_settings_changed(event)
end
local function setup_se_compat()
@@ -1012,29 +739,45 @@ local function setup_se_compat()
train.se_awaiting_rename = nil
end
if not (train.status == STATUS_D_TO_P or train.status == STATUS_P_TO_R) then return end
if not (train.status == STATUS_TO_P or train.status == STATUS_TO_R) then return end
local schedule = train_entity.schedule
if schedule then
local p_station = map_data.stations[train.p_station_id]
local p_name = p_station.entity_stop.backer_name
local p_surface_i = p_station.entity_stop.surface.index
local r_station = map_data.stations[train.r_station_id]
local r_name = r_station.entity_stop.backer_name
local r_surface_i = r_station.entity_stop.surface.index
local records = schedule.records
local i = schedule.current
while i <= #records do
if records[i].station == p_name and p_surface_i ~= old_surface_index then
table_insert(records, i, create_direct_to_station_order(p_station.entity_stop))
i = i + 1
elseif records[i].station == r_name and r_surface_i ~= old_surface_index then
table_insert(records, i, create_direct_to_station_order(r_station.entity_stop))
if train.status == STATUS_TO_P or train.status == STATUS_TO_R then
local p_station = map_data.stations[train.p_station_id]
local p_name = p_station.entity_stop.backer_name
local p_surface_i = p_station.entity_stop.surface.index
local r_station = map_data.stations[train.r_station_id]
local r_name = r_station.entity_stop.backer_name
local r_surface_i = r_station.entity_stop.surface.index
local records = schedule.records
local i = schedule.current
while i <= #records do
if records[i].station == p_name and p_surface_i ~= old_surface_index then
table_insert(records, i, create_direct_to_station_order(p_station.entity_stop))
i = i + 1
elseif records[i].station == r_name and r_surface_i ~= old_surface_index then
table_insert(records, i, create_direct_to_station_order(r_station.entity_stop))
i = i + 1
end
i = i + 1
end
i = i + 1
train_entity.schedule = schedule
elseif train.status == STATUS_TO_F then
local refueler = map_data.refuelers[train.refueler_id]
local f_name = refueler.entity_stop.backer_name
local f_surface_i = refueler.entity_stop.surface.index
local records = schedule.records
local i = schedule.current
while i <= #records do
if records[i].station == f_name and f_surface_i ~= old_surface_index then
table_insert(records, i, create_direct_to_station_order(refueler.entity_stop))
i = i + 1
end
i = i + 1
end
train_entity.schedule = schedule
end
train_entity.schedule = schedule
end
interface_raise_train_teleported(new_id, old_id)
end)
@@ -1061,9 +804,10 @@ local function main()
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.depot_bypass_threshold = settings.global["cybersyn-depot-bypass-threshold"].value--[[@as double]]
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]]
mod_settings.missing_train_alert_enabled = true
mod_settings.stuck_train_alert_enabled = true
+30 -118
View File
@@ -2,122 +2,6 @@ local flib_migration = require("__flib__.migration")
local migrations_table = {
["0.2.1"] = function()
---@type MapData
local map_data = global
for id, station in pairs(map_data.stations) do
station.p_threshold = nil
end
end,
["0.3.0"] = function()
---@type MapData
local map_data = global
map_data.warmup_station_ids = {}
map_data.active_station_ids = map_data.all_station_ids
map_data.all_station_ids = nil
mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]]
end,
["0.4.0"] = function()
---@type MapData
local map_data = global
map_data.is_player_cursor_blueprint = {}
map_data.to_comb_params = {}
for id, comb in pairs(map_data.to_comb) do
map_data.to_comb_params[id] = get_comb_params(comb)
end
end,
["0.4.1"] = function()
---@type MapData
local map_data = global
map_data.tick_state = STATE_INIT
for id, station in pairs(map_data.stations) do
station.allows_all_trains = station.allow_all_trains or station.allows_all_trains
station.allow_all_trains = nil
end
end,
["0.4.2"] = function()
---@type MapData
local map_data = global
map_data.tick_state = STATE_INIT
map_data.available_trains = map_data.trains_available
for id, train in pairs(map_data.trains) do
local depot = train.depot
if depot then
train.parked_at_depot_id = depot.entity_comb.unit_number
train.network_name = depot.network_name
train.network_flag = depot.network_flag
train.priority = depot.priority
else
train.network_name = ""
train.network_flag = 0
train.priority = 0
end
end
for id, depot in pairs(map_data.depots) do
map_data.depots[id] = {
entity_comb = depot.entity_comb,
entity_stop = depot.entity_stop,
available_train_id = depot.available_train,
}
end
end,
["0.4.3"] = function()
---@type MapData
local map_data = global
map_data.tick_state = STATE_INIT
for id, station in pairs(map_data.stations) do
set_station_from_comb_state(station)
station.allow_all_trains = nil
end
for id, train in pairs(map_data.trains) do
train.last_manifest_tick = map_data.total_ticks
end
mod_settings.stuck_train_time = settings.global["cybersyn-stuck-train-time"].value--[[@as int]]
end,
["0.4.4"] = function()
---@type MapData
local map_data = global
map_data.tick_state = STATE_INIT
for id, layout in pairs(map_data.layouts) do
local new_layout = {}
local i = 1
for c in string.gmatch(layout, ".") do
if c == "N" then
elseif c == "C" then
new_layout[i] = 1
elseif c == "F" then
new_layout[i] = 2
end
i = i + 1
end
map_data.layouts[id] = new_layout
end
end,
["0.5.1"] = function()
---@type MapData
local map_data = global
map_data.tick_state = STATE_INIT
map_data.is_player_cursor_blueprint = nil
for id, layout in pairs(map_data.layouts) do
local new_layout = {}
local max_i = 0
for i, v in pairs(layout) do
new_layout[i] = v
if i > max_i then
max_i = i
end
end
for i = 1, max_i do
if new_layout[i] == nil then
new_layout[i] = 0
end
end
map_data.layouts[id] = new_layout
end
for id, station in pairs(map_data.stations) do
reset_station_layout(map_data, station)
end
end,
["1.0.3"] = function()
---@type MapData
local map_data = global
@@ -173,9 +57,9 @@ local migrations_table = {
map_data.tick_data = {}
for id, station in pairs(map_data.stations) do
local params = get_comb_params(station.entity_comb1)
if params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST then
if params.operation == MODE_PRIMARY_IO_FAILED_REQUEST then
station.display_state = 1
elseif params.operation == OPERATION_PRIMARY_IO_ACTIVE then
elseif params.operation == MODE_PRIMARY_IO_ACTIVE then
station.display_state = 2
else
station.display_state = 0
@@ -184,7 +68,35 @@ local migrations_table = {
station.update_display = nil
end
end,
["1.1.0"] = function()
---@type MapData
local map_data = global
map_data.tick_state = STATE_INIT
map_data.tick_data = {}
map_data.refuelers = {}
map_data.to_refuelers = {}
for id, station in pairs(map_data.stations) do
station.p_count_or_r_threshold_per_item = nil
end
local OLD_STATUS_R_TO_D = 5
local NEW_STATUS_TO_D = 5
local NEW_STATUS_TO_D_BYPASS = 6
for id, train in pairs(map_data.trains) do
if train.status == OLD_STATUS_R_TO_D then
train.manifest = nil
train.p_station_id = nil
train.r_station_id = nil
if train.is_available then
train.status = NEW_STATUS_TO_D_BYPASS
else
train.status = NEW_STATUS_TO_D
end
end
end
end,
}
--STATUS_R_TO_D = 5
---@param data ConfigurationChangedData
function on_config_changed(data)
+367 -290
View File
@@ -13,20 +13,353 @@ local on_station_created = nil
local on_station_removed = nil
local on_depot_created = nil
local on_depot_removed = nil
local on_refueler_created = nil
local on_refueler_removed = nil
local on_train_created = nil
local on_train_removed = nil
local on_train_available = nil
local on_train_nonempty_in_depot = nil
local on_train_dispatched = nil
local on_train_dispatch_failed = nil
local on_train_failed_delivery = nil
local on_train_completed_provide = nil
local on_train_completed_request = nil
local on_train_parked_at_depot = nil
local on_train_status_changed = nil
local on_train_stuck = nil
local on_train_teleport_started = nil
local on_train_teleported = nil
local on_tick_init = nil
local on_mod_settings_changed = nil
local interface = {}
------------------------------------------------------------------
--[[get event id functions]]
------------------------------------------------------------------
function interface.get_on_combinator_changed()
if not on_combinator_changed then on_combinator_changed = script_generate_event_name() end
return on_combinator_changed
end
function interface.get_on_station_created()
if not on_station_created then on_station_created = script_generate_event_name() end
return on_station_created
end
function interface.get_on_station_removed()
if not on_station_removed then on_station_removed = script_generate_event_name() end
return on_station_removed
end
function interface.get_on_depot_created()
if not on_depot_created then on_depot_created = script_generate_event_name() end
return on_depot_created
end
function interface.get_on_depot_removed()
if not on_depot_removed then on_depot_removed = script_generate_event_name() end
return on_depot_removed
end
function interface.get_on_refueler_created()
if not on_refueler_created then on_refueler_created = script_generate_event_name() end
return on_refueler_created
end
function interface.get_on_refueler_removed()
if not on_refueler_removed then on_refueler_removed = script_generate_event_name() end
return on_refueler_removed
end
function interface.get_on_train_created()
if not on_train_created then on_train_created = script_generate_event_name() end
return on_train_created
end
function interface.get_on_train_removed()
if not on_train_removed then on_train_removed = script_generate_event_name() end
return on_train_removed
end
function interface.get_on_train_available()
if not on_train_available then on_train_available = script_generate_event_name() end
return on_train_available
end
function interface.get_on_train_nonempty_in_depot()
if not on_train_nonempty_in_depot then on_train_nonempty_in_depot = script_generate_event_name() end
return on_train_nonempty_in_depot
end
function interface.get_on_train_dispatch_failed()
if not on_train_dispatch_failed then on_train_dispatch_failed = script_generate_event_name() end
return on_train_dispatch_failed
end
function interface.get_on_train_failed_delivery()
if not on_train_failed_delivery then on_train_failed_delivery = script_generate_event_name() end
return on_train_failed_delivery
end
function interface.get_on_train_status_changed()
if not on_train_status_changed then on_train_status_changed = script_generate_event_name() end
return on_train_status_changed
end
function interface.get_on_train_stuck()
if not on_train_stuck then on_train_stuck = script_generate_event_name() end
return on_train_stuck
end
function interface.get_on_train_teleport_started()
if not on_train_teleport_started then on_train_teleport_started = script_generate_event_name() end
return on_train_teleport_started
end
function interface.get_on_train_teleported()
if not on_train_teleported then on_train_teleported = script_generate_event_name() end
return on_train_teleported
end
function interface.get_on_tick_init()
if not on_tick_init then on_tick_init = script_generate_event_name() end
return on_tick_init
end
function interface.get_on_mod_settings_changed()
if not on_mod_settings_changed then on_mod_settings_changed = script_generate_event_name() end
return on_mod_settings_changed
end
------------------------------------------------------------------
--[[helper functions]]
------------------------------------------------------------------
--NOTE: the policy of cybersyn is to give modders access to as much of the raw data of the mod as possible. Factorio only allows me to return copies of the original data rather than the actual thing, which sucks. The unsafe api has some tools to help you bypass this limitation.
function interface.get_mod_settings()
return mod_settings
end
---@param key string
function interface.read_setting(key)
return mod_settings[key]
end
---@param ... string|int
function interface.read_global(...)
--this can read anything off of cybersyn's map_data
--so interface.read_global("trains", 31415, "manifest") == global.trains[31415].manifest (or nil if train 31415 does not exist)
--the second return value is how many parameters could be processed before a nil value was encountered (in the above example it's useful for telling apart global.trains[31415] == nil vs global.trains[31415].manifest == nil)
local base = global
local depth = 0
for i, v in ipairs({...}) do
depth = i
base = base[v]
if not base then break end
end
return base, depth
end
---@param id uint
function interface.get_station(id)
return global.stations[id]
end
---@param id uint
function interface.get_depot(id)
return global.depots[id]
end
---@param id uint
function interface.get_refueler(id)
return global.refuelers[id]
end
---@param id uint
function interface.get_train(id)
return global.trains[id]
end
---@param train_entity LuaTrain
function interface.get_train_id_from_luatrain(train_entity)
return train_entity.id
end
---@param stop LuaEntity
function interface.get_id_from_stop(stop)
return stop.unit_number
end
---@param comb LuaEntity
function interface.get_id_from_comb(comb)
local stop = global.to_stop[comb.unit_number]
if stop then
return stop.unit_number
end
end
------------------------------------------------------------------
--[[safe API]]
------------------------------------------------------------------
--NOTE: These functions can be called whenever however so long as their parameters have the correct types. Their ability to cause harm is extremely minimal.
---@param key string
---@param value any
function interface.write_setting(key, value)
--be careful that the value you write is of the correct type specified in global.lua
--these settings are not saved and have to be set on load and on init
mod_settings[key] = value
end
---@param comb LuaEntity
function interface.combinator_update(comb)
combinator_update(global, comb)
end
---@param train_id uint
function interface.update_train_layout(train_id)
local train = global.trains[train_id]
assert(train)
local old_layout_id = train.layout_id
local count = global.layout_train_count[old_layout_id]
if count <= 1 then
global.layout_train_count[old_layout_id] = nil
global.layouts[old_layout_id] = nil
for station_id, station in pairs(global.stations) do
station.accepted_layouts[old_layout_id] = nil
end
else
global.layout_train_count[old_layout_id] = count - 1
end
set_train_layout(global, train)
end
---@param layout_pattern (0|1|2|3)[]
---@param layout (0|1|2)[]
function interface.is_layout_accepted(layout_pattern, layout)
return is_layout_accepted(layout_pattern, layout)
end
---@param layout_pattern (0|1|2|3)[]
---@param layout (0|1|2)[]
function interface.is_refuel_layout_accepted(layout_pattern, layout)
return is_refuel_layout_accepted(layout_pattern, layout)
end
---@param stop_id uint
---@param forbidden_entity LuaEntity?
---@param force_update boolean?
function interface.reset_stop_layout(stop_id, forbidden_entity, force_update)
local is_station = true
---@type Refueler|Station
local stop = global.stations[stop_id]
if not stop then
is_station = false
stop = global.refuelers[stop_id]
end
assert(stop)
if force_update or not stop.allows_all_trains then
reset_stop_layout(global, stop, is_station, forbidden_entity)
end
end
---@param rail LuaEntity
---@param forbidden_entity LuaEntity?
---@param force_update boolean?
function interface.update_stop_from_rail(rail, forbidden_entity, force_update)
update_stop_from_rail(global, rail, forbidden_entity, force_update)
end
------------------------------------------------------------------
--[[unsafe API]]
------------------------------------------------------------------
--NOTE: The following functions can cause serious longterm damage to someone's world if they are given bad parameters. Please refer to global.lua for type information. Use caution.
---@param value any
---@param ... string|int
function interface.write_global(value, ...)
--this can write anything into cybersyn's map_data, please be very careful with anything you write, it can cause permanent damage
--so interface.read_global(nil, "trains", 31415, "manifest") will cause global.trains[31415].manifest = nil (or return false if train 31415 does not exist)
local params = {...}
local size = #params
local key = params[size]
assert(key ~= nil)
local base = global
for i = 1, size - 1 do
base = base[params[i]]
if not base then return false end
end
base[key] = value
return true
end
---@param station_id Station
---@param manifest Manifest
---@param sign -1|1
function interface.remove_manifest_from_station_deliveries(station_id, manifest, sign)
local station = global.stations[station_id]
assert(station)
remove_manifest(global, station, manifest, sign)
end
---@param r_station_id uint
---@param p_station_id uint
---@param train_id uint
function interface.create_manifest(r_station_id, p_station_id, train_id)
local train = global.trains[train_id]
assert(global.stations[r_station_id] and global.stations[p_station_id] and train and train.is_available)
create_manifest(global, r_station_id, p_station_id, train_id)
end
---@param r_station_id uint
---@param p_station_id uint
---@param train_id uint
---@param manifest Manifest
function interface.create_delivery(r_station_id, p_station_id, train_id, manifest)
local train = global.trains[train_id]
assert(global.stations[r_station_id] and global.stations[p_station_id] and train and train.is_available and manifest)
create_delivery(global, r_station_id, p_station_id, train_id, manifest)
end
---@param train_id uint
function interface.fail_delivery(train_id)
local train = global.trains[train_id]
assert(train)
on_failed_delivery(global, train_id, train)
end
---@param train_id uint
function interface.remove_train(train_id)
local train = global.trains[train_id]
assert(train)
remove_train(global, train_id, train)
end
---@param train_id uint
function interface.add_available_train(train_id)
--This function marks a train as available but not in a depot so it can do depot bypass, be sure the train has no active deliveries before calling this
--available trains can be chosen by the dispatcher to be rescheduled and dispatched for a new delivery
--when this train parks at a depot add_available_train_to_depot will be called on it automatically
local train = global.trains[train_id]
assert(train)
add_available_train(global, train_id, train)
end
---@param depot_id uint
---@param train_id uint
function interface.add_available_train_to_depot(train_id, depot_id)
--This function marks a train as available and in a depot, be sure the train has no active deliveries before calling this
--available trains can be chosen by the dispatcher to be rescheduled and dispatched for a new delivery
local train = global.trains[train_id]
local depot = global.depots[depot_id]
assert(train and depot)
add_available_train_to_depot(global, mod_settings, train_id, train, depot_id, depot)
end
---@param train_id uint
function interface.remove_available_train(train_id)
--this function removes a train from the available trains list so it cannot be rescheduled and dispatched. if the train was not already available nothing will happen
local train = global.trains[train_id]
assert(train)
remove_available_train(global, train_id, train)
end
------------------------------------------------------------------
--[[train schedule]]
------------------------------------------------------------------
interface.create_loading_order = create_loading_order
interface.create_unloading_order = create_unloading_order
interface.create_inactivity_order = create_inactivity_order
interface.create_direct_to_station_order = create_direct_to_station_order
interface.set_depot_schedule = set_depot_schedule
interface.lock_train = lock_train
interface.rename_manifest_schedule = rename_manifest_schedule
interface.se_get_space_elevator_name = se_get_space_elevator_name
interface.se_create_elevator_order = se_create_elevator_order
interface.set_manifest_schedule = set_manifest_schedule
interface.add_refueler_schedule = add_refueler_schedule
------------------------------------------------------------------
--[[alerts]]
------------------------------------------------------------------
interface.send_missing_train_alert = send_missing_train_alert
interface.send_lost_train_alert = send_lost_train_alert
interface.send_unexpected_train_alert = send_unexpected_train_alert
interface.send_nonempty_train_in_depot_alert = send_nonempty_train_in_depot_alert
interface.send_stuck_train_alert = send_stuck_train_alert
remote.add_interface("cybersyn", interface)
------------------------------------------------------------------
--[[internal event calls]]
------------------------------------------------------------------
---@param entity LuaEntity
---@param old_parameters ArithmeticCombinatorParameters
@@ -77,6 +410,25 @@ function interface_raise_depot_removed(old_depot_id, old_depot)
end
end
---@param refueler_id uint
function interface_raise_refueler_created(refueler_id)
if on_refueler_created then
raise_event(on_refueler_created, {
refueler_id = refueler_id,
})
end
end
---@param old_refueler_id uint
---@param old_refueler Refueler
function interface_raise_refueler_removed(old_refueler_id, old_refueler)
if on_refueler_removed then
raise_event(on_refueler_removed, {
old_refueler_id = old_refueler_id, --this id is now invalid
old_refueler = old_refueler, --this is the data that used to be stored at the old id
})
end
end
---@param train_id uint
---@param depot_id uint
function interface_raise_train_created(train_id, depot_id)
@@ -118,14 +470,6 @@ function interface_raise_train_nonempty_in_depot(depot_id, train_entity, train_i
end
end
---@param train_id uint
function interface_raise_train_dispatched(train_id)
if on_train_dispatched then
raise_event(on_train_dispatched, {
train_id = train_id,
})
end
end
---@param train_id uint
function interface_raise_train_dispatch_failed(train_id)
--this event is rare, it can only occur when a train is bypassing the depot and can't find a path to the provide station, that train is marked as unavailable but not dispatched
@@ -154,28 +498,14 @@ function interface_raise_train_failed_delivery(train_id, was_p_in_progress, p_st
end
end
---@param train_id uint
function interface_raise_train_completed_provide(train_id)
if on_train_completed_provide then
raise_event(on_train_completed_provide, {
---@param old_status uint
---@param new_status uint
function interface_raise_train_status_changed(train_id, old_status, new_status)
if on_train_status_changed then
raise_event(on_train_status_changed, {
train_id = train_id,
})
end
end
---@param train_id uint
function interface_raise_train_completed_request(train_id)
if on_train_completed_request then
raise_event(on_train_completed_request, {
train_id = train_id,
})
end
end
---@param train_id uint
---@param depot_id uint
function interface_raise_train_parked_at_depot(train_id, depot_id)
if on_train_parked_at_depot then
raise_event(on_train_parked_at_depot, {
train_id = train_id,
depot_id = depot_id,
old_status = old_status,
new_status = new_status,
})
end
end
@@ -191,7 +521,7 @@ end
function interface_raise_train_teleport_started(old_train_id)
if on_train_teleport_started then
raise_event(on_train_teleport_started, {
old_train_id = old_train_id,--this id is currently valid but will become valid just before on_train_teleported is raised
old_train_id = old_train_id,--this id is currently valid but will become invalid just before on_train_teleported is raised
})
end
end
@@ -212,261 +542,8 @@ function interface_raise_tick_init()
})
end
end
local interface = {}
------------------------------------------------------------------
--[[get event id functions]]
------------------------------------------------------------------
function interface.get_on_combinator_changed()
if not on_combinator_changed then on_combinator_changed = script_generate_event_name() end
return on_combinator_changed
end
function interface.get_on_station_created()
if not on_station_created then on_station_created = script_generate_event_name() end
return on_station_created
end
function interface.get_on_station_removed()
if not on_station_removed then on_station_removed = script_generate_event_name() end
return on_station_removed
end
function interface.get_on_depot_created()
if not on_depot_created then on_depot_created = script_generate_event_name() end
return on_depot_created
end
function interface.get_on_depot_removed()
if not on_depot_removed then on_depot_removed = script_generate_event_name() end
return on_depot_removed
end
function interface.get_on_train_created()
if not on_train_created then on_train_created = script_generate_event_name() end
return on_train_created
end
function interface.get_on_train_removed()
if not on_train_removed then on_train_removed = script_generate_event_name() end
return on_train_removed
end
function interface.get_on_train_available()
if not on_train_available then on_train_available = script_generate_event_name() end
return on_train_available
end
function interface.get_on_train_nonempty_in_depot()
if not on_train_nonempty_in_depot then on_train_nonempty_in_depot = script_generate_event_name() end
return on_train_nonempty_in_depot
end
function interface.get_on_train_dispatched()
if not on_train_dispatched then on_train_dispatched = script_generate_event_name() end
return on_train_dispatched
end
function interface.get_on_train_dispatch_failed()
if not on_train_dispatch_failed then on_train_dispatch_failed = script_generate_event_name() end
return on_train_dispatch_failed
end
function interface.get_on_train_failed_delivery()
if not on_train_failed_delivery then on_train_failed_delivery = script_generate_event_name() end
return on_train_failed_delivery
end
function interface.get_on_train_completed_provide()
if not on_train_completed_provide then on_train_completed_provide = script_generate_event_name() end
return on_train_completed_provide
end
function interface.get_on_train_completed_request()
if not on_train_completed_request then on_train_completed_request = script_generate_event_name() end
return on_train_completed_request
end
function interface.get_on_train_parked_at_depot()
if not on_train_parked_at_depot then on_train_parked_at_depot = script_generate_event_name() end
return on_train_parked_at_depot
end
function interface.get_on_train_stuck()
if not on_train_stuck then on_train_stuck = script_generate_event_name() end
return on_train_stuck
end
function interface.get_on_train_teleport_started()
if not on_train_teleport_started then on_train_teleport_started = script_generate_event_name() end
return on_train_teleport_started
end
function interface.get_on_train_teleported()
if not on_train_teleported then on_train_teleported = script_generate_event_name() end
return on_train_teleported
end
function interface.get_on_tick_init()
if not on_tick_init then on_tick_init = script_generate_event_name() end
return on_tick_init
end
------------------------------------------------------------------
--[[safe API]]
------------------------------------------------------------------
--NOTE: These functions can be called whenever however so long as their parameters have the correct types. Their ability to cause harm is extremely minimal.
---@param comb LuaEntity
function interface.combinator_update(comb)
combinator_update(global, comb)
end
---@param train_id uint
function interface.update_train_layout(train_id)
local train = global.trains[train_id]
assert(train)
local old_layout_id = train.layout_id
local count = global.layout_train_count[old_layout_id]
if count <= 1 then
global.layout_train_count[old_layout_id] = nil
global.layouts[old_layout_id] = nil
for station_id, station in pairs(global.stations) do
station.accepted_layouts[old_layout_id] = nil
end
else
global.layout_train_count[old_layout_id] = count - 1
end
set_train_layout(global, train)
end
---@param layout_pattern (0|1|2|3)[]
---@param layout (0|1|2)[]
function interface.is_layout_accepted(layout_pattern, layout)
return is_layout_accepted(layout_pattern, layout)
end
---@param station_id uint
---@param forbidden_entity LuaEntity?
---@param force_update boolean?
function interface.reset_station_layout(station_id, forbidden_entity, force_update)
local station = global.stations[station_id]
assert(station)
if force_update or not station.allows_all_trains then
reset_station_layout(global, station, forbidden_entity)
function interface_raise_on_mod_settings_changed(e)
if on_mod_settings_changed then
raise_event(on_mod_settings_changed, e)
end
end
---@param rail LuaEntity
---@param forbidden_entity LuaEntity?
---@param force_update boolean?
function interface.update_station_from_rail(rail, forbidden_entity, force_update)
update_station_from_rail(global, rail, forbidden_entity, force_update)
end
------------------------------------------------------------------
--[[unsafe API]]
------------------------------------------------------------------
--NOTE: The following functions can cause serious longterm damage to someone's world if they are given bad parameters. Use caution.
---@param station_id Station
---@param manifest Manifest
---@param sign -1|1
function interface.remove_manifest_from_station_deliveries(station_id, manifest, sign)
local station = global.stations[station_id]
assert(station)
remove_manifest(global, station, manifest, sign)
end
---@param r_station_id uint
---@param p_station_id uint
---@param train_id uint
---@param primary_item_name string?
function interface.create_new_delivery_between_stations(r_station_id, p_station_id, train_id, primary_item_name)
local train = global.trains[train_id]
assert(global.stations[r_station_id] and global.stations[p_station_id] and train and train.is_available)
send_train_between(global, r_station_id, p_station_id, train_id, primary_item_name)
end
---@param train_id uint
function interface.fail_delivery(train_id)
local train = global.trains[train_id]
assert(train)
on_failed_delivery(global, train_id, train)
end
---@param train_id uint
function interface.remove_train(train_id)
local train = global.trains[train_id]
assert(train)
remove_train(global, train_id, train)
end
---@param train_id uint
function interface.add_available_train(train_id)
local train = global.trains[train_id]
assert(train)
add_available_train(global, train_id, train)
end
---@param depot_id uint
---@param train_id uint
function interface.add_available_train_to_depot(train_id, depot_id)
local train = global.trains[train_id]
local depot = global.depots[depot_id]
assert(train and depot)
add_available_train_to_depot(global, mod_settings, train_id, train, depot_id, depot)
end
---@param train_id uint
function interface.remove_available_train(train_id)
local train = global.trains[train_id]
assert(train)
remove_available_train(global, train_id, train)
end
------------------------------------------------------------------
--[[train schedule]]
------------------------------------------------------------------
interface.create_loading_order = create_loading_order
interface.create_unloading_order = create_unloading_order
interface.create_inactivity_order = create_inactivity_order
interface.create_direct_to_station_order = create_direct_to_station_order
interface.set_depot_schedule = set_depot_schedule
interface.lock_train = lock_train
interface.rename_manifest_schedule = rename_manifest_schedule
interface.se_get_space_elevator_name = se_get_space_elevator_name
interface.se_create_elevator_order = se_create_elevator_order
interface.set_manifest_schedule = set_manifest_schedule
------------------------------------------------------------------
--[[alerts]]
------------------------------------------------------------------
interface.send_missing_train_alert = send_missing_train_alert
interface.send_lost_train_alert = send_lost_train_alert
interface.send_unexpected_train_alert = send_unexpected_train_alert
interface.send_nonempty_train_in_depot_alert = send_nonempty_train_in_depot_alert
interface.send_stuck_train_alert = send_stuck_train_alert
------------------------------------------------------------------
--[[helper functions]]
------------------------------------------------------------------
--NOTE: the policy of cybersyn is to give modders access to the raw data of the mod, please either treat all tables returned from the modding interface as "read only", or if you do modify them take responsibility that your modification does not result in an error occuring in cybersyn later on.
--NOTE: the follow functions aren't strictly necessary; they are provided more as a guide how the mod api works rather than as practical functions.
function interface.get_map_data()
return global
end
function interface.get_mod_settings()
return mod_settings
end
---@param id uint
function interface.get_station(id)
return global.stations[id]
end
---@param id uint
function interface.get_depot(id)
return global.depots[id]
end
---@param id uint
function interface.get_train(id)
return global.trains[id]
end
---@param train_entity LuaTrain
function interface.get_train_id_from_luatrain(train_entity)
return train_entity.id
end
---@param stop LuaEntity
function interface.get_station_or_depot_id_from_stop(stop)
return stop.unit_number
end
---@param comb LuaEntity
function interface.get_station_or_depot_id_from_comb(comb)
local stop = global.to_stop[comb.unit_number]
if stop then
return stop.unit_number
end
end
remote.add_interface("cybersyn", interface)
+447
View File
@@ -0,0 +1,447 @@
--By Mami
local min = math.min
local INF = math.huge
---@param map_data MapData
---@param station Station
---@param manifest Manifest
---@param sign int?
local function set_comb1(map_data, station, manifest, sign)
local comb = station.entity_comb1
if comb.valid then
if manifest then
local signals = {}
for i, item in ipairs(manifest) do
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = sign*item.count}
end
set_combinator_output(map_data, comb, signals)
else
set_combinator_output(map_data, comb, nil)
end
end
end
---@param map_data MapData
---@param train_id uint
---@param train Train
function on_failed_delivery(map_data, train_id, train)
--NOTE: must either change this train's status or remove it after this call
local p_station_id = train.p_station_id--[[@as uint]]
local r_station_id = train.r_station_id--[[@as uint]]
local manifest = train.manifest--[[@as Manifest]]
local is_p_in_progress = train.status == STATUS_TO_P or train.status == STATUS_P
local is_r_in_progress = is_p_in_progress or train.status == STATUS_TO_R or train.status == STATUS_R
if is_p_in_progress then
local station = map_data.stations[p_station_id]
remove_manifest(map_data, station, manifest, 1)
if train.status == STATUS_P then
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
end
end
if is_r_in_progress then
local station = map_data.stations[r_station_id]
remove_manifest(map_data, station, manifest, -1)
if train.status == STATUS_R then
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
end
end
train.r_station_id = nil
train.p_station_id = nil
train.manifest = nil
interface_raise_train_failed_delivery(train_id, is_p_in_progress, p_station_id, is_r_in_progress, r_station_id, manifest)
end
---@param map_data MapData
---@param train_id uint
---@param train Train
function add_available_train(map_data, train_id, train)
local network_name = train.network_name
if network_name then
local network = map_data.available_trains[network_name]
if not network then
network = {}
map_data.available_trains[network_name] = network
end
network[train_id] = true
train.is_available = true
interface_raise_train_available(train_id)
end
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
---@param depot_id uint
---@param depot Depot
---@param train_id uint
---@param train Train
function add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
local comb = depot.entity_comb
local network_name = get_comb_network_name(comb)
if network_name then
local network = map_data.available_trains[network_name]
if not network then
network = {}
map_data.available_trains[network_name] = network
end
network[train_id] = true
train.is_available = true
end
depot.available_train_id = train_id
train.status = STATUS_D
train.parked_at_depot_id = depot_id
train.depot_name = depot.entity_stop.backer_name
train.se_depot_surface_i = depot.entity_stop.surface.index
train.network_name = network_name
train.network_flag = mod_settings.network_flag
train.priority = 0
if network_name then
local signals = comb.get_merged_signals(defines.circuit_connector_id.combinator_input)
if signals then
for k, v in pairs(signals) do
local item_name = v.signal.name
local item_count = v.count
if item_name then
if item_name == SIGNAL_PRIORITY then
train.priority = item_count
end
if item_name == network_name then
train.network_flag = item_count
end
end
end
end
interface_raise_train_available(train_id)
end
end
---@param map_data MapData
---@param train_id uint
---@param train Train
function remove_available_train(map_data, train_id, train)
---@type uint
if train.is_available and train.network_name then
local network = map_data.available_trains[train.network_name--[[@as string]]]
if network then
network[train_id] = nil
if next(network) == nil then
map_data.available_trains[train.network_name] = nil
end
end
train.is_available = nil
end
end
---@param map_data MapData
---@param depot_id uint
---@param train_entity LuaTrain
local function on_train_arrives_depot(map_data, depot_id, train_entity)
local contents = train_entity.get_contents()
local fluid_contents = train_entity.get_fluid_contents()
local is_train_empty = next(contents) == nil and next(fluid_contents) == nil
local train_id = train_entity.id
local train = map_data.trains[train_id]
if train then
if train.status == STATUS_TO_D then
elseif train.status == STATUS_TO_D_BYPASS or train.status == STATUS_D then
--shouldn't be possible to get train.status == STATUS_D
remove_available_train(map_data, train_id, train)
elseif mod_settings.react_to_train_early_to_depot then
if train.manifest then
on_failed_delivery(map_data, train_id, train)
end
send_unexpected_train_alert(train.entity)
else
return
end
if is_train_empty then
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)
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(train_entity)
remove_train(map_data, train_id, train)
send_nonempty_train_in_depot_alert(train_entity)
end
interface_raise_train_nonempty_in_depot(depot_id, train_entity, train_id)
end
elseif is_train_empty then
--NOTE: only place where new Train
train = {
entity = train_entity,
--layout_id = set_train_layout,
--item_slot_capacity = set_train_layout,
--fluid_capacity = set_train_layout,
--status = add_available_train_to_depot,
p_station_id = 0,
r_station_id = 0,
manifest = nil,
last_manifest_tick = map_data.total_ticks,
has_filtered_wagon = nil,
--is_available = add_available_train_to_depot,
--parked_at_depot_id = add_available_train_to_depot,
--depot_name = add_available_train_to_depot,
--network_name = add_available_train_to_depot,
--network_flag = add_available_train_to_depot,
--priority = add_available_train_to_depot,
}
set_train_layout(map_data, train)
map_data.trains[train_id] = train
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id])
set_depot_schedule(train_entity, train.depot_name)
interface_raise_train_created(train_id, depot_id)
else
if mod_settings.react_to_nonempty_train_in_depot then
lock_train(train_entity)
send_nonempty_train_in_depot_alert(train_entity)
end
interface_raise_train_nonempty_in_depot(depot_id, train_entity)
end
end
---@param map_data MapData
---@param station_id uint
---@param train_id uint
---@param train Train
local function on_train_arrives_station(map_data, station_id, train_id, train)
if train.manifest then
---@type uint
if train.status == STATUS_TO_P then
if train.p_station_id == station_id then
train.status = STATUS_P
local station = map_data.stations[station_id]
set_comb1(map_data, station, train.manifest, 1)
set_p_wagon_combs(map_data, station, train)
interface_raise_train_status_changed(train_id, STATUS_TO_P, STATUS_P)
end
elseif train.status == STATUS_TO_R then
if train.r_station_id == station_id then
train.status = STATUS_R
local station = map_data.stations[station_id]
set_comb1(map_data, station, train.manifest, -1)
set_r_wagon_combs(map_data, station, train)
interface_raise_train_status_changed(train_id, STATUS_TO_R, STATUS_R)
end
elseif train.status == STATUS_P and train.p_station_id == station_id then
--this is player intervention that is considered valid
elseif (train.status == STATUS_R or train.status == STATUS_TO_D or train.status == STATUS_TO_D_BYPASS) and train.r_station_id == station_id then
--this is player intervention that is considered valid
elseif mod_settings.react_to_train_at_incorrect_station then
on_failed_delivery(map_data, train_id, train)
remove_train(map_data, train_id, train)
lock_train(train.entity)
send_lost_train_alert(train.entity, train.depot_name)
end
elseif mod_settings.react_to_train_at_incorrect_station then
--train is lost somehow, probably from player intervention
remove_train(map_data, train_id, train)
send_lost_train_alert(train.entity, train.depot_name)
end
end
---@param map_data MapData
---@param refueler_id uint
---@param train_id uint
---@param train Train
local function on_train_arrives_refueler(map_data, refueler_id, train_id, train)
if train.status == STATUS_TO_F then
local refueler = map_data.refuelers[refueler_id]
train.status = STATUS_F
set_refueler_combs(map_data, refueler, train)
interface_raise_train_status_changed(train_id, STATUS_TO_F, STATUS_F)
end
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
---@param train_id uint
---@param train Train
local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
if train.status == STATUS_P then
train.status = STATUS_TO_R
local station = map_data.stations[train.p_station_id]
remove_manifest(map_data, station, train.manifest, 1)
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
if train.has_filtered_wagon then
train.has_filtered_wagon = nil
for carriage_i, carriage in ipairs(train.entity.cargo_wagons) do
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
if inv and inv.is_filtered() then
---@type uint
for i = 1, #inv do
inv.set_filter(i, nil)
end
end
end
end
interface_raise_train_status_changed(train_id, STATUS_P, STATUS_TO_R)
elseif train.status == STATUS_R then
local station = map_data.stations[train.r_station_id]
remove_manifest(map_data, station, train.manifest, -1)
set_comb1(map_data, station, nil)
unset_wagon_combs(map_data, station)
--complete delivery
train.p_station_id = nil
train.r_station_id = nil
train.manifest = nil
--add to available trains for depot bypass
local fuel_fill = INF
for _, v in pairs(train.entity.locomotives) do
for _, loco in pairs(v) do
local inv = loco.get_fuel_inventory()
if inv then
local inv_size = #inv
if inv_size > 0 then
local fuel_total = 0
---@type uint
for i = 1, inv_size do
local item = inv[i]
if item.valid_for_read then
fuel_total = fuel_total + item.count/get_stack_size(map_data, item.name)
end
end
fuel_fill = min(fuel_fill, fuel_total/inv_size)
end
end
end
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
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(mod_settings, refueler)
if bit32.btest(train.network_flag, refueler.network_flag) and (refueler.allows_all_trains or refueler.accepted_layouts[train.layout_id]) and refueler.trains_total < refueler.entity_stop.trains_limit then
local accepted = false
local dist = nil
if refueler.priority == best_prior then
dist = get_stop_dist(train.entity.front_stock, refueler.entity_stop)
accepted = dist < best_dist
end
if accepted or refueler.priority > best_prior then
best_refueler_id = id
best_dist = dist or get_stop_dist(train.entity.front_stock, refueler.entity_stop)
best_prior = refueler.priority
end
end
end
if best_refueler_id then
train.status = STATUS_TO_F
train.refueler_id = best_refueler_id
local refueler = map_data.refuelers[best_refueler_id]
refueler.trains_total = refueler.trains_total + 1
add_refueler_schedule(train.entity, refueler.entity_stop, train.depot_name)
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_F)
return
end
end
end
--the train has not qualified for depot bypass nor refueling
train.status = STATUS_TO_D
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D)
elseif train.status == STATUS_F then
local refueler = map_data.refuelers[train.refueler_id]
train.refueler_id = nil
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
train.status = STATUS_TO_D_BYPASS
add_available_train(map_data, train_id, train)
else
train.status = STATUS_TO_D
end
interface_raise_train_status_changed(train_id, STATUS_F, train.status)
elseif train.status == STATUS_D then
--The train is leaving the depot without a manifest, the player likely intervened
local depot = map_data.depots[train.parked_at_depot_id--[[@as uint]]]
remove_train(map_data, train_id, train)
send_lost_train_alert(train.entity, depot.entity_stop.backer_name)
end
end
---@param map_data MapData
---@param train_id uint
---@param train Train
function on_train_broken(map_data, train_id, train)
--NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0
if not train.se_is_being_teleported then
if train.manifest then
on_failed_delivery(map_data, train_id, train)
end
remove_train(map_data, train_id, train)
end
end
---@param map_data MapData
---@param pre_train_id uint
local function on_train_modified(map_data, pre_train_id)
local train = map_data.trains[pre_train_id]
--NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0
if train and not train.se_is_being_teleported then
if train.manifest then
on_failed_delivery(map_data, pre_train_id, train)
end
remove_train(map_data, pre_train_id, train)
end
end
function on_train_built(event)
local train_e = event.train
if event.old_train_id_1 then
on_train_modified(global, event.old_train_id_1)
end
if event.old_train_id_2 then
on_train_modified(global, event.old_train_id_2)
end
end
function on_train_changed(event)
local train_e = event.train--[[@as LuaTrain]]
if not train_e.valid then return end
local train_id = train_e.id
if train_e.state == defines.train_state.wait_station then
local stop = train_e.station
if stop and stop.valid and stop.name == "train-stop" then
local id = stop.unit_number--[[@as uint]]
if global.stations[id] then
local train = global.trains[train_id]
if train then
on_train_arrives_station(global, id, train_id, train)
end
elseif global.depots[id] then
on_train_arrives_depot(global, id, train_e)
elseif global.refuelers[id] then
local train = global.trains[train_id]
if train then
on_train_arrives_refueler(global, id, train_id, train)
end
end
end
elseif event.old_state == defines.train_state.wait_station then
local train = global.trains[train_id]
if train then
on_train_leaves_stop(global, mod_settings, train_id, train)
end
end
end
+10 -3
View File
@@ -47,17 +47,24 @@ data:extend({
},
{
type = "double-setting",
name = "cybersyn-depot-bypass-threshold",
name = "cybersyn-fuel-threshold",
order = "ae",
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 = "af",
order = "ag",
setting_type = "runtime-global",
default_value = 20,
minimum_value = 0,
@@ -66,7 +73,7 @@ data:extend({
{
type = "double-setting",
name = "cybersyn-stuck-train-time",
order = "ag",
order = "ah",
setting_type = "runtime-global",
default_value = 600,
minimum_value = 0,
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB