Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local EPSILON = 0.001
- local ROUNDING_POSITION_PRECISION = 0.5
- local MAX_ENTITY_LIMIT = 10000
- function round_to_multiple_of(number, rounding)
- return rounding * math.floor(number / rounding + 0.5)
- end
- function numbers_same(n1, n2)
- return math.abs(n1 - n2) < EPSILON
- end
- function effective_name(entity)
- if entity.name == "entity-ghost" then
- return entity.ghost_name
- end
- return entity.name
- end
- function effective_type(entity)
- if entity.type == "entity-ghost" then
- return entity.ghost_type
- end
- return entity.type
- end
- -- Given two power pole entities pole1 and pole2, checks if there is a third power pole between them
- -- and should therefore not be connected (because they will be connected through that third power pole).
- function has_power_pole_in_middle(pole1, pole2, entities, alignment)
- local name1 = effective_name(pole1)
- local name2 = effective_name(pole2)
- local min_x = math.min(pole1.position.x, pole2.position.x)
- local max_x = math.max(pole1.position.x, pole2.position.x)
- local min_y = math.min(pole1.position.y, pole2.position.y)
- local max_y = math.max(pole1.position.y, pole2.position.y)
- for _, entity in ipairs(entities) do
- -- Don't check the source/target power pole itself
- if entity == pole1 or entity == pole2 then
- goto continue
- end
- -- Only check power poles of the same type as at least one of the source/target power poles.
- -- This should for example prevent two large power poles being disconnected because of a substation that is between them
- -- but can't reach either of them (probably can't happen in vanilla, but can happen with quality or other mods).
- local name = effective_name(entity)
- if name ~= name1 and name ~= name2 then
- goto continue
- end
- if alignment == "x" and entity.position.y > min_y and entity.position.y < max_y then
- return true
- end
- if alignment == "y" and entity.position.x > min_x and entity.position.x < max_x then
- return true
- end
- ::continue::
- end
- return false
- end
- function collect_power_pole_entities(event)
- local selected_poles = {}
- for _, entity in ipairs(event.entities) do
- if entity.valid and effective_type(entity) == "electric-pole" then
- table.insert(selected_poles, entity)
- end
- end
- return selected_poles
- end
- function rewire_tool_main(player, event)
- local selected_poles = collect_power_pole_entities(event)
- if (#selected_poles > MAX_ENTITY_LIMIT) then
- player.print(
- "Number of selected power poles (" .. #selected_poles .. ") exceeded the maximum of " .. MAX_ENTITY_LIMIT,
- { skip = defines.print_skip.never }
- )
- return
- end
- disconnect_all_wires(selected_poles)
- -- We will be checking only power poles on the same x or y axis, so we sort them by their rounded x/y value.
- local by_x = {}
- local by_y = {}
- for _, entity in ipairs(selected_poles) do
- local rounded_x = round_to_multiple_of(entity.position.x, ROUNDING_POSITION_PRECISION)
- if not by_x[rounded_x] then by_x[rounded_x] = {} end
- table.insert(by_x[rounded_x], entity)
- local rounded_y = round_to_multiple_of(entity.position.y, ROUNDING_POSITION_PRECISION)
- if not by_y[rounded_y] then by_y[rounded_y] = {} end
- table.insert(by_y[rounded_y], entity)
- end
- -- First un-wire all poles
- disconnect_all_wires(selected_poles)
- -- Then wire x-aligned poles
- for _, x_aligned in pairs(by_x) do
- for i1, pole1 in pairs(x_aligned) do
- local connector1 = pole1.get_wire_connector(defines.wire_connector_id.pole_copper)
- for i2, pole2 in pairs(x_aligned) do
- -- Only check each pair once
- if i2 <= i1 then
- goto continue
- end
- local connector2 = pole2.get_wire_connector(defines.wire_connector_id.pole_copper)
- if connector1.can_wire_reach(connector2) and not has_power_pole_in_middle(pole1, pole2, x_aligned, "x") then
- connector1.connect_to(connector2)
- end
- ::continue::
- end
- end
- end
- -- And then y-aligned poles
- for _, y_aligned in pairs(by_y) do
- for i1, pole1 in pairs(y_aligned) do
- local connector1 = pole1.get_wire_connector(defines.wire_connector_id.pole_copper)
- for i2, pole2 in pairs(y_aligned) do
- -- Only check each pair once
- if i2 <= i1 then
- goto continue
- end
- local connector2 = pole2.get_wire_connector(defines.wire_connector_id.pole_copper)
- if connector1.can_wire_reach(connector2) and not has_power_pole_in_middle(pole1, pole2, y_aligned, "y") then
- connector1.connect_to(connector2)
- end
- ::continue::
- end
- end
- end
- end
- function disconnect_all_wires(selected_poles)
- -- Save ids of selected entities to disconnect existing connection only from these
- local ids = {}
- for _, pole in pairs(selected_poles) do
- ids[pole.unit_number] = true
- end
- -- Only after the ids set has been filled we can go over the entities for real
- for _, pole in pairs(selected_poles) do
- local connector = pole.get_wire_connector(defines.wire_connector_id.pole_copper)
- for _, connection in ipairs(connector.connections) do
- local target_connector = connection.target
- local target_entity = target_connector.owner
- if ids[target_entity.unit_number] then
- connector.disconnect_from(target_connector)
- end
- end
- end
- end
- function rewire_tool_alt(player, event)
- local selected_poles = collect_power_pole_entities(event)
- if (#selected_poles > MAX_ENTITY_LIMIT) then
- player.print(
- "Number of selected power poles (" .. #selected_poles .. ") exceeded the maximum of " .. MAX_ENTITY_LIMIT,
- { skip = defines.print_skip.never }
- )
- return
- end
- disconnect_all_wires(selected_poles)
- end
- script.on_event(
- defines.events.on_player_selected_area,
- function(event)
- local player = game.players[event.player_index]
- local item = event.item
- if item == "rewire-tool-v2" then
- rewire_tool_main(player, event)
- end
- end
- )
- script.on_event(
- defines.events.on_player_alt_selected_area,
- function(event)
- local player = game.players[event.player_index]
- local item = event.item
- if item == "rewire-tool-v2" then
- rewire_tool_alt(player, event)
- end
- end
- )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement