added stack thresholds

This commit is contained in:
mamoniot
2022-12-23 10:11:04 -05:00
parent be849acb43
commit 2a5b4543d4
8 changed files with 175 additions and 111 deletions

6
TODO
View File

@@ -1,18 +1,18 @@
bugs:
fix crash in sumbitted massively modded world
fix crash in submitted massively modded world
check for bugs in new player's submitted world
find se bug https://mods.factorio.com/mod/cybersyn/discussion/639d1ea26f00ecfa7b048da0
https://mods.factorio.com/mod/cybersyn/discussion/63a52e54be4341bccc0446f8
major:
debug output
add in game guide
move to an event based algorithm
models & art
add stack threshold setting
minor:
railloader compat
request stack threshold
refuelers set to everything should check for new signals
close gui when the combinator is destroyed
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

View File

@@ -10,6 +10,7 @@ Date: 2022-12-22
- Nonempty trains in depot are no longer put in manual mode, instead they are forced to park at the depot
- Made several alerts persistent
- Allowed station and fuel combinators to be set to network id "everything", for each virtual signal they recieve as input, the stop is added to that network and its signal strength is used as the network mask
- Added the ability to specify per-station whether request thresholds represent total items or total stacks
---------------------------------------------------------------------------------------------------
Version: 1.1.7
Date: 2022-12-17

View File

@@ -66,8 +66,10 @@ comb2=Station info
wagon-manifest=Wagon info
network=Network
network-tooltip=A signal is used to identify which network this combinator is a member of. Trains will only be dispatched from depots to provider and requester stations if they are all identified with the same signal.
auto-tooltip=When checked trains in the network are automatically added to the allow-list if every wagon of the train is able to be loaded or unloaded by this station. When unchecked the allow-list is not used and all trains are allowed to park here.
auto-description=Automatic train allow-list
allow-list-description=Automatic train allow-list
allow-list-tooltip=When checked trains in the network are automatically added to the allow-list if every wagon of the train is able to be loaded or unloaded by this station. When unchecked the allow-list is not used and all trains are allowed to park here.
is-stack-description=Enable 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. Fluid thresholds are unaffected.
switch-provide=Provide only
switch-request=Request only
switch-provide-tooltip=Lock this station to only provide items to the network. By default it both requests and provides.

View File

@@ -116,9 +116,15 @@ function create_manifest(map_data, r_station_id, p_station_id, train_id, primary
local r_effective_item_count = r_item_count + (r_station.deliveries[item_name] or 0)
if r_effective_item_count < 0 and r_item_count < 0 then
local r_threshold = r_station.item_thresholds and r_station.item_thresholds[item_name] or r_station.r_threshold
if r_station.is_stack and item_type == "item" then
r_threshold = r_threshold*get_stack_size(map_data, item_name)
end
local p_effective_item_count = p_station.item_p_counts[item_name]
--could be an item that is not present at the station
local override_threshold = p_station.item_thresholds and p_station.item_thresholds[item_name]
if override_threshold and p_station.is_stack and item_type == "item" then
override_threshold = override_threshold*get_stack_size(map_data, item_name)
end
if p_effective_item_count and p_effective_item_count >= (override_threshold or r_threshold) then
local item = {name = item_name, type = item_type, count = min(-r_effective_item_count, p_effective_item_count)}
if item_name == primary_item_name then
@@ -317,7 +323,13 @@ local function tick_dispatch(map_data, mod_settings)
----------------------------------------------------------------
-- check for valid train
----------------------------------------------------------------
local slot_threshold = is_fluid and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name))
local slot_threshold
if r_station.is_stack and item_type == "item" then
slot_threshold = r_threshold
r_threshold = r_threshold*get_stack_size(map_data, item_name)
else
slot_threshold = is_fluid and r_threshold or ceil(r_threshold/get_stack_size(map_data, item_name))
end
---@type uint?
local best_p_train_id = nil
@@ -385,6 +397,9 @@ local function tick_dispatch(map_data, mod_settings)
local effective_count = p_station.item_p_counts[item_name]
local override_threshold = p_station.item_thresholds and p_station.item_thresholds[item_name]
if override_threshold and p_station.is_stack and item_type == "item" then
override_threshold = override_threshold*get_stack_size(map_data, item_name)
end
if effective_count < (override_threshold or r_threshold) then
--this p station should have serviced the current r station, lock it so it can't serve any others
--this will lock stations even when the r station manages to find a p station, this not a problem because all stations will be unlocked before it could be an issue
@@ -531,12 +546,16 @@ local function tick_poll_station(map_data, mod_settings)
for k, v in pairs(comb1_signals) do
---@type string
local item_name = v.signal.name
local item_type = v.signal.type
local item_count = v.count
local effective_item_count = item_count + (station.deliveries[item_name] or 0)
local is_not_requesting = true
if station.is_r then
local r_threshold = station.item_thresholds and station.item_thresholds[item_name] or station.r_threshold
if station.is_stack and item_type == "item" then
r_threshold = r_threshold*get_stack_size(map_data, item_name)
end
if -effective_item_count >= r_threshold and -item_count >= r_threshold then
is_not_requesting = false
is_requesting_nothing = false
@@ -645,7 +664,6 @@ function tick(map_data, mod_settings)
interface_raise_tick_init()
tick_poll_train(map_data, mod_settings)
tick_poll_comb(map_data)
return
elseif map_data.tick_state == STATE_POLL_STATIONS then
for i = 1, mod_settings.update_rate do
if tick_poll_station(map_data, mod_settings) then break end

View File

@@ -296,55 +296,12 @@ function get_comb_params(comb)
return comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]]
end
---@param comb LuaEntity
function get_comb_gui_settings(comb)
local params = get_comb_params(comb)
local op = params.operation
local selected_index = 0
local switch_state = "none"
local bits = params.second_constant or 0
local allows_all_trains = bits%2 == 1
local is_pr_state = floor(bits/2)%3
if is_pr_state == 0 then
switch_state = "none"
elseif is_pr_state == 1 then
switch_state = "left"
elseif is_pr_state == 2 then
switch_state = "right"
end
if op == MODE_PRIMARY_IO or op == MODE_PRIMARY_IO_ACTIVE or op == MODE_PRIMARY_IO_FAILED_REQUEST then
selected_index = 1
elseif op == MODE_DEPOT then
selected_index = 2
elseif op == MODE_REFUELER then
selected_index = 3
elseif op == MODE_SECONDARY_IO then
selected_index = 4
elseif op == MODE_WAGON_MANIFEST then
selected_index = 5
end
return selected_index, params.first_signal, not allows_all_trains, switch_state
end
---@param comb LuaEntity
function get_comb_network_name(comb)
local params = get_comb_params(comb)
local signal = params.first_signal
return signal and signal.name or nil
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 bits = params.second_constant or 0
local is_pr_state = floor(bits/2)%3
local signal = params.first_signal
station.network_name = signal and signal.name or nil
station.allows_all_trains = bits%2 == 1
station.is_p = is_pr_state == 0 or is_pr_state == 1
station.is_r = is_pr_state == 0 or is_pr_state == 2
end
---@param map_data MapData
---@param mod_settings CybersynModSettings
---@param id uint
@@ -372,7 +329,7 @@ function set_refueler_from_comb(map_data, mod_settings, id)
end
refueler.network_name = signal and signal.name or nil
refueler.allows_all_trains = bits%2 == 1
refueler.allows_all_trains = (bits%2 == 1) or nil
local signals = refueler.entity_comb.get_merged_signals(DEFINES_COMBINATOR_INPUT)
refueler.priority = 0
@@ -435,14 +392,56 @@ function update_display(map_data, station)
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 = bit32.extract(bits, 0, 2)
local allows_all_trains = bit32.extract(bits, 2) > 0
local is_stack = bit32.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 allows_all_trains boolean
function set_comb_allows_all_trains(comb, allows_all_trains)
local control = get_comb_control(comb)
local param = control.parameters
local bits = param.second_constant or 0
param.second_constant = (bits - bits%2) + (allows_all_trains and 1 or 0)
control.parameters = param
function get_comb_gui_settings(comb)
local params = get_comb_params(comb)
local op = params.operation
local selected_index = 0
local switch_state = "none"
local bits = params.second_constant or 0
local is_pr_state = bit32.extract(bits, 0, 2)
local allows_all_trains = bit32.extract(bits, 2) > 0
local is_stack = bit32.extract(bits, 3) > 0
if is_pr_state == 0 then
switch_state = "none"
elseif is_pr_state == 1 then
switch_state = "left"
elseif is_pr_state == 2 then
switch_state = "right"
end
if op == MODE_PRIMARY_IO or op == MODE_PRIMARY_IO_ACTIVE or op == MODE_PRIMARY_IO_FAILED_REQUEST then
selected_index = 1
elseif op == MODE_DEPOT then
selected_index = 2
elseif op == MODE_REFUELER then
selected_index = 3
elseif op == MODE_SECONDARY_IO then
selected_index = 4
elseif op == MODE_WAGON_MANIFEST then
selected_index = 5
end
return selected_index, params.first_signal, switch_state, not allows_all_trains, is_stack
end
---@param comb LuaEntity
---@param is_pr_state 0|1|2
@@ -450,7 +449,28 @@ function set_comb_is_pr_state(comb, is_pr_state)
local control = get_comb_control(comb)
local param = control.parameters
local bits = param.second_constant or 0
param.second_constant = (bits%2) + (2*is_pr_state)
param.second_constant = bit32.replace(bits, is_pr_state, 0, 2)
control.parameters = param
end
---@param comb LuaEntity
---@param allows_all_trains boolean
function set_comb_allows_all_trains(comb, allows_all_trains)
local control = get_comb_control(comb)
local param = control.parameters
local bits = param.second_constant or 0
param.second_constant = bit32.replace(bits, allows_all_trains and 1 or 0, 2)
control.parameters = param
end
---@param comb LuaEntity
---@param is_stack boolean
function set_comb_is_stack(comb, is_stack)
local control = get_comb_control(comb)
local param = control.parameters
local bits = param.second_constant or 0
param.second_constant = bit32.replace(bits, is_stack and 1 or 0, 3)
control.parameters = param
end
---@param comb LuaEntity

View File

@@ -26,9 +26,10 @@
---@field public entity_stop LuaEntity
---@field public entity_comb1 LuaEntity
---@field public entity_comb2 LuaEntity?
---@field public is_p boolean
---@field public is_r boolean
---@field public allows_all_trains boolean
---@field public is_p true?
---@field public is_r true?
---@field public is_stack true?
---@field public allows_all_trains true?
---@field public deliveries_total int
---@field public last_delivery_tick int
---@field public priority int --transient
@@ -58,10 +59,10 @@
---@field public accepted_layouts {[uint]: true?}
---@field public layout_pattern (0|1|2|3)[]?
---@field public wagon_combs {[int]: LuaEntity}?--NOTE: allowed to be invalid entities or combinators with the wrong operation, these must be checked and lazy deleted when found
---@field public allows_all_trains boolean
---@field public allows_all_trains true?
---@field public priority int
---@field public network_name string?
---@field public network_flag int
---@field public network_flag int|{[string]: int}
---@class Train
---@field public entity LuaTrain --should only be invalid if se_is_being_teleported is true

View File

@@ -26,13 +26,34 @@ STATUS_NAMES[defines.entity_status.disabled_by_script] = "entity-status.disabled
STATUS_NAMES[defines.entity_status.marked_for_deconstruction] = "entity-status.marked-for-deconstruction"
STATUS_NAMES_DEFAULT = "entity-status.disabled"
---@param main_window LuaGuiElement
---@param selected_index int
local function set_visibility(main_window, selected_index)
local uses_network = selected_index == 1 or selected_index == 2 or selected_index == 3
local uses_allow_list = selected_index == 1 or selected_index == 3
local is_station = selected_index == 1
local vflow = main_window.frame.vflow--[[@as LuaGuiElement]]
local top_flow = vflow.top--[[@as LuaGuiElement]]
local bottom_flow = vflow.bottom--[[@as LuaGuiElement]]
local right_flow = bottom_flow.right--[[@as LuaGuiElement]]
top_flow.is_pr_switch.visible = is_station
vflow.network_label.visible = uses_network
bottom_flow.network.visible = uses_network
right_flow.allow_list.visible = uses_allow_list
right_flow.allow_list_label.visible = uses_allow_list
right_flow.is_stack.visible = is_station
right_flow.is_stack_label.visible = is_station
end
---@param comb LuaEntity
---@param player LuaPlayer
function gui_opened(comb, player)
combinator_update(global, comb, true)
local rootgui = player.gui.screen
local selected_index, signal, check, switch_state = get_comb_gui_settings(comb)
local selected_index, signal, switch_state, allow_list, is_stack = get_comb_gui_settings(comb)
local window = flib_gui.build(rootgui, {
{type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={
@@ -44,8 +65,8 @@ function gui_opened(comb, player)
on_click = {"close", comb.unit_number}
}}
}},
{type="frame", style="inside_shallow_frame_with_padding", style_mods={padding=12}, children={
{type="flow", direction="vertical", style_mods={horizontal_align="left"}, children={
{type="frame", name="frame", style="inside_shallow_frame_with_padding", style_mods={padding=12}, children={
{type="flow", name="vflow", direction="vertical", style_mods={horizontal_align="left"}, children={
--status
{type="flow", style="status_flow", direction="horizontal", style_mods={vertical_align="center", horizontally_stretchable=true, bottom_padding=4}, children={
{type="sprite", sprite=STATUS_SPRITES[comb.status] or STATUS_SPRITES_DEFAULT, style="status_image", ref={"status_icon"}, style_mods={stretch_image_to_widget_size=true}},
@@ -67,8 +88,8 @@ function gui_opened(comb, player)
{"cybersyn-gui.comb2"},
{"cybersyn-gui.wagon-manifest"},
}},
{type="switch", name="switch", ref={"switch"}, allow_none_state=true, switch_state=switch_state, left_label_caption={"cybersyn-gui.switch-provide"}, right_label_caption={"cybersyn-gui.switch-request"}, left_label_tooltip={"cybersyn-gui.switch-provide-tooltip"}, right_label_tooltip={"cybersyn-gui.switch-request-tooltip"}, actions={
on_switch_state_changed={"switch", comb.unit_number}
{type="switch", name="is_pr_switch", ref={"is_pr_switch"}, allow_none_state=true, switch_state=switch_state, left_label_caption={"cybersyn-gui.switch-provide"}, right_label_caption={"cybersyn-gui.switch-request"}, left_label_tooltip={"cybersyn-gui.switch-provide-tooltip"}, right_label_tooltip={"cybersyn-gui.switch-request-tooltip"}, actions={
on_switch_state_changed={"is_pr_switch", comb.unit_number}
}}
}},
---choose-elem-button
@@ -78,10 +99,16 @@ function gui_opened(comb, player)
{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}, actions={
on_elem_changed={"choose-elem-button", comb.unit_number}
}},
{type="checkbox", name="radio_button", ref={"radio_button"}, state=check, tooltip={"cybersyn-gui.auto-tooltip"}, actions={
on_checked_state_changed={"radio_button", comb.unit_number}
}},
{type="label", name="radio_label", style_mods={left_padding=3}, ref={"radio_label"}, caption={"cybersyn-gui.auto-description"}},
{type="flow", name="right", direction="vertical", style_mods={horizontal_align="left"}, children={
{type="checkbox", name="allow_list", ref={"allow_list"}, state=allow_list, tooltip={"cybersyn-gui.allow-list-tooltip"}, actions={
on_checked_state_changed={"allow_list", comb.unit_number}
}},
{type="label", name="allow_list_label", style_mods={left_padding=3}, ref={"allow_list_label"}, caption={"cybersyn-gui.allow-list-description"}},
{type="checkbox", name="is_stack", ref={"is_stack"}, state=is_stack, tooltip={"cybersyn-gui.is-stack-tooltip"}, actions={
on_checked_state_changed={"is_stack", comb.unit_number}
}},
{type="label", name="is_stack_label", style_mods={left_padding=3}, ref={"is_stack_label"}, caption={"cybersyn-gui.is-stack-description"}},
}}
}}
}}
}}
@@ -91,14 +118,8 @@ function gui_opened(comb, player)
window.preview.entity = comb
window.titlebar.drag_target = window.main_window
window.main_window.force_auto_center()
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
window.network.visible = uses_network
window.network_label.visible = uses_network
window.radio_button.visible = uses_allow_list
window.radio_label.visible = uses_allow_list
window.switch.visible = selected_index == 1
set_visibility(window.main_window, selected_index)
player.opened = window.main_window
end
@@ -142,45 +163,18 @@ function register_gui_actions()
local comb = global.to_comb[msg[2]]
if not comb or not comb.valid then return end
local top_flow = element.parent
local all_flow = top_flow.parent
local bottom_flow = all_flow.bottom
local param
set_visibility(rootgui[COMBINATOR_NAME], element.selected_index)
if element.selected_index == 1 then
set_comb_operation(comb, MODE_PRIMARY_IO)
top_flow["switch"].visible = true
all_flow["network_label"].visible = true
bottom_flow["network"].visible = true
bottom_flow["radio_button"].visible = true
bottom_flow["radio_label"].visible = true
elseif element.selected_index == 2 then
set_comb_operation(comb, MODE_DEPOT)
top_flow["switch"].visible = false
all_flow["network_label"].visible = true
bottom_flow["network"].visible = true
bottom_flow["radio_button"].visible = false
bottom_flow["radio_label"].visible = false
elseif element.selected_index == 3 then
set_comb_operation(comb, MODE_REFUELER)
top_flow["switch"].visible = false
all_flow["network_label"].visible = true
bottom_flow["network"].visible = true
bottom_flow["radio_button"].visible = true
bottom_flow["radio_label"].visible = true
elseif element.selected_index == 4 then
set_comb_operation(comb, MODE_SECONDARY_IO)
top_flow["switch"].visible = false
all_flow["network_label"].visible = false
bottom_flow["network"].visible = false
bottom_flow["radio_button"].visible = false
bottom_flow["radio_label"].visible = false
elseif element.selected_index == 5 then
set_comb_operation(comb, MODE_WAGON_MANIFEST)
top_flow["switch"].visible = false
all_flow["network_label"].visible = false
bottom_flow["network"].visible = false
bottom_flow["radio_button"].visible = false
bottom_flow["radio_label"].visible = false
else
return
end
@@ -207,7 +201,7 @@ function register_gui_actions()
set_comb_network_name(comb, signal)
combinator_update(global, comb)
elseif msg[1] == "radio_button" then
elseif msg[1] == "allow_list" then
local element = event.element
if not element then return end
local comb = global.to_comb[msg[2]]
@@ -217,7 +211,17 @@ function register_gui_actions()
set_comb_allows_all_trains(comb, allows_all_trains)
combinator_update(global, comb)
elseif msg[1] == "switch" then
elseif msg[1] == "is_stack" then
local element = event.element
if not element then return end
local comb = global.to_comb[msg[2]]
if not comb or not comb.valid then return end
local is_stack = element.state
set_comb_is_stack(comb, is_stack)
combinator_update(global, comb)
elseif msg[1] == "is_pr_switch" then
local element = event.element
if not element then return end
local comb = global.to_comb[msg[2]]

View File

@@ -114,6 +114,24 @@ local migrations_table = {
end
end
end,
["1.1.8"] = function()
---@type MapData
local map_data = global
map_data.tick_state = STATE_INIT
map_data.tick_data = {}
for k, comb in pairs(map_data.to_comb) do
local control = get_comb_control(comb)
local params = control.parameters
local bits = params.second_constant or 0
local allows_all_trains = bits%2
local is_pr_state = math.floor(bits/2)%3
local new_bits = bit32.bor(is_pr_state, allows_all_trains*4)
params.second_constant = new_bits
control.parameters = params
end
end,
}
--STATUS_R_TO_D = 5