From 7e1d761086fb559b621651c766680c4190f496c1 Mon Sep 17 00:00:00 2001 From: Cbow2018 Date: Tue, 14 Jul 2026 13:39:33 +0100 Subject: [PATCH 1/2] Update data-final-fixes.lua Fixes issue #68 Factorio 2.1 added validation that rolling stock and its next_upgrade target must share the same connection_distance. Ship rolling stock is deepcopied from the vanilla prototypes in prototypes/ships.lua (boat and cargo_ship from cargo-wagon, oil_tanker from fluid-wagon, boat_engine and cargo_ship_engine from locomotive) and then overrides connection_distance. Bob's Logistics 3.0.0 ("Enabled automatic train upgrading", 2026-06-29) sets next_upgrade on those vanilla prototypes in place at the data stage, so the deepcopies inherit it and fail validation. This clears next_upgrade on the five ship rolling stock prototypes at data-final-fixes, following the existing pattern used for bridge, invisible_rail and floating_pole. Why data-final-fixes rather than clearing it in ships.lua: clearing at the point of deepcopy would only catch mods that set next_upgrade during the data stage. Doing it in final-fixes also covers mods that add it during data-updates. Happy to move it if you'd rather keep the logic local to ships.lua. Tested on Factorio 2.1.11 with Bob's Logistics 3.0.0 + Krastorio 2 + Space Age; loads cleanly with the patch, fails without it. --- data-final-fixes.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/data-final-fixes.lua b/data-final-fixes.lua index 566000a5..ac50a507 100644 --- a/data-final-fixes.lua +++ b/data-final-fixes.lua @@ -69,3 +69,15 @@ for _, character in pairs(data.raw.character) do character.collision_mask = collision_mask end end + + +-- Factorio 2.1 requires rolling stock and its next_upgrade target to share the same connection_distance. +for _, stock_type in pairs({ "locomotive", "cargo-wagon", "fluid-wagon", "artillery-wagon" }) do + if data.raw[stock_type] then + for _, name in pairs({ "boat", "boat_engine", "cargo_ship", "cargo_ship_engine", "oil_tanker" }) do + if data.raw[stock_type][name] then + data.raw[stock_type][name].next_upgrade = nil + end + end + end +end From f97e56195c4b9ee4fc5ca81ba72a3212db5c7447 Mon Sep 17 00:00:00 2001 From: Cbow2018 Date: Tue, 14 Jul 2026 18:05:06 +0100 Subject: [PATCH 2/2] Move next_upgrade clearing to data stage per review. --- data-final-fixes.lua | 11 ----------- prototypes/ships.lua | 6 ++++++ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/data-final-fixes.lua b/data-final-fixes.lua index ac50a507..7b21709d 100644 --- a/data-final-fixes.lua +++ b/data-final-fixes.lua @@ -70,14 +70,3 @@ for _, character in pairs(data.raw.character) do end end - --- Factorio 2.1 requires rolling stock and its next_upgrade target to share the same connection_distance. -for _, stock_type in pairs({ "locomotive", "cargo-wagon", "fluid-wagon", "artillery-wagon" }) do - if data.raw[stock_type] then - for _, name in pairs({ "boat", "boat_engine", "cargo_ship", "cargo_ship_engine", "oil_tanker" }) do - if data.raw[stock_type][name] then - data.raw[stock_type][name].next_upgrade = nil - end - end - end -end diff --git a/prototypes/ships.lua b/prototypes/ships.lua index 485275c7..a108b811 100644 --- a/prototypes/ships.lua +++ b/prototypes/ships.lua @@ -457,6 +457,7 @@ indep_boat.corpse = nil local boat_max_speed = 0.27*speed_modifier local boat = table.deepcopy(data.raw["cargo-wagon"]["cargo-wagon"]) +boat.next_upgrade = nil boat.name = "boat" boat.icons = {{icon=GRAPHICSPATH .. "icons/boat.png", icon_size = 64}} boat.icon = nil @@ -492,7 +493,9 @@ boat.corpse = nil local boat_engine_power = 300 + (speed_modifier -1) * 150 + local boat_engine = table.deepcopy(data.raw["locomotive"]["locomotive"]) +boat_engine.next_upgrade = nil boat_engine.name = "boat_engine" boat_engine.flags = {"placeable-neutral", "placeable-off-grid", @@ -578,6 +581,7 @@ local cargo_ship_capacity = settings.startup["cargo_ship_capacity"].value local ship_max_speed = 0.15 * speed_modifier local cargo_ship = table.deepcopy(data.raw["cargo-wagon"]["cargo-wagon"]) +cargo_ship.next_upgrade = nil cargo_ship.name = "cargo_ship" cargo_ship.icons = {{icon=GRAPHICSPATH .. "icons/cargoship_icon.png", icon_size = 64}} cargo_ship.icon = nil @@ -649,6 +653,7 @@ cargo_ship.corpse = nil local tanker_capacity = settings.startup["tanker_capacity"].value local oil_tanker = table.deepcopy(data.raw["fluid-wagon"]["fluid-wagon"]) +oil_tanker.next_upgrade = nil oil_tanker.name = "oil_tanker" oil_tanker.icon = GRAPHICSPATH .. "icons/tanker.png" oil_tanker.icon_size = 64 @@ -701,6 +706,7 @@ oil_tanker.connection_category = "ship_pump" local cargo_ship_engine_power = 2000 + (speed_modifier-1)*1200 local cargo_ship_engine = table.deepcopy(data.raw["locomotive"]["locomotive"]) +cargo_ship_engine.next_upgrade = nil cargo_ship_engine.name = "cargo_ship_engine" cargo_ship_engine.minable = {mining_time = 1, result = nil} cargo_ship_engine.flags = {"placeable-neutral",