Advertisement
kajacx

Wire connector mod improvements

May 20th, 2025
147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.89 KB | Gaming | 0 0
  1. local EPSILON = 0.001
  2. local ROUNDING_POSITION_PRECISION = 0.5
  3. local MAX_ENTITY_LIMIT = 10000
  4.  
  5. function round_to_multiple_of(number, rounding)
  6.     return rounding * math.floor(number / rounding + 0.5)
  7. end
  8.  
  9. function numbers_same(n1, n2)
  10.     return math.abs(n1 - n2) < EPSILON
  11. end
  12.  
  13. function effective_name(entity)
  14.     if entity.name == "entity-ghost" then
  15.         return entity.ghost_name
  16.     end
  17.     return entity.name
  18. end
  19.  
  20. function effective_type(entity)
  21.     if entity.type == "entity-ghost" then
  22.         return entity.ghost_type
  23.     end
  24.     return entity.type
  25. end
  26.  
  27. -- Given two power pole entities pole1 and pole2, checks if there is a third power pole between them
  28. -- and should therefore not be connected (because they will be connected through that third power pole).
  29. function has_power_pole_in_middle(pole1, pole2, entities, alignment)
  30.     local name1 = effective_name(pole1)
  31.     local name2 = effective_name(pole2)
  32.  
  33.     local min_x = math.min(pole1.position.x, pole2.position.x)
  34.     local max_x = math.max(pole1.position.x, pole2.position.x)
  35.  
  36.     local min_y = math.min(pole1.position.y, pole2.position.y)
  37.     local max_y = math.max(pole1.position.y, pole2.position.y)
  38.  
  39.     for _, entity in ipairs(entities) do
  40.         -- Don't check the source/target power pole itself
  41.         if entity == pole1 or entity == pole2 then
  42.             goto continue
  43.         end
  44.  
  45.         -- Only check power poles of the same type as at least one of the source/target power poles.
  46.         -- This should for example prevent two large power poles being disconnected because of a substation that is between them
  47.         -- but can't reach either of them (probably can't happen in vanilla, but can happen with quality or other mods).
  48.         local name = effective_name(entity)
  49.         if name ~= name1 and name ~= name2 then
  50.             goto continue
  51.         end
  52.  
  53.         if alignment == "x" and entity.position.y > min_y and entity.position.y < max_y then
  54.             return true
  55.         end
  56.  
  57.         if alignment == "y" and entity.position.x > min_x and entity.position.x < max_x then
  58.             return true
  59.         end
  60.  
  61.         ::continue::
  62.     end
  63.  
  64.     return false
  65. end
  66.  
  67. function collect_power_pole_entities(event)
  68.     local selected_poles = {}
  69.  
  70.     for _, entity in ipairs(event.entities) do
  71.         if entity.valid and effective_type(entity) == "electric-pole" then
  72.             table.insert(selected_poles, entity)
  73.         end
  74.     end
  75.  
  76.     return selected_poles
  77. end
  78.  
  79. function rewire_tool_main(player, event)
  80.     local selected_poles = collect_power_pole_entities(event)
  81.  
  82.     if (#selected_poles > MAX_ENTITY_LIMIT) then
  83.         player.print(
  84.             "Number of selected power poles (" .. #selected_poles .. ") exceeded the maximum of " .. MAX_ENTITY_LIMIT,
  85.             { skip = defines.print_skip.never }
  86.         )
  87.         return
  88.     end
  89.  
  90.     disconnect_all_wires(selected_poles)
  91.  
  92.     -- We will be checking only power poles on the same x or y axis, so we sort them by their rounded x/y value.
  93.     local by_x = {}
  94.     local by_y = {}
  95.  
  96.     for _, entity in ipairs(selected_poles) do
  97.         local rounded_x = round_to_multiple_of(entity.position.x, ROUNDING_POSITION_PRECISION)
  98.         if not by_x[rounded_x] then by_x[rounded_x] = {} end
  99.         table.insert(by_x[rounded_x], entity)
  100.  
  101.         local rounded_y = round_to_multiple_of(entity.position.y, ROUNDING_POSITION_PRECISION)
  102.         if not by_y[rounded_y] then by_y[rounded_y] = {} end
  103.         table.insert(by_y[rounded_y], entity)
  104.     end
  105.  
  106.     -- First un-wire all poles
  107.     disconnect_all_wires(selected_poles)
  108.  
  109.     -- Then wire x-aligned poles
  110.     for _, x_aligned in pairs(by_x) do
  111.         for i1, pole1 in pairs(x_aligned) do
  112.             local connector1 = pole1.get_wire_connector(defines.wire_connector_id.pole_copper)
  113.  
  114.             for i2, pole2 in pairs(x_aligned) do
  115.                 -- Only check each pair once
  116.                 if i2 <= i1 then
  117.                     goto continue
  118.                 end
  119.  
  120.                 local connector2 = pole2.get_wire_connector(defines.wire_connector_id.pole_copper)
  121.                 if connector1.can_wire_reach(connector2) and not has_power_pole_in_middle(pole1, pole2, x_aligned, "x") then
  122.                     connector1.connect_to(connector2)
  123.                 end
  124.  
  125.                 ::continue::
  126.             end
  127.         end
  128.     end
  129.  
  130.  
  131.     -- And then y-aligned poles
  132.     for _, y_aligned in pairs(by_y) do
  133.         for i1, pole1 in pairs(y_aligned) do
  134.             local connector1 = pole1.get_wire_connector(defines.wire_connector_id.pole_copper)
  135.  
  136.             for i2, pole2 in pairs(y_aligned) do
  137.                 -- Only check each pair once
  138.                 if i2 <= i1 then
  139.                     goto continue
  140.                 end
  141.  
  142.                 local connector2 = pole2.get_wire_connector(defines.wire_connector_id.pole_copper)
  143.                 if connector1.can_wire_reach(connector2) and not has_power_pole_in_middle(pole1, pole2, y_aligned, "y") then
  144.                     connector1.connect_to(connector2)
  145.                 end
  146.  
  147.                 ::continue::
  148.             end
  149.         end
  150.     end
  151. end
  152.  
  153. function disconnect_all_wires(selected_poles)
  154.     -- Save ids of selected entities to disconnect existing connection only from these
  155.     local ids = {}
  156.     for _, pole in pairs(selected_poles) do
  157.         ids[pole.unit_number] = true
  158.     end
  159.  
  160.     -- Only after the ids set has been filled we can go over the entities for real
  161.     for _, pole in pairs(selected_poles) do
  162.         local connector = pole.get_wire_connector(defines.wire_connector_id.pole_copper)
  163.         for _, connection in ipairs(connector.connections) do
  164.             local target_connector = connection.target
  165.             local target_entity = target_connector.owner
  166.             if ids[target_entity.unit_number] then
  167.                 connector.disconnect_from(target_connector)
  168.             end
  169.         end
  170.     end
  171. end
  172.  
  173. function rewire_tool_alt(player, event)
  174.     local selected_poles = collect_power_pole_entities(event)
  175.  
  176.     if (#selected_poles > MAX_ENTITY_LIMIT) then
  177.         player.print(
  178.             "Number of selected power poles (" .. #selected_poles .. ") exceeded the maximum of " .. MAX_ENTITY_LIMIT,
  179.             { skip = defines.print_skip.never }
  180.         )
  181.         return
  182.     end
  183.  
  184.     disconnect_all_wires(selected_poles)
  185. end
  186.  
  187. script.on_event(
  188.     defines.events.on_player_selected_area,
  189.     function(event)
  190.         local player = game.players[event.player_index]
  191.         local item = event.item
  192.  
  193.         if item == "rewire-tool-v2" then
  194.             rewire_tool_main(player, event)
  195.         end
  196.     end
  197. )
  198.  
  199. script.on_event(
  200.     defines.events.on_player_alt_selected_area,
  201.     function(event)
  202.         local player = game.players[event.player_index]
  203.         local item = event.item
  204.  
  205.         if item == "rewire-tool-v2" then
  206.             rewire_tool_alt(player, event)
  207.         end
  208.     end
  209. )
  210.  
Tags: factorio
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement