diff --git a/cybersyn/changelog.txt b/cybersyn/changelog.txt index 1ceff0f..9d73006 100644 --- a/cybersyn/changelog.txt +++ b/cybersyn/changelog.txt @@ -19,3 +19,9 @@ Version: 0.3.0 Date: 2022-11-13 Features: - Added warmup period on just built stations +--------------------------------------------------------------------------------------------------- +Version: 0.3.0 +Date: 2022-11-13 + Features: + - Fixed copy-paste + - Added alert sounds diff --git a/cybersyn/info.json b/cybersyn/info.json index 8dddfad..6343fd8 100644 --- a/cybersyn/info.json +++ b/cybersyn/info.json @@ -1,6 +1,6 @@ { "name": "cybersyn", - "version": "0.3.0", + "version": "0.4.0", "title": "Project Cybersyn", "author": "Mami", "factorio_version": "1.1", diff --git a/cybersyn/locale/en/base.cfg b/cybersyn/locale/en/base.cfg index f0f8c42..de9e67f 100644 --- a/cybersyn/locale/en/base.cfg +++ b/cybersyn/locale/en/base.cfg @@ -39,6 +39,7 @@ cybersyn-locked-slots=Locked slots per cargo wagon missing-trains=No trains available to make a delivery from __2__ to __1__ lost-train=A train has become lost nonempty-train=A train has parked in a depot while still containing items; it cannot be dispatched until it is empty +unexpected-train=A train has returned to the depot before completing it's delivery [cybersyn-gui] combinator-title=Cybernetic combinator diff --git a/cybersyn/prototypes/entity.lua b/cybersyn/prototypes/entity.lua index c5fd57c..44ebb83 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 = true diff --git a/cybersyn/scripts/constants.lua b/cybersyn/scripts/constants.lua index cab734f..14406aa 100644 --- a/cybersyn/scripts/constants.lua +++ b/cybersyn/scripts/constants.lua @@ -11,6 +11,7 @@ LOCKED_SLOTS = "cybersyn-locked-slots" COMBINATOR_NAME = "cybersyn-combinator" COMBINATOR_OUT_NAME = "cybersyn-combinator-output" COMBINATOR_CLOSE_SOUND = "entity-close/cybersyn-combinator" +ALERT_SOUND = "utility/console_message" OPERATION_DEFAULT = "*" OPERATION_PRIMARY_IO = "/" diff --git a/cybersyn/scripts/factorio-api.lua b/cybersyn/scripts/factorio-api.lua index 4565793..71bf86b 100644 --- a/cybersyn/scripts/factorio-api.lua +++ b/cybersyn/scripts/factorio-api.lua @@ -70,7 +70,9 @@ function create_manifest_schedule(depot_name, p_stop, r_stop, manifest) }} end - +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 @@ -78,14 +80,14 @@ function get_comb_secondary_state(param) end ---@param depot Depot function set_depot_from_comb_state(depot) - local param = depot.entity_comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local param = get_comb_params(depot.entity_comb) local signal = param.first_signal depot.network_name = 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 = station.entity_comb1.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] + local param = get_comb_params(station.entity_comb1) local bits = param.second_constant or 0 local is_pr_state = floor(bits/2)%3 local signal = param.first_signal @@ -94,19 +96,36 @@ function set_station_from_comb_state(station) station.is_p = is_pr_state == 0 or is_pr_state == 1 station.is_r = is_pr_state == 0 or is_pr_state == 2 end ----@param control LuaArithmeticCombinatorControlBehavior -function set_comb_allows_all_trains(control, allows_all_trains) +---@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 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 control LuaArithmeticCombinatorControlBehavior -function set_comb_is_pr_state(control, is_pr_state) +---@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 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 +---@param signal SignalID? +function set_comb_network_name(comb, signal) + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local param = control.parameters + + param.first_signal = signal + control.parameters = param + return param end @@ -122,24 +141,25 @@ end ---@param comb LuaEntity ---@param op string function set_combinator_operation(comb, op) - local a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - control.operation = op - a.parameters = control + 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 a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters + local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] + local param = control.parameters if is_failed then - if control.operation == OPERATION_PRIMARY_IO then - control.operation = OPERATION_PRIMARY_IO_REQUEST_FAILED - a.parameters = control + if param.operation == OPERATION_PRIMARY_IO then + param.operation = OPERATION_PRIMARY_IO_REQUEST_FAILED + control.parameters = param end - elseif control.operation == OPERATION_PRIMARY_IO_REQUEST_FAILED then - control.operation = OPERATION_PRIMARY_IO - a.parameters = control + elseif param.operation == OPERATION_PRIMARY_IO_REQUEST_FAILED then + param.operation = OPERATION_PRIMARY_IO + control.parameters = param end end @@ -193,9 +213,8 @@ function send_missing_train_alert_for_stops(r_stop, p_stop) r_stop, send_missing_train_alert_for_stop_icon, {"cybersyn-messages.missing-trains", r_stop.backer_name, p_stop.backer_name}, - true - ) -end + true) + end end local send_lost_train_alert_icon = {name = LOST_TRAIN_NAME, type = "fluid"} @@ -208,12 +227,26 @@ function send_lost_train_alert(train) loco, send_lost_train_alert_icon, {"cybersyn-messages.lost-train"}, - true - ) + true) + player.play_sound({path = ALERT_SOUND}) + end end end +---@param train LuaTrain +function send_unexpected_train_alert(train) + local loco = train.front_stock or train.back_stock + if loco then + for _, player in pairs(loco.force.players) do + player.add_custom_alert( + loco, + send_lost_train_alert_icon, + {"cybersyn-messages.unexpected-train"}, + true) + end + end end + local send_nonempty_train_in_depot_alert_icon = {name = NONEMPTY_TRAIN_NAME, type = "fluid"} ---@param train LuaTrain function send_nonempty_train_in_depot_alert(train) @@ -224,8 +257,8 @@ function send_nonempty_train_in_depot_alert(train) loco, send_nonempty_train_in_depot_alert_icon, {"cybersyn-messages.nonempty-train"}, - true - ) + true) + player.play_sound({path = ALERT_SOUND}) + end end end -end diff --git a/cybersyn/scripts/global.lua b/cybersyn/scripts/global.lua index 19dfac3..9ab230c 100644 --- a/cybersyn/scripts/global.lua +++ b/cybersyn/scripts/global.lua @@ -2,7 +2,9 @@ ---@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} ---@field public to_stop {[uint]: LuaEntity} ---@field public stations {[uint]: Station} @@ -90,6 +92,7 @@ function init_global() all_names = {}, } global.to_comb = {} + global.to_comb_params = {} global.to_output = {} global.to_stop = {} global.stations = {} @@ -101,4 +104,5 @@ function init_global() global.layouts = {} global.layout_train_count = {} global.layout_top_id = 1 + global.is_player_cursor_blueprint = {} end diff --git a/cybersyn/scripts/gui.lua b/cybersyn/scripts/gui.lua index 0f27d03..b00e32b 100644 --- a/cybersyn/scripts/gui.lua +++ b/cybersyn/scripts/gui.lua @@ -30,12 +30,12 @@ STATUS_NAMES_DEFAULT = "entity-status.disabled" ---@param player LuaPlayer function gui_opened(comb, player) local rootgui = player.gui.screen - local control = comb.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - local op = control.operation + 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(control) + 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 @@ -94,7 +94,7 @@ 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=control.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=param.first_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={ @@ -162,29 +162,30 @@ function register_gui_actions() local top_flow = element.parent local all_flow = top_flow.parent local bottom_flow = all_flow.bottom + local param if element.selected_index == 1 then - set_combinator_operation(comb, OPERATION_PRIMARY_IO) + param = set_combinator_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 - set_combinator_operation(comb, OPERATION_SECONDARY_IO) + param = set_combinator_operation(comb, OPERATION_SECONDARY_IO) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false bottom_flow["radio_button"].visible = false bottom_flow["radio_label"].visible = false elseif element.selected_index == 3 then - set_combinator_operation(comb, OPERATION_DEPOT) + param = set_combinator_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 - set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) + param = set_combinator_operation(comb, OPERATION_WAGON_MANIFEST) top_flow["switch"].visible = false all_flow["network_label"].visible = false bottom_flow["network"].visible = false @@ -194,44 +195,31 @@ function register_gui_actions() return end - on_combinator_updated(global, comb) + on_combinator_updated(global, comb, param) elseif msg[1] == "choose-elem-button" 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 a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local control = a.parameters - local signal = element.elem_value if signal and (signal.name == "signal-everything" or signal.name == "signal-anything" or signal.name == "signal-each") then - control.first_signal = nil + signal = nil element.elem_value = nil - else - control.first_signal = signal end - a.parameters = control + local param = set_comb_network_name(comb, signal) - on_combinator_network_updated(global, comb, signal and signal.name or nil) + on_combinator_updated(global, comb, param) elseif msg[1] == "radio_button" 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 control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - local allows_all_trains = not element.state - set_comb_allows_all_trains(control, allows_all_trains) + local param = set_comb_allows_all_trains(comb, allows_all_trains) - local stop = global.to_stop[comb.unit_number] - if stop then - local station = global.stations[stop.unit_number] - if station then - set_station_train_class(global, station, allows_all_trains) - end - end + on_combinator_updated(global, comb, param) elseif msg[1] == "switch" then local element = event.element if not element then return end @@ -239,17 +227,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 a = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] - set_comb_is_pr_state(a, is_pr_state) + local param = set_comb_is_pr_state(comb, is_pr_state) - local stop = global.to_stop[comb.unit_number] - if stop then - local station = global.stations[stop.unit_number] - if station then - 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 - end + on_combinator_updated(global, comb, param) end end end) diff --git a/cybersyn/scripts/layout.lua b/cybersyn/scripts/layout.lua index 9b71a35..2c7423c 100644 --- a/cybersyn/scripts/layout.lua +++ b/cybersyn/scripts/layout.lua @@ -397,8 +397,8 @@ local function reset_station_layout(map_data, station, forbidden_entity) supports_fluid = true end elseif entity.name == COMBINATOR_NAME then - local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - if control.operation == OPERATION_WAGON_MANIFEST then + local param = map_data.to_comb_params[entity.unit_number] + if param.operation == OPERATION_WAGON_MANIFEST then local pos = entity.position local is_there if is_ver then @@ -445,18 +445,6 @@ local function reset_station_layout(map_data, station, forbidden_entity) end end ----@param map_data MapData ----@param station Station ----@param allows_all_trains boolean -function set_station_train_class(map_data, station, allows_all_trains) - if station.allows_all_trains ~= allows_all_trains then - station.allows_all_trains = allows_all_trains - if not allows_all_trains then - reset_station_layout(map_data, station, nil) - end - end -end - ---@param map_data MapData ---@param station Station ---@param forbidden_entity LuaEntity? diff --git a/cybersyn/scripts/main.lua b/cybersyn/scripts/main.lua index 4980bf7..744e0ec 100644 --- a/cybersyn/scripts/main.lua +++ b/cybersyn/scripts/main.lua @@ -1,5 +1,6 @@ --By Mami local flib_event = require("__flib__.event") +local floor = math.floor ---@param map_data MapData @@ -186,8 +187,8 @@ local function search_for_station_combinator(map_data, stop, comb_operation, com entity.valid and entity.name == COMBINATOR_NAME and entity ~= comb_forbidden and map_data.to_stop[entity.unit_number] == stop then - local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - if control.operation == comb_operation then + local param = get_comb_params(entity) + if param.operation == comb_operation then return entity end end @@ -243,13 +244,15 @@ local function on_combinator_built(map_data, comb) wire = defines.wire_type.red, }) - map_data.to_comb[comb.unit_number] = comb - map_data.to_output[comb.unit_number] = out - map_data.to_stop[comb.unit_number] = stop - local control = comb.get_or_create_control_behavior()--[[@as LuaArithmeticCombinatorControlBehavior]] local param = control.parameters local op = param.operation + + map_data.to_comb[comb.unit_number] = comb + 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_DEFAULT then op = OPERATION_PRIMARY_IO param.operation = op @@ -334,8 +337,10 @@ end ---@param comb LuaEntity local 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 - local out = map_data.to_output[comb.unit_number] - local stop = map_data.to_stop[comb.unit_number] + ---@type uint + local comb_id = comb.unit_number + local out = map_data.to_output[comb_id] + local stop = map_data.to_stop[comb_id] if stop and stop.valid then local station = map_data.stations[stop.unit_number] @@ -345,6 +350,7 @@ local function on_combinator_broken(map_data, comb) if comb1 then station.entity_comb1 = comb1 set_station_from_comb_state(station) + update_station_if_auto(map_data, station) else on_station_broken(map_data, stop.unit_number, station) local depot_comb = search_for_station_combinator(map_data, stop, OPERATION_DEPOT, comb) @@ -374,16 +380,47 @@ local function on_combinator_broken(map_data, comb) if out and out.valid then out.destroy() end - map_data.to_comb[comb.unit_number] = nil - map_data.to_output[comb.unit_number] = nil - map_data.to_stop[comb.unit_number] = nil + map_data.to_comb[comb_id] = nil + map_data.to_output[comb_id] = nil + map_data.to_stop[comb_id] = nil + map_data.to_comb_params[comb_id] = nil end + ---@param map_data MapData ---@param comb LuaEntity -function on_combinator_updated(map_data, comb) - --NOTE: this is the lazy way to implement updates and puts strong restrictions on data validity on on_combinator_broken - on_combinator_broken(map_data, comb) - on_combinator_built(map_data, comb) +---@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 + on_combinator_broken(map_data, comb) + on_combinator_built(map_data, comb) + else + local new_signal = new_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) + end + if new_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 is_pr_state = floor(bits/2)%3 + station.is_p = is_pr_state == 0 or is_pr_state == 1 + station.is_r = is_pr_state == 0 or is_pr_state == 2 + local allow_all_trains = bits%2 == 1 + if station.allow_all_trains ~= allow_all_trains then + station.allow_all_trains = allow_all_trains + update_station_if_auto(map_data, station) + end + end + end + end + map_data.to_comb_params[comb.unit_number] = new_params + end end ---@param map_data MapData @@ -403,8 +440,8 @@ local function on_stop_built(map_data, stop) for _, entity in pairs(entities) do if entity.valid and entity.name == COMBINATOR_NAME and map_data.to_stop[entity.unit_number] == nil then map_data.to_stop[entity.unit_number] = stop - local control = entity.get_or_create_control_behavior().parameters--[[@as ArithmeticCombinatorParameters]] - local op = control.operation + local param = get_comb_params(entity) + local op = param.operation if op == OPERATION_PRIMARY_IO or op == OPERATION_PRIMARY_IO_ACTIVE or op == OPERATION_PRIMARY_IO_REQUEST_FAILED then comb1 = entity elseif op == OPERATION_SECONDARY_IO then @@ -510,7 +547,7 @@ local function on_train_arrives_depot(map_data, depot, train_entity) else if train.manifest then on_failed_delivery(map_data, train) - send_lost_train_alert(train.entity) + send_unexpected_train_alert(train.entity) end train.status = STATUS_D add_available_train(map_data, depot, train_id) @@ -734,15 +771,43 @@ local function on_surface_removed(event) end end + local function on_paste(event) local entity = event.destination if not entity or not entity.valid then return end if entity.name == COMBINATOR_NAME then - on_combinator_updated(global, entity) + on_combinator_updated(global, entity, get_comb_params(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 + player.play_sound({path = ALERT_SOUND}) + + 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]] mod_settings.r_threshold = settings.global["cybersyn-request-threshold"].value--[[@as int]] @@ -792,6 +857,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) local nth_tick = math.ceil(60/mod_settings.tps); flib_event.on_nth_tick(nth_tick, function() diff --git a/cybersyn/scripts/migrations.lua b/cybersyn/scripts/migrations.lua index d854087..d6d0db7 100644 --- a/cybersyn/scripts/migrations.lua +++ b/cybersyn/scripts/migrations.lua @@ -31,6 +31,15 @@ local migrations_table = { map_data.all_station_ids = nil mod_settings.warmup_time = settings.global["cybersyn-warmup-time"].value--[[@as int]] end, + ["0.4.0"] = function() + ---@type MapData + local map_data = global + map_data.is_player_cursor_blueprint = {} + map_data.to_comb_params = {} + for id, comb in pairs(map_data.to_comb) do + map_data.to_comb_params[id] = get_comb_params(comb) + end + end, } ---@param data ConfigurationChangedData