diff --git a/TODO b/TODO index 6bdfff0..9dfd64e 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,13 @@ -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 -do hardcore testing -models & art -space elevator compat -railloader compat -major bug with copy-paste when the operation is changed by blueprint but it gets copied to the old settings before it's checked for update -go over init and make it agree with type info +bugs: + + +major: + do hardcore testing + models & art + railloader compat + +minor: + 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 + go over init and make it agree with type info diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index a61230a..4491ea7 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -52,3 +52,10 @@ Version: 0.5.0 Date: 2022-11-25 Features: - Added SE space elevator compat +--------------------------------------------------------------------------------------------------- +Version: 0.5.1 +Date: 2022-11-25 + Features: + - Re-added combinator copy-paste + - Fixed bugs within train blacklist logic + - Fixed a crash diff --git a/cybersyn/info.json b/cybersyn/info.json index 6c19641..5ffb433 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.5.0", + "version": "0.5.1", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index c5fd57c..172a2df 100644 --- a/cybersyn/prototypes/entity.lua +++ b/cybersyn/prototypes/entity.lua @@ -12,7 +12,7 @@ combinator_entity.radius_visualisation_specification = { distance = 1.5, } combinator_entity.active_energy_usage = "10KW" -combinator_entity.allow_copy_paste = false +--combinator_entity.allow_copy_paste = false diff --git a/cybersyn/scripts/central-planning.lua b/cybersyn/scripts/central-planning.lua index b61aeac..9f915ee 100644 --- a/cybersyn/scripts/central-planning.lua +++ b/cybersyn/scripts/central-planning.lua @@ -26,7 +26,7 @@ function remove_manifest(map_data, station, manifest, sign) set_comb2(map_data, station) station.deliveries_total = station.deliveries_total - 1 if station.deliveries_total == 0 and station.entity_comb1.valid then - map_data.to_comb_params[station.entity_comb1.unit_number] = set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) + set_comb_operation_with_check(map_data, station.entity_comb1, OPERATION_PRIMARY_IO) end end @@ -214,10 +214,10 @@ local function send_train_between(map_data, r_station_id, p_station_id, depot, p set_comb2(map_data, p_station) set_comb2(map_data, r_station) if p_station.entity_comb1.valid then - map_data.to_comb_params[p_station.entity_comb1.unit_number] = set_combinator_operation(p_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + set_comb_operation_with_check(map_data, p_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) end if r_station.entity_comb1.valid then - map_data.to_comb_params[r_station.entity_comb1.unit_number] = set_combinator_operation(r_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) + set_comb_operation_with_check(map_data, r_station.entity_comb1, OPERATION_PRIMARY_IO_ACTIVE) end end @@ -229,11 +229,22 @@ local function tick_poll_train(map_data, mod_settings) local train_id, train = next(map_data.trains, tick_data.last_train) tick_data.last_train = train_id - if train and train.manifest and train.last_manifest_tick + mod_settings.stuck_train_time*mod_settings.tps < map_data.total_ticks then + if train and train.manifest and train.entity and train.last_manifest_tick + mod_settings.stuck_train_time*mod_settings.tps < map_data.total_ticks then send_stuck_train_alert(train.entity, train.depot_name) end end ---@param map_data MapData +local function tick_poll_comb(map_data) + local tick_data = map_data.tick_data + --NOTE: the following has undefined behavior if last_comb is deleted + local comb_id, comb = next(map_data.to_comb, tick_data.last_comb) + tick_data.last_comb = comb_id + + if comb and comb.valid then + combinator_update(map_data, comb) + end +end +---@param map_data MapData ---@param mod_settings CybersynModSettings local function tick_poll_station(map_data, mod_settings) local tick_data = map_data.tick_data @@ -254,7 +265,7 @@ local function tick_poll_station(map_data, mod_settings) station = map_data.stations[station_id] if station then if station.display_update then - map_data.to_comb_params[station.entity_comb1.unit_number] = update_combinator_display(station.entity_comb1, station.display_failed_request) + update_combinator_display(map_data, station.entity_comb1, station.display_failed_request) station.display_update = station.display_failed_request station.display_failed_request = nil end @@ -373,6 +384,7 @@ local function tick_dispatch(map_data, mod_settings) local name_i = size <= 2 and 2 or 2*random(size/2) local item_network_name = all_names[name_i - 1] local signal = all_names[name_i] + --swap remove all_names[name_i - 1] = all_names[size - 1] all_names[name_i] = all_names[size] @@ -384,7 +396,7 @@ local function tick_dispatch(map_data, mod_settings) if p_stations then tick_data.r_stations = r_stations tick_data.p_stations = p_stations - tick_data.item_name = signal.name + tick_data.item_name = signal.name--[[@as string]] tick_data.item_type = signal.type table_sort(r_stations, function(a_id, b_id) local a = stations[a_id] @@ -417,6 +429,7 @@ local function tick_dispatch(map_data, mod_settings) if r_station and r_station.deliveries_total < r_station.entity_stop.trains_limit then local item_name = tick_data.item_name local item_type = tick_data.item_type + --NOTE: the station at r_station_id could have been deleted and reregistered since last poll, this check here prevents it from being processed for a delivery in that case local r_threshold = r_station.p_count_or_r_threshold_per_item[item_name] if r_threshold then @@ -483,6 +496,7 @@ function tick(map_data, mod_settings) end end tick_poll_train(map_data, mod_settings) + tick_poll_comb(map_data) end if map_data.tick_state == STATE_POLL_STATIONS then diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index ca20e59..19d8897 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -14,10 +14,31 @@ end ---@param stop0 LuaEntity ---@param stop1 LuaEntity function get_stop_dist(stop0, stop1) - return get_distance(stop0.position, stop1.position) + local surface0 = stop0.surface.index + local surface1 = stop1.surface.index + return (surface0 == surface1 and get_distance(stop0.position, stop1.position) or DIFFERENT_SURFACE_DISTANCE) end +---@param surface LuaSurface +local function se_get_space_elevator_name(surface) + --TODO: check how expensive the following is and potentially cache it's results + local entity = surface.find_entities_filtered({ + name = SE_ELEVATOR_STOP_PROTO_NAME, + type = "train-stop", + limit = 1, + })[1] + if entity and entity.valid then + return string.sub(entity.backer_name, 1, string.len(entity.backer_name) - SE_ELEVATOR_SUFFIX_LENGTH) + end +end + + +------------------------------------------------------------------------------ +--[[train schedules]]-- +------------------------------------------------------------------------------ + + local create_loading_order_condition = {type = "inactivity", compare_type = "and", ticks = 120} ---@param stop LuaEntity ---@param manifest Manifest @@ -69,22 +90,7 @@ end function lock_train(train) train.manual_mode = true end ---[[ ----@param train LuaTrain ----@param depot_stop LuaEntity ----@param p_stop LuaEntity ----@param r_stop LuaEntity ----@param manifest Manifest -function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) - train.schedule = {current = 1, records = { - create_inactivity_order(depot_stop.backer_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), - }} -end -]] + ---@param train LuaTrain ---@param stop LuaEntity ---@param old_name string @@ -99,64 +105,230 @@ function rename_manifest_schedule(train, stop, old_name) end train.schedule = schedule end + +---@param train LuaTrain +---@param depot_stop LuaEntity +---@param p_stop LuaEntity +---@param r_stop LuaEntity +---@param manifest Manifest +function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) + --NOTE: train must be on same surface as depot_stop + local d_surface = depot_stop.surface + local p_surface = p_stop.surface + local r_surface = r_stop.surface + local d_surface_i = d_surface.index + local p_surface_i = p_surface.index + local r_surface_i = r_surface.index + if d_surface_i == p_surface_i and p_surface_i == r_surface_i then + train.schedule = {current = 1, records = { + create_inactivity_order(depot_stop.backer_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), + }} + return + elseif IS_SE_PRESENT and (d_surface_i == p_surface_i or p_surface_i == r_surface_i or r_surface_i == d_surface_i) then + local d_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = d_surface_i}) + local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = (d_surface_i == p_surface_i) and r_surface_i or p_surface_i}) + local is_train_in_orbit = other_zone.orbit_index == d_zone.index + if is_train_in_orbit or d_zone.orbit_index == other_zone.index then + local elevator_name = se_get_space_elevator_name(d_surface) + if elevator_name then + local records = {create_inactivity_order(depot_stop.backer_name)} + if d_surface_i == p_surface_i then + records[#records + 1] = create_direct_to_station_order(p_stop) + else + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + records[#records + 1] = create_loading_order(p_stop, manifest) + if p_surface_i ~= r_surface_i then + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + records[#records + 1] = create_unloading_order(r_stop) + if r_surface_i ~= d_surface_i then + records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} + is_train_in_orbit = not is_train_in_orbit + end + + train.schedule = {current = 1, records = records} + return + end + 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 + train.schedule = {current = 1, records = { + create_inactivity_order(depot_stop.backer_name), + create_loading_order(p_stop, manifest), + create_unloading_order(r_stop), + }} + lock_train(train) + send_lost_train_alert(train, depot_stop.backer_name) +end + + +------------------------------------------------------------------------------ +--[[combinators]]-- +------------------------------------------------------------------------------ + + +---@param comb LuaEntity +function get_comb_control(comb) + --NOTE: using this as opposed to get_comb_params gives you R/W access + return comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] +end +---@param comb LuaEntity function get_comb_params(comb) return comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] end ----@param param ArithmeticCombinatorParameters -function get_comb_secondary_state(param) - local bits = param.second_constant or 0 - return bits%2 == 1, floor(bits/2)%3 +---@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 == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then + selected_index = 1 + elseif op == OPERATION_SECONDARY_IO then + selected_index = 2 + elseif op == OPERATION_DEPOT then + selected_index = 3 + elseif op == OPERATION_WAGON_MANIFEST then + selected_index = 4 + 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 param = get_comb_params(station.entity_comb1) - local bits = param.second_constant or 0 + 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 = param.first_signal + 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 unit_number uint +---@param params ArithmeticCombinatorParameters +function has_comb_params_changed(map_data, unit_number, params) + local old_params = map_data.to_comb_params[unit_number] + + if params.operation ~= old_params.operation then + if (old_params.operation == OPERATION_PRIMARY_IO) and (params.operation == OPERATION_PRIMARY_IO_ACTIVE or params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST) then + else + return true + end + end + local new_signal = params.first_signal + local old_signal = old_params.first_signal + local new_network = new_signal and new_signal.name or nil + local old_network = old_signal and old_signal.name or nil + if new_network ~= old_network then + return true + end + if params.second_constant ~= old_params.second_constant then + return true + end + return false +end +---@param map_data MapData +---@param comb LuaEntity +---@param op string +function set_comb_operation_with_check(map_data, comb, op) + ---@type uint + local unit_number = comb.unit_number + local control = get_comb_control(comb) + local params = control.parameters + if not has_comb_params_changed(map_data, unit_number, params) then + params.operation = op + control.parameters = params + if (op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST) then + params.operation = OPERATION_PRIMARY_IO + end + map_data.to_comb_params[unit_number] = params + end +end +---@param map_data MapData +---@param comb LuaEntity +---@param is_failed boolean +function update_combinator_display(map_data, comb, is_failed) + ---@type uint + local unit_number = comb.unit_number + local control = get_comb_control(comb) + local params = control.parameters + if not has_comb_params_changed(map_data, unit_number, params) then + if is_failed then + if params.operation == OPERATION_PRIMARY_IO then + params.operation = OPERATION_PRIMARY_IO_FAILED_REQUEST + control.parameters = params + params.operation = OPERATION_PRIMARY_IO + map_data.to_comb_params[unit_number] = params + end + elseif params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST then + params.operation = OPERATION_PRIMARY_IO + control.parameters = params + map_data.to_comb_params[unit_number] = params + end + end +end ---@param comb LuaEntity ---@param allows_all_trains boolean function set_comb_allows_all_trains(comb, allows_all_trains) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + 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 - return param end ---@param comb LuaEntity ---@param is_pr_state 0|1|2 function set_comb_is_pr_state(comb, is_pr_state) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + 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) control.parameters = param - return param end - ----@param comb LuaEntity -function get_comb_network_name(comb) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local signal = control.parameters.first_signal - - return signal and signal.name -end - ---@param comb LuaEntity ---@param signal SignalID? function set_comb_network_name(comb, signal) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local control = get_comb_control(comb) local param = control.parameters param.first_signal = signal control.parameters = param - return param +end +---@param comb LuaEntity +---@param op string +function set_comb_operation(comb, op) + local control = get_comb_control(comb) + local params = control.parameters + params.operation = op + control.parameters = params end @@ -169,32 +341,6 @@ function set_combinator_output(map_data, comb, signals) out.get_or_create_control_behavior().parameters = signals end end ----@param comb LuaEntity ----@param op string -function set_combinator_operation(comb, op) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local param = control.parameters - param.operation = op - control.parameters = param - return param -end ----@param comb LuaEntity ----@param is_failed boolean -function update_combinator_display(comb, is_failed) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local param = control.parameters - if is_failed then - if param.operation == OPERATION_PRIMARY_IO then - param.operation = OPERATION_PRIMARY_IO_FAILED_REQUEST - control.parameters = param - end - elseif param.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST then - param.operation = OPERATION_PRIMARY_IO - control.parameters = param - end - return param -end - ---@param station Station function get_signals(station) @@ -236,6 +382,12 @@ function get_threshold(map_data, station, signal) return station.r_threshold end + +------------------------------------------------------------------------------ +--[[alerts]]-- +------------------------------------------------------------------------------ + + local send_missing_train_alert_for_stop_icon = {name = MISSING_TRAIN_NAME, type = "fluid"} ---@param r_stop LuaEntity ---@param p_stop LuaEntity @@ -312,89 +464,3 @@ function send_stuck_train_alert(train, depot_name) end end end - ---function se_create_placeholder_order() ---end - ----@param surface LuaSurface -local function se_get_space_elevator_name(surface) - --TODO: check how expensive the following is and potentially cache it's results - local entity = surface.find_entities_filtered({ - name = SE_ELEVATOR_STOP_PROTO_NAME, - type = "train-stop", - limit = 1, - })[1] - if entity and entity.valid then - return string.sub(entity.backer_name, 1, string.len(entity.backer_name) - SE_ELEVATOR_SUFFIX_LENGTH) - end -end - ----@param train LuaTrain ----@param depot_stop LuaEntity ----@param p_stop LuaEntity ----@param r_stop LuaEntity ----@param manifest Manifest -function set_manifest_schedule(train, depot_stop, p_stop, r_stop, manifest) - --NOTE: train must be on same surface as depot_stop - local d_surface = depot_stop.surface - local p_surface = p_stop.surface - local r_surface = r_stop.surface - local d_surface_i = d_surface.index - local p_surface_i = p_surface.index - local r_surface_i = r_surface.index - if d_surface_i == p_surface_i and p_surface_i == r_surface_i then - train.schedule = {current = 1, records = { - create_inactivity_order(depot_stop.backer_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), - }} - return - elseif IS_SE_PRESENT and (d_surface_i == p_surface_i or p_surface_i == r_surface_i or r_surface_i == d_surface_i) then - local d_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = d_surface_i}) - local other_zone = remote.call("space-exploration", "get_zone_from_surface_index", {surface_index = (d_surface_i == p_surface_i) and r_surface_i or p_surface_i}) - local is_train_in_orbit = other_zone.orbit_index == d_zone.index - if is_train_in_orbit or d_zone.orbit_index == other_zone.index then - local elevator_name = se_get_space_elevator_name(d_surface) - if elevator_name then - local records = {create_inactivity_order(depot_stop.backer_name)} - if d_surface_i == p_surface_i then - records[#records + 1] = create_direct_to_station_order(p_stop) - else - records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} - is_train_in_orbit = not is_train_in_orbit - end - records[#records + 1] = create_loading_order(p_stop, manifest) - if p_surface_i ~= r_surface_i then - records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} - is_train_in_orbit = not is_train_in_orbit - end - records[#records + 1] = create_unloading_order(r_stop) - if r_surface_i ~= d_surface_i then - records[#records + 1] = {station = elevator_name..(is_train_in_orbit and SE_ELEVATOR_ORBIT_SUFFIX or SE_ELEVATOR_PLANET_SUFFIX)} - is_train_in_orbit = not is_train_in_orbit - end - - train.schedule = {current = 1, records = records} - return - end - 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 - train.schedule = {current = 1, records = { - create_inactivity_order(depot_stop.backer_name), - create_loading_order(p_stop, manifest), - create_unloading_order(r_stop), - }} - lock_train(train) - send_lost_train_alert(train, depot_stop.backer_name) -end - ----@param stop0 LuaEntity ----@param stop1 LuaEntity -function se_get_stop_dist(stop0, stop1) - local surface0 = stop0.surface.index - local surface1 = stop1.surface.index - return (surface0 == surface1 and get_distance(stop0.position, stop1.position) or DIFFERENT_SURFACE_DISTANCE) -end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 38f57dc..d7fb57a 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -2,7 +2,6 @@ ---@class MapData ---@field public total_ticks uint ---@field public layout_top_id uint ----@field public is_player_cursor_blueprint {[uint]: true|nil} ---@field public to_comb {[uint]: LuaEntity} ---@field public to_comb_params {[uint]: ArithmeticCombinatorParameters} ---@field public to_output {[uint]: LuaEntity} @@ -36,7 +35,7 @@ ---@field public deliveries {[string]: int} ---@field public network_name string? ---@field public network_flag int --transient ----@field public accepted_layouts TrainClass +---@field public accepted_layouts {[uint]: true?} ---@field public layout_pattern {[uint]: int} ---@field public tick_signals {[uint]: Signal}? --transient ---@field public p_count_or_r_threshold_per_item {[string]: int} --transient @@ -61,21 +60,20 @@ ---@field public has_filtered_wagon boolean ---@field public depot_id uint? ---@field public depot_name string ----@field public network_name string +---@field public network_name string? ---@field public network_flag int ---@field public priority int ---@field public se_awaiting_removal any? ---@field public se_awaiting_rename any? ---@alias Manifest {}[] ----@alias TrainClass {[uint]: true} ---@alias cybersyn.global MapData ---@class Economy ---could contain invalid stations ----@field public all_r_stations {[string]: uint[]} --{[network_name:item_name]: count} ----@field public all_p_stations {[string]: uint[]} --{[network_name:item_name]: count} ----@field public all_names {[string]: uint[]} --{[network_name:item_name]: count} +---@field public all_r_stations {[string]: uint[]} --{[network_name:item_name]: station_id} +---@field public all_p_stations {[string]: uint[]} --{[network_name:item_name]: station_id} +---@field public all_names (string|SignalID)[] ---@class CybersynModSettings ---@field public tps int @@ -111,7 +109,6 @@ function init_global() global.layouts = {} global.layout_train_count = {} global.layout_top_id = 1 - global.is_player_cursor_blueprint = {} if IS_SE_PRESENT then global.se_tele_old_id = {} diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 5d490d9..6d661c4 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -30,29 +30,7 @@ STATUS_NAMES_DEFAULT = "entity-status.disabled" ---@param player LuaPlayer function gui_opened(comb, player) local rootgui = player.gui.screen - local param = get_comb_params(comb) - local op = param.operation - - local selected_index = 0 - local switch_state = "none" - local allows_all_trains, is_pr_state = get_comb_secondary_state(param) - 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 == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then - selected_index = 1 - elseif op == OPERATION_SECONDARY_IO then - selected_index = 2 - elseif op == OPERATION_DEPOT then - selected_index = 3 - elseif op == OPERATION_WAGON_MANIFEST then - selected_index = 4 - end + local selected_index, signal, check, switch_state = get_comb_gui_settings(comb) local window = flib_gui.build(rootgui, { {type="frame", direction="vertical", ref={"main_window"}, name=COMBINATOR_NAME, children={ @@ -94,10 +72,10 @@ function gui_opened(comb, player) {type="line", style_mods={top_padding=10}}, {type="label", name="network_label", ref={"network_label"}, style="heading_3_label", caption={"cybersyn-gui.network"}, style_mods={top_padding=8}}, {type="flow", name="bottom", direction="horizontal", style_mods={vertical_align="center"}, children={ - {type="choose-elem-button", name="network", style="slot_button_in_shallow_frame", ref={"network"}, elem_type="signal", tooltip={"cybersyn-gui.network-tooltip"}, signal=param.first_signal, style_mods={bottom_margin=1, right_margin=6}, actions={ + {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=not allows_all_trains, tooltip={"cybersyn-gui.auto-tooltip"}, actions={ + {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"}}, @@ -164,28 +142,28 @@ function register_gui_actions() local bottom_flow = all_flow.bottom local param if element.selected_index == 1 then - param = set_combinator_operation(comb, OPERATION_PRIMARY_IO) + set_comb_operation(comb, OPERATION_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 - param = set_combinator_operation(comb, OPERATION_SECONDARY_IO) + set_comb_operation(comb, OPERATION_SECONDARY_IO) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false bottom_flow["radio_button"].visible = false bottom_flow["radio_label"].visible = false elseif element.selected_index == 3 then - param = set_combinator_operation(comb, OPERATION_DEPOT) + set_comb_operation(comb, OPERATION_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 == 4 then - param = set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) + set_comb_operation(comb, OPERATION_WAGON_MANIFEST) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false @@ -195,7 +173,7 @@ function register_gui_actions() return end - on_combinator_updated(global, comb, param) + combinator_update(global, comb) elseif msg[1] == "choose-elem-button" then local element = event.element if not element then return end @@ -207,9 +185,9 @@ function register_gui_actions() signal = nil element.elem_value = nil end - local param = set_comb_network_name(comb, signal) + set_comb_network_name(comb, signal) - on_combinator_updated(global, comb, param) + combinator_update(global, comb) elseif msg[1] == "radio_button" then local element = event.element if not element then return end @@ -217,9 +195,9 @@ function register_gui_actions() if not comb or not comb.valid then return end local allows_all_trains = not element.state - local param = set_comb_allows_all_trains(comb, allows_all_trains) + set_comb_allows_all_trains(comb, allows_all_trains) - on_combinator_updated(global, comb, param) + combinator_update(global, comb) elseif msg[1] == "switch" then local element = event.element if not element then return end @@ -227,9 +205,9 @@ function register_gui_actions() if not comb or not comb.valid then return end local is_pr_state = (element.switch_state == "none" and 0) or (element.switch_state == "left" and 1) or 2 - local param = set_comb_is_pr_state(comb, is_pr_state) + set_comb_is_pr_state(comb, is_pr_state) - on_combinator_updated(global, comb, param) + combinator_update(global, comb) end end end) diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index c0cd134..6ac779f 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -5,7 +5,19 @@ local floor = math.floor local ceil = math.ceil local string_find = string.find local string_sub = string.sub -local table_compare = table.compare + + +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 local function iterr(a, i) i = i + 1 @@ -447,10 +459,10 @@ function reset_station_layout(map_data, station, forbidden_entity) if supports_fluid then layout_pattern[wagon_number] = 3 else - layout_pattern[wagon_number] = 2 + layout_pattern[wagon_number] = 1 end elseif supports_fluid then - layout_pattern[wagon_number] = 1 + layout_pattern[wagon_number] = 2 else --layout_pattern[wagon_number] = nil end diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 2cd2e3f..a877c37 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -152,10 +152,8 @@ local function on_station_built(map_data, stop, comb1, comb2) priority = 0, r_threshold = 0, locked_slots = 0, - --network_name = param.first_signal and param.first_signal.name or nil, network_flag = 0, deliveries = {}, - --allows_all_trains = param.second_constant == 1, accepted_layouts = {}, layout_pattern = nil, p_count_or_r_threshold_per_item = {}, @@ -270,25 +268,25 @@ local function on_combinator_built(map_data, comb) wire = defines.wire_type.red, }) - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local param = control.parameters - local op = param.operation + local control = get_comb_control(comb) + local params = control.parameters + local op = params.operation if op == OPERATION_DEFAULT then op = OPERATION_PRIMARY_IO - param.operation = op - param.first_signal = NETWORK_SIGNAL_DEFAULT - control.parameters = param - elseif op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_FAILED_REQUEST then + params.operation = op + params.first_signal = NETWORK_SIGNAL_DEFAULT + control.parameters = params + elseif op ~= OPERATION_PRIMARY_IO and op ~= OPERATION_SECONDARY_IO and op ~= OPERATION_DEPOT and op ~= OPERATION_WAGON_MANIFEST then op = OPERATION_PRIMARY_IO - param.operation = op - control.parameters = param + params.operation = op + control.parameters = params end map_data.to_comb[comb.unit_number] = comb + map_data.to_comb_params[comb.unit_number] = params map_data.to_output[comb.unit_number] = out map_data.to_stop[comb.unit_number] = stop - map_data.to_comb_params[comb.unit_number] = param if op == OPERATION_WAGON_MANIFEST then if rail then @@ -360,7 +358,7 @@ function on_combinator_network_updated(map_data, comb, network_name) end ---@param map_data MapData ---@param comb LuaEntity -local function on_combinator_broken(map_data, comb) +function on_combinator_broken(map_data, comb) --NOTE: we do not check for wagon manifest combinators and update their stations, it is assumed they will be lazy deleted later ---@type uint local comb_id = comb.unit_number @@ -411,13 +409,17 @@ end ---@param map_data MapData ---@param comb LuaEntity ----@param new_params ArithmeticCombinatorParameters -function on_combinator_updated(map_data, comb, new_params) - local old_params = map_data.to_comb_params[comb.unit_number] - if new_params.operation ~= old_params.operation then - if (new_params.operation == OPERATION_PRIMARY_IO_ACTIVE or new_params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST or new_params.operation == OPERATION_PRIMARY_IO) and (old_params.operation == OPERATION_PRIMARY_IO_ACTIVE or old_params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST or old_params.operation == OPERATION_PRIMARY_IO) then - set_combinator_operation(comb, old_params.operation) - new_params.operation = old_params.operation +function combinator_update(map_data, comb) + ---@type uint + local unit_number = comb.unit_number + local control = get_comb_control(comb) + local params = control.parameters + local old_params = map_data.to_comb_params[unit_number] + + if params.operation ~= old_params.operation then + if (old_params.operation == OPERATION_PRIMARY_IO) and (params.operation == OPERATION_PRIMARY_IO_ACTIVE or params.operation == OPERATION_PRIMARY_IO_FAILED_REQUEST) then + --make sure only OPERATION_PRIMARY_IO gets stored on map_data.to_comb_params + params.operation = OPERATION_PRIMARY_IO else --NOTE: This is rather dangerous, we may need to actually implement operation changing on_combinator_broken(map_data, comb) @@ -425,19 +427,20 @@ function on_combinator_updated(map_data, comb, new_params) return end end - local new_signal = new_params.first_signal + local new_signal = params.first_signal local old_signal = old_params.first_signal local new_network = new_signal and new_signal.name or nil local old_network = old_signal and old_signal.name or nil if new_network ~= old_network then on_combinator_network_updated(map_data, comb, new_network) + map_data.to_comb_params[unit_number] = params end - if new_params.second_constant ~= old_params.second_constant then + if params.second_constant ~= old_params.second_constant then local stop = global.to_stop[comb.unit_number] if stop then local station = global.stations[stop.unit_number] if station then - local bits = new_params.second_constant + local bits = params.second_constant local is_pr_state = floor(bits/2)%3 station.is_p = is_pr_state == 0 or is_pr_state == 1 station.is_r = is_pr_state == 0 or is_pr_state == 2 @@ -448,8 +451,8 @@ function on_combinator_updated(map_data, comb, new_params) end end end + map_data.to_comb_params[unit_number] = params end - map_data.to_comb_params[comb.unit_number] = new_params end ---@param map_data MapData @@ -594,7 +597,7 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) set_depot_schedule(train_entity, train.depot_name) else --train still has cargo - lock_train(train.entity) + lock_train(train_entity) remove_train(map_data, train, train_id) send_nonempty_train_in_depot_alert(train_entity) end @@ -616,7 +619,7 @@ local function on_train_arrives_depot(map_data, depot_id, train_entity) set_depot_schedule(train_entity, train.depot_name) else - lock_train(train.entity) + lock_train(train_entity) send_nonempty_train_in_depot_alert(train_entity) end end @@ -825,35 +828,10 @@ local function on_paste(event) if not entity or not entity.valid then return end if entity.name == COMBINATOR_NAME then - on_combinator_updated(global, entity, get_comb_params(entity)) + combinator_update(global, entity) end end -local function on_cursor_stack_changed(event) - local i = event.player_index - local player = game.get_player(i) - if not player then return end - local cursor = player.cursor_stack - - if global.is_player_cursor_blueprint[i] then - --TODO: check if we can limit this search somehow? - for id, comb in pairs(global.to_comb) do - on_combinator_updated(global, comb, get_comb_params(comb)) - end - end - local contains_comb = nil - if cursor and cursor.valid_for_read and cursor.type == "blueprint" then - local cost_to_build = cursor.cost_to_build - for k, v in pairs(cost_to_build) do - if k == COMBINATOR_NAME then - contains_comb = true - break - end - end - end - global.is_player_cursor_blueprint[i] = contains_comb -end - local function on_settings_changed(event) mod_settings.tps = settings.global["cybersyn-ticks-per-second"].value --[[@as int]] @@ -907,8 +885,7 @@ local function main() flib_event.register({defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared}, on_surface_removed) - --flib_event.register(defines.events.on_entity_settings_pasted, on_paste) - --flib_event.register(defines.events.on_player_cursor_stack_changed, on_cursor_stack_changed) + flib_event.register(defines.events.on_entity_settings_pasted, on_paste) local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nth_tick, function() @@ -939,7 +916,6 @@ local function main() ---@type MapData local map_data = global local old_id = event.old_train_id_1 - local old_surface_index = event.old_surface_index --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 diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index 6209f04..163378b 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -2,20 +2,6 @@ local flib_migration = require("__flib__.migration") local migrations_table = { - ["0.2.0"] = function() - ---@type MapData - local map_data = global - map_data.tick_state = STATE_INIT - map_data.all_station_ids = {} - for id, station in pairs(map_data.stations) do - station.p_count_or_r_threshold_per_item = {} - station.p_threshold = nil - station.is_all = nil - set_station_from_comb_state(station) - set_combinator_operation(station.entity_comb1, OPERATION_PRIMARY_IO) - map_data.all_station_ids[#map_data.all_station_ids + 1] = id - end - end, ["0.2.1"] = function() ---@type MapData local map_data = global @@ -106,6 +92,28 @@ local migrations_table = { end map_data.layouts[id] = new_layout end + end, + ["0.5.1"] = function() + ---@type MapData + local map_data = global + map_data.tick_state = STATE_INIT + map_data.is_player_cursor_blueprint = nil + for id, layout in pairs(map_data.layouts) do + local new_layout = {} + local max_i = 0 + for i, v in pairs(layout) do + new_layout[i] = v + if i > max_i then + max_i = i + end + end + for i = 1, max_i do + if new_layout[i] == nil then + new_layout[i] = 0 + end + end + map_data.layouts[id] = new_layout + end for id, station in pairs(map_data.stations) do reset_station_layout(map_data, station) end