Versions: Transport Drones (Continued) 2.0.64, Maraxsis 1.31.8, Factorio 2.0.77 (Space Age)
Summary: With Maraxsis installed, rails, rail signals, train stops and rolling stock cannot be placed on road tiles. The engine rejects placement with the flying text "Cannot build on Road" (vanilla locale key cant-build-on-tile). The block is direction-dependent (a north-south rail may place where an east-west rail won't), which makes it look random. It is not caused by collision masks and not by any control-stage script. Disabling only Maraxsis makes rail placement on roads work — verified.
Root cause: Two data-stage tweaks collide.
- Transport Drones replaces the road tiles' collision mask entirely (road_tile.lua / process_road_item):
tile.collision_mask = {layers = {[road_collision_layer] = true}}
This wipes ground_tile and everything else from road tiles.
- Maraxsis (prototypes/collision-mask.lua, function
blacklist_via_tile_buildability_rule, running in its data-final-fixes) adds a tile_buildability_rules entry to essentially every entity with a collision box, to blacklist its underwater tiles. Since the API forces a required_tiles mask on such rules, it whitelists every layer it knows of — the code literally says "just throw all the layers and hope something collides":
required_tiles = {layers = {object, player, ground_tile, water_tile, <maraxsis layers>}}
Every tile in the game matches at least one of those layers — except Transport Drones roads, which only carry transport_drone_road. So every entity Maraxsis touched now requires ground beneath it that roads cannot provide, hence "Cannot build on Road". The rule's check area is the entity's slightly shrunken collision box, which rotates with the entity — that explains the direction-dependent behavior.
Reproduction: Enable both mods, place a road patch, try to place a straight rail on it → "Cannot build on Road". Disable Maraxsis, restart → same placement works.
Verified fix: We run this in a small patch mod's data-final-fixes.lua, with "? maraxsis" as an optional dependency in info.json so it executes after Maraxsis (load order matters — alphabetical order alone runs most mod names before "maraxsis"):
local types = {
"straight-rail", "curved-rail-a", "curved-rail-b", "half-diagonal-rail",
"legacy-straight-rail", "legacy-curved-rail",
"rail-signal", "rail-chain-signal", "train-stop",
"locomotive", "cargo-wagon", "fluid-wagon", "artillery-wagon",
}
for _, t in pairs(types) do
for _, proto in pairs(data.raw[t] or {}) do
for _, rule in pairs(proto.tile_buildability_rules or {}) do
if rule.required_tiles and rule.required_tiles.layers then
rule.required_tiles.layers["transport_drone_road"] = true
end
end
end
end
This extends Maraxsis' "any of these counts as valid ground" list with the road layer, for rail-related prototypes only. Confirmed in an ongoing Space Age save: rails now cross roads in both orientations, buildings remain blocked on roads, drones are unaffected (their confinement works via road tiles lacking ground_tile, which this patch doesn't touch), and Maraxsis' underwater blacklisting keeps working as intended.
Suggested integration: The snippet could go into this mod's data-final-fixes guarded by if mods["maraxsis"] then, plus "? maraxsis" in info.json for ordering. A more future-proof variant would apply it to all entities' tile_buildability_rules unconditionally, since other mods may use the same required_tiles pattern. Maraxsis conveniently tags its rules with is_maraxsis_rule = true if you want to target only those. Happy to provide more details or test a beta — and I can cross-post to the Maraxsis board.
Credit: I debugged this together with Claude (Anthropic's AI), which traced it from collision masks through control-stage scripts down to the engine's tile buildability rules and read both mods' sources to pinpoint the interaction, including the load-order trap.