Experimental 1.2.2 (#21)

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

View File

@@ -6,6 +6,8 @@ Behold one of the most feature-rich and performant train logistics network mods
## Quick Start Guide ## Quick Start Guide
Copy the contents of [Project Cybersyn Official Example Blueprints](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/cybersyn_blueprints.txt) and paste them into the import blueprint text box to see examples of how to create a functional network. I highly recommend taking a look at these; the blueprint book showcases both the basics of the mod and highly advanced designs you can create.
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. 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 "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. 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 "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.
@@ -15,7 +17,7 @@ A bare minimum Cybersyn train network consists of 2 components: depots and stati
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. Be sure that the requester station has the space to at minimum unload 2 fully loaded trains. 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. Be sure that the requester station has the space to at minimum unload 2 fully loaded trains.
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. 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. There is one issue with this kind of network though; trains won't always deliver full loads of cargo, meaning trains will make deliveries more frequently than is necessary. To fix this issue you will need to start using "request thresholds". To get a full picture of how request thresholds work, either import the official example blueprints from above, or read the **Request thresholds** section below.
## Features ## Features
@@ -37,7 +39,7 @@ 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. 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 automatically visit **centralized refuelers**. 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. 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.
@@ -69,7 +71,7 @@ This mod adds a single new entity to the game, the cybernetic combinator. This c
![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/multi-item.png) ![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. 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 (negative) or unloaded (positive) from the train.
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 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.
@@ -81,11 +83,11 @@ Stations can be set to provide only or request only. By default stations can bot
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 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. 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 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.
### Fuel loader mode ### Refueler 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. When placed adjacent to a vanilla train stop, a Cybersyn refueler 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 refueler before returning to the depot. The train will search for a refueler 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. Refuelers 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 control mode ### Station control mode
@@ -107,11 +109,11 @@ 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) ![Image](https://raw.githubusercontent.com/mamoniot/project-cybersyn/main/previews/gui-network.png)
Stations, depots and fuel loaders can be set to belong to a particular network by setting that network on their 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. Stations, depots and refuelers can be set to belong to a particular network by setting that network on their 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. 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.
Stations and fuel loader combinators allow their network to be set to the "each" virtual signal. When in this mode, each virtual signal given to them as input is interpretted as a network mask for that network Id. The station or fuel loader is thus made a part of that network with the specified network mask. This allows you to union together as many different networks as you would like. A network can be set to the "each" virtual signal. When in this mode, each virtual signal given to them as input is interpretted as a network mask for that network Id. The stop is thus made a part of that network with the specified network mask. This allows you to union together as many different networks as you would like.
### Request threshold ### Request threshold
@@ -127,7 +129,7 @@ After an order has been generated, enough items will be subtracted from that ord
### Priority ### Priority
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. Provider stations will be prevented from providing items to lower priority requester stations until the highest priority requester station is satisfied. Orders will be generated first for stations, depots and refuelers which are receiving a higher priority signal than the others. If multiple depots or refuelers have the same priority, the closest one will be prioritized. Depots calculate distance based on the location of their train. If multiple 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. Provider stations will be prevented from providing items to lower priority requester stations until the highest priority requester station is satisfied.
If a combinator set to station control 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. If a combinator set to station control 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.

4
TODO
View File

@@ -6,12 +6,12 @@ major:
add in game guide add in game guide
move to an event based algorithm move to an event based algorithm
models & art models & art
train always returns to same depot setting
make train inactivity condition a setting
minor: minor:
handle if signals are removed from the game during migration handle if signals are removed from the game during migration
update wagon control combinators immediately upon placement
railloader compat railloader compat
deadlocks signals compat
close gui when the combinator is destroyed close gui when the combinator is destroyed
do not play close sound when a different gui is opened do not play close sound when a different gui is opened
gui can desync if settings are changed outside of it while it is open gui can desync if settings are changed outside of it while it is open

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

@@ -48,6 +48,18 @@ function on_failed_delivery(map_data, train_id, train)
unset_wagon_combs(map_data, station) unset_wagon_combs(map_data, station)
end end
end end
if train.has_filtered_wagon then
train.has_filtered_wagon = nil
for carriage_i, carriage in ipairs(train.entity.cargo_wagons) do
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
if inv and inv.is_filtered() then
---@type uint
for i = 1, #inv do
inv.set_filter(i, nil)
end
end
end
end
train.r_station_id = nil train.r_station_id = nil
train.p_station_id = nil train.p_station_id = nil
train.manifest = nil train.manifest = nil
@@ -60,14 +72,21 @@ end
---@param train_id uint ---@param train_id uint
---@param train Train ---@param train Train
function add_available_train(map_data, train_id, train) function add_available_train(map_data, train_id, train)
local network_name = train.network_name if train.network_name then
if network_name then local f, a
if train.network_name == NETWORK_EACH then
f, a = next, train.network_flag
else
f, a = once, train.network_name
end
for network_name in f, a do
local network = map_data.available_trains[network_name] local network = map_data.available_trains[network_name]
if not network then if not network then
network = {} network = {}
map_data.available_trains[network_name] = network map_data.available_trains[network_name] = network
end end
network[train_id] = true network[train_id] = true
end
train.is_available = true train.is_available = true
interface_raise_train_available(train_id) interface_raise_train_available(train_id)
end end
@@ -80,61 +99,42 @@ end
---@param train Train ---@param train Train
function add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot) function add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
local comb = depot.entity_comb local comb = depot.entity_comb
local network_name = get_comb_network_name(comb) set_train_from_comb(mod_settings, train, 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 depot.available_train_id = train_id
train.depot_id = depot_id
train.status = STATUS_D train.status = STATUS_D
train.parked_at_depot_id = depot_id
train.depot_name = depot.entity_stop.backer_name add_available_train(map_data, train_id, train)
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 end
---@param map_data MapData ---@param map_data MapData
---@param train_id uint ---@param train_id uint
---@param train Train ---@param train Train
function remove_available_train(map_data, train_id, train) function remove_available_train(map_data, train_id, train)
---@type uint if train.is_available then
if train.is_available and train.network_name then train.is_available = nil
local network = map_data.available_trains[train.network_name--[[@as string]]] local f, a
if train.network_name == NETWORK_EACH then
f, a = next, train.network_flag
else
f, a = once, train.network_name
end
for network_name in f, a do
local network = map_data.available_trains[network_name]
if network then if network then
network[train_id] = nil network[train_id] = nil
if next(network) == nil then if next(network) == nil then
map_data.available_trains[train.network_name] = nil map_data.available_trains[network_name] = nil
end end
end end
train.is_available = nil end
local depot = map_data.depots[train.depot_id]
if depot.available_train_id == train_id then
depot.available_train_id = nil
return true
end end
end end
return false
end
@@ -142,15 +142,13 @@ end
---@param depot_id uint ---@param depot_id uint
---@param train_entity LuaTrain ---@param train_entity LuaTrain
local function on_train_arrives_depot(map_data, depot_id, train_entity) local function on_train_arrives_depot(map_data, depot_id, train_entity)
local contents = train_entity.get_contents() local is_train_empty = next(train_entity.get_contents()) == nil and next(train_entity.get_fluid_contents()) == nil
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_id = train_entity.id
local train = map_data.trains[train_id] local train = map_data.trains[train_id]
if train then if train then
if train.status == STATUS_TO_D 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 --shouldn't be possible to get train.status == STATUS_D
elseif train.status == STATUS_TO_D_BYPASS or train.status == STATUS_D then
remove_available_train(map_data, train_id, train) remove_available_train(map_data, train_id, train)
elseif mod_settings.react_to_train_early_to_depot then elseif mod_settings.react_to_train_early_to_depot then
if train.manifest then if train.manifest then
@@ -160,21 +158,19 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity)
else else
return return
end end
if is_train_empty then if is_train_empty or mod_settings.allow_cargo_in_depot then
local old_status = train.status local old_status = train.status
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id]) local depot = map_data.depots[depot_id]
set_depot_schedule(train_entity, train.depot_name) add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
set_depot_schedule(train_entity, depot.entity_stop.backer_name)
interface_raise_train_status_changed(train_id, old_status, STATUS_D) interface_raise_train_status_changed(train_id, old_status, STATUS_D)
else else
--train still has cargo --train still has cargo
if mod_settings.react_to_nonempty_train_in_depot then
lock_train_to_depot(train_entity) lock_train_to_depot(train_entity)
remove_train(map_data, train_id, train) remove_train(map_data, train_id, train)
send_alert_nonempty_train_in_depot(map_data, train_entity) send_alert_nonempty_train_in_depot(map_data, train_entity)
end end
interface_raise_train_nonempty_in_depot(depot_id, train_entity, train_id) elseif is_train_empty or mod_settings.allow_cargo_in_depot then
end
elseif is_train_empty then
--NOTE: only place where new Train --NOTE: only place where new Train
train = { train = {
entity = train_entity, entity = train_entity,
@@ -188,22 +184,25 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity)
last_manifest_tick = map_data.total_ticks, last_manifest_tick = map_data.total_ticks,
has_filtered_wagon = nil, has_filtered_wagon = nil,
--is_available = add_available_train_to_depot, --is_available = add_available_train_to_depot,
--parked_at_depot_id = add_available_train_to_depot, --depot_id = add_available_train_to_depot,
--depot_name = add_available_train_to_depot, --use_any_depot = add_available_train_to_depot,
--disable_bypass = add_available_train_to_depot,
--network_name = add_available_train_to_depot, --network_name = add_available_train_to_depot,
--network_flag = add_available_train_to_depot, --network_flag = add_available_train_to_depot,
--priority = add_available_train_to_depot, --priority = add_available_train_to_depot,
} }--[[@as Train]]
set_train_layout(map_data, train) set_train_layout(map_data, train)
map_data.trains[train_id] = train map_data.trains[train_id] = train
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, map_data.depots[depot_id]) local depot = map_data.depots[depot_id]
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
set_depot_schedule(train_entity, train.depot_name) set_depot_schedule(train_entity, depot.entity_stop.backer_name)
interface_raise_train_created(train_id, depot_id) interface_raise_train_created(train_id, depot_id)
else else
if mod_settings.react_to_nonempty_train_in_depot then lock_train_to_depot(train_entity)
send_alert_nonempty_train_in_depot(map_data, train_entity) send_alert_nonempty_train_in_depot(map_data, train_entity)
end end
if not is_train_empty then
interface_raise_train_nonempty_in_depot(depot_id, train_entity) interface_raise_train_nonempty_in_depot(depot_id, train_entity)
end end
end end
@@ -218,7 +217,7 @@ local function on_train_arrives_station(map_data, station_id, train_id, train)
if train.p_station_id == station_id then if train.p_station_id == station_id then
train.status = STATUS_P train.status = STATUS_P
local station = map_data.stations[station_id] local station = map_data.stations[station_id]
set_comb1(map_data, station, train.manifest, 1) set_comb1(map_data, station, train.manifest, mod_settings.invert_sign and 1 or -1)
set_p_wagon_combs(map_data, station, train) set_p_wagon_combs(map_data, station, train)
interface_raise_train_status_changed(train_id, STATUS_TO_P, STATUS_P) interface_raise_train_status_changed(train_id, STATUS_TO_P, STATUS_P)
end end
@@ -226,7 +225,7 @@ local function on_train_arrives_station(map_data, station_id, train_id, train)
if train.r_station_id == station_id then if train.r_station_id == station_id then
train.status = STATUS_R train.status = STATUS_R
local station = map_data.stations[station_id] local station = map_data.stations[station_id]
set_comb1(map_data, station, train.manifest, -1) set_comb1(map_data, station, train.manifest, mod_settings.invert_sign and -1 or 1)
set_r_wagon_combs(map_data, station, train) set_r_wagon_combs(map_data, station, train)
interface_raise_train_status_changed(train_id, STATUS_TO_R, STATUS_R) interface_raise_train_status_changed(train_id, STATUS_TO_R, STATUS_R)
end end
@@ -316,14 +315,21 @@ local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
end end
if fuel_fill > mod_settings.fuel_threshold then if fuel_fill > mod_settings.fuel_threshold then
--if fuel_fill == INF, it's probably a modded electric train --if fuel_fill == INF, it's probably a modded electric train
if mod_settings.depot_bypass_enabled then if not train.disable_bypass then
train.status = STATUS_TO_D_BYPASS train.status = STATUS_TO_D_BYPASS
add_available_train(map_data, train_id, train) add_available_train(map_data, train_id, train)
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D_BYPASS) interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D_BYPASS)
return return
end end
else else
local refuelers = map_data.to_refuelers[train.network_name] local f, a
if train.network_name == NETWORK_EACH then
f, a = next, train.network_flag
else
f, a = once, train.network_name
end
for network_name in f, a do
local refuelers = map_data.to_refuelers[network_name]
if refuelers then if refuelers then
local best_refueler_id = nil local best_refueler_id = nil
local best_dist = INF local best_dist = INF
@@ -332,8 +338,9 @@ local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
local refueler = map_data.refuelers[id] local refueler = map_data.refuelers[id]
set_refueler_from_comb(map_data, mod_settings, id) set_refueler_from_comb(map_data, mod_settings, id)
local refueler_network_flag = refueler.network_name == NETWORK_EACH and (refueler.network_flag[train.network_name] or 0) or refueler.network_flag local refueler_network_flag = get_network_flag(refueler, network_name)
if btest(train.network_flag, refueler_network_flag) and (refueler.allows_all_trains or refueler.accepted_layouts[train.layout_id]) and refueler.trains_total < refueler.entity_stop.trains_limit then local train_network_flag = get_network_flag(train, network_name)
if btest(train_network_flag, refueler_network_flag) and (refueler.allows_all_trains or refueler.accepted_layouts[train.layout_id]) and refueler.trains_total < refueler.entity_stop.trains_limit then
local accepted = false local accepted = false
local dist = nil local dist = nil
if refueler.priority == best_prior then if refueler.priority == best_prior then
@@ -358,6 +365,7 @@ local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
end end
end end
end end
end
--the train has not qualified for depot bypass nor refueling --the train has not qualified for depot bypass nor refueling
train.status = STATUS_TO_D train.status = STATUS_TO_D
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D) interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D)
@@ -367,7 +375,7 @@ local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
refueler.trains_total = refueler.trains_total - 1 refueler.trains_total = refueler.trains_total - 1
unset_wagon_combs(map_data, refueler) unset_wagon_combs(map_data, refueler)
set_combinator_output(map_data, refueler.entity_comb, nil) set_combinator_output(map_data, refueler.entity_comb, nil)
if mod_settings.depot_bypass_enabled then if not train.disable_bypass then
train.status = STATUS_TO_D_BYPASS train.status = STATUS_TO_D_BYPASS
add_available_train(map_data, train_id, train) add_available_train(map_data, train_id, train)
else else

View File

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

1
cybersyn_blueprints.txt Normal file
View File

File diff suppressed because one or more lines are too long