402 lines
18 KiB
Lua
402 lines
18 KiB
Lua
-------------------------------------------------------------------------------
|
|
--[[Pipe Clamps]] --
|
|
-------------------------------------------------------------------------------
|
|
-- Concept designed and code written by TheStaplergun (staplergun on mod portal)
|
|
-- STDLib and code reviews provided by Nexela
|
|
|
|
local Event = require('__stdlib__/stdlib/event/event')
|
|
local Player = require('__stdlib__/stdlib/event/player')
|
|
require('__stdlib__/stdlib/area/area')
|
|
local Position = require('__stdlib__/stdlib/area/position')
|
|
local not_clampable = require('utils/not-clampable')
|
|
local utils = require('scripts/utils')
|
|
local abs = math.abs
|
|
|
|
--[[
|
|
defines.direction.north == 0 1
|
|
defines.direction.east == 2 4
|
|
defines.direction.south == 4 16
|
|
defines.direction.west == 6 64
|
|
--]]
|
|
|
|
local map_clamped_name = {
|
|
--[0] = "-clamped-none",
|
|
['-clamped-N'] = {name = '-clamped-single', direction = defines.direction.north},
|
|
['-clamped-E'] = {name = '-clamped-single', direction = defines.direction.east},
|
|
['-clamped-NE'] = {name = '-clamped-l', direction = defines.direction.east},
|
|
['-clamped-S'] = {name = '-clamped-single', direction = defines.direction.south},
|
|
['-clamped-NS'] = {name = '-clamped-i', direction = defines.direction.north},
|
|
['-clamped-SE'] = {name = '-clamped-l', direction = defines.direction.south},
|
|
['-clamped-NSE'] = {name = '-clamped-t', direction = defines.direction.east},
|
|
['-clamped-W'] = {name = '-clamped-single', direction = defines.direction.west},
|
|
['-clamped-NW'] = {name = '-clamped-l', direction = defines.direction.north},
|
|
['-clamped-EW'] = {name = '-clamped-i', direction = defines.direction.east},
|
|
['-clamped-NEW'] = {name = '-clamped-t', direction = defines.direction.north},
|
|
['-clamped-SW'] = {name = '-clamped-l', direction = defines.direction.west},
|
|
['-clamped-NSW'] = {name = '-clamped-t', direction = defines.direction.west},
|
|
['-clamped-SEW'] = {name = '-clamped-t', direction = defines.direction.south},
|
|
['-clamped-NSEW'] = {name = '-clamped-x', direction = defines.direction.north}
|
|
}
|
|
|
|
local clamped_name = {
|
|
--[0] = "-clamped-none",
|
|
[1] = '-clamped-N',
|
|
[4] = '-clamped-E',
|
|
[5] = '-clamped-NE',
|
|
[16] = '-clamped-S',
|
|
[17] = '-clamped-NS',
|
|
[20] = '-clamped-SE',
|
|
[21] = '-clamped-NSE',
|
|
[64] = '-clamped-W',
|
|
[65] = '-clamped-NW',
|
|
[68] = '-clamped-EW',
|
|
[69] = '-clamped-NEW',
|
|
[80] = '-clamped-SW',
|
|
[81] = '-clamped-NSW',
|
|
[84] = '-clamped-SEW',
|
|
[85] = '-clamped-NSEW'
|
|
}
|
|
|
|
-- can return nil or entity
|
|
local function get_last_pipe(player, pdata)
|
|
return pdata.last_pipe_position and (player.surface.find_entities_filtered {position = pdata.last_pipe_position, type = 'pipe'})[1]
|
|
end
|
|
|
|
-- returns a table which may or may not have contents if entity passed is nil
|
|
local function get_pipe_info(entity)
|
|
local data = {}
|
|
if entity and entity.valid then
|
|
local box = entity.fluidbox[1]
|
|
data.entity = entity
|
|
data.fluid_name = box and box.name
|
|
end
|
|
return data
|
|
end
|
|
|
|
--((
|
|
-- Clamping and Unclamping need to check for for a filter and add it to the replaced pipe
|
|
local function place_clamped_pipe(entity, table_entry, player, lock_pipe, autoclamp, area_clamp)
|
|
--local player, pdata = Player.get(player.index)
|
|
local entity_position = entity.position
|
|
local old_entity_unit_number = entity.unit_number
|
|
local new
|
|
if table_entry <= 85 and clamped_name[table_entry] then
|
|
local filter_table = entity.fluidbox.get_filter(1)
|
|
local event_data = {
|
|
entity = entity,
|
|
player_index = player.index
|
|
}
|
|
script.raise_event(defines.events.script_raised_destroy, event_data)
|
|
new =
|
|
entity.surface.create_entity {
|
|
name = entity.prototype.mineable_properties.products[1].name .. map_clamped_name[clamped_name[table_entry]].name,
|
|
position = entity_position,
|
|
direction = map_clamped_name[clamped_name[table_entry]].direction,
|
|
force = entity.force,
|
|
fast_replace = true,
|
|
spill = false
|
|
}
|
|
if not autoclamp and not area_clamp then
|
|
new.surface.create_entity {
|
|
name = 'flying-text',
|
|
position = entity_position,
|
|
text = {'pipe-tools.clamped'},
|
|
time_to_live = 60,
|
|
color = utils.color.green
|
|
}
|
|
end
|
|
new.last_user = player
|
|
new.fluidbox.set_filter(1, filter_table)
|
|
local event = {
|
|
created_entity = new,
|
|
entity = new,
|
|
player_index = player.index,
|
|
clamped = true,
|
|
replaced_entity_unit_number = old_entity_unit_number
|
|
}
|
|
script.raise_event(defines.events.script_raised_built, event)
|
|
if entity then
|
|
entity.destroy()
|
|
end
|
|
else
|
|
if lock_pipe then
|
|
entity.surface.create_entity {
|
|
name = 'flying-text',
|
|
position = entity_position,
|
|
text = {'pipe-tools.fail'},
|
|
time_to_live = 120,
|
|
color = utils.color.red
|
|
}
|
|
end
|
|
end
|
|
return new
|
|
end
|
|
|
|
|
|
local function clamp_pipe(entity, player, lock_pipe, autoclamp, reverse_entity, area_clamp)
|
|
local table_entry = 0
|
|
local neighbour_count = 0
|
|
local entity_position = entity.position
|
|
for _, entities in pairs(entity.neighbours) do
|
|
for _, neighbour in pairs(entities) do
|
|
local neighbour_position = neighbour.position
|
|
local delta_x = entity_position.x - neighbour_position.x
|
|
local delta_y = entity_position.y - neighbour_position.y
|
|
if delta_x ~= 0 then
|
|
if delta_y == 0 then
|
|
table_entry = table_entry + 2 ^ (utils.get_ew(delta_x))
|
|
neighbour_count = neighbour_count + 1
|
|
else
|
|
local adx,ady = abs(delta_x), abs(delta_y)
|
|
if adx > ady then
|
|
table_entry = table_entry + 2 ^ (utils.get_ew(delta_x))
|
|
else
|
|
table_entry = table_entry + 2 ^ (utils.get_ns(delta_y))
|
|
end
|
|
neighbour_count = neighbour_count + 1
|
|
end
|
|
else
|
|
table_entry = table_entry + 2 ^ (utils.get_ns(delta_y))
|
|
neighbour_count = neighbour_count + 1
|
|
end
|
|
end
|
|
end
|
|
if neighbour_count > 0 then
|
|
if reverse_entity then
|
|
table_entry = table_entry - 2 ^ (Position.direction_to(entity_position, reverse_entity.position))
|
|
end
|
|
place_clamped_pipe(entity, table_entry, player, lock_pipe, autoclamp, area_clamp)
|
|
end
|
|
end
|
|
|
|
local function check_sub_neighbours(sub_neighbours, neighbour, entity)
|
|
local fluid_box_counter = 0
|
|
for _, subsequent_entities in pairs(sub_neighbours) do
|
|
for _, subsequent_neighbour in pairs(subsequent_entities) do
|
|
if subsequent_neighbour ~= entity then
|
|
fluid_box_counter = fluid_box_counter + 1
|
|
end
|
|
end
|
|
if fluid_box_counter > 1 then
|
|
return neighbour
|
|
end
|
|
end
|
|
end
|
|
|
|
local function pipe_autoclamp_clamp(event, unclamp)
|
|
local entity = event.created_entity or event.entity
|
|
local player, pdata = Player.get(event.player_index)
|
|
|
|
local pipes_to_clamp = {}
|
|
local clamp_self
|
|
|
|
local current_fluid = get_pipe_info(entity).fluid_name
|
|
local last_pipe_data = get_pipe_info(get_last_pipe(player, pdata))
|
|
local last_pipe = last_pipe_data.entity
|
|
|
|
for _, entities in pairs(entity.neighbours) do
|
|
for _, neighbour in pairs(entities) do
|
|
if neighbour.type == 'pipe' or (neighbour.type == 'pipe-to-ground' and string.find(neighbour.name, '%-clamped%-')) then
|
|
local neighbour_fluid = get_pipe_info(neighbour).fluid_name
|
|
if current_fluid then
|
|
-- Ensure fluids don't mix
|
|
if neighbour_fluid and (neighbour_fluid ~= current_fluid) then
|
|
-- If the neighbour has a fluid and they don't match, we're clamping it. Period.
|
|
neighbour.surface.create_entity {
|
|
name = 'flying-text',
|
|
position = neighbour.position,
|
|
text = {'pipe-tools.mismatch'},
|
|
time_to_live = 120,
|
|
speed = 0,
|
|
color = utils.color.red
|
|
}
|
|
pipes_to_clamp[#pipes_to_clamp + 1] = neighbour
|
|
elseif not unclamp and not pdata.disable_auto_clamp then
|
|
-- This is not a logic duplicate of below. This branch is different and has a different purpose than above.
|
|
-- If the player wasn't unclamping, do further checks if auto clamp is on.
|
|
if last_pipe and neighbour ~= last_pipe then
|
|
-- If there's a last pipe make sure it isnt the neighbour. If it's not clamp it. Allows parallel laying and T-ing into a pipeline.
|
|
pipes_to_clamp[#pipes_to_clamp + 1] = check_sub_neighbours(neighbour.neighbours, neighbour, entity)
|
|
elseif not last_pipe then
|
|
-- Explicit check to make sure there isn't a last pipe. I don't want false to the above but then last pipe getting clamped anyways.
|
|
pipes_to_clamp[#pipes_to_clamp + 1] = check_sub_neighbours(neighbour.neighbours, neighbour, entity)
|
|
end
|
|
end
|
|
elseif not unclamp and not pdata.disable_auto_clamp then
|
|
-- If the current pipe doesn't have a fluid, make sure the player wasn't just unclamping, and make sure auto clamp is on.
|
|
-- <AUTO CLAMP MODE>
|
|
if last_pipe and neighbour ~= last_pipe and Position.distance(entity.position, last_pipe.position) == 1 then
|
|
-- This will see if last pipe exists, make sure that the neighbour isn't the last pipe, and if it isn't, see if it's within a tile (Tracking last pipes fluid)
|
|
if last_pipe_data.fluid_name and neighbour_fluid and (last_pipe_data.fluid_name ~= neighbour_fluid) then
|
|
-- Within, if the last pipe has a fluid name see if the neighbour has a fluid. If so, do they match? If not clamp that neighbour. Allows parallel pipe laying of dissimilar fluids.
|
|
neighbour.surface.create_entity {
|
|
name = 'flying-text',
|
|
position = neighbour.position,
|
|
text = {'pipe-tools.mismatch'},
|
|
time_to_live = 120,
|
|
speed = 0,
|
|
color = utils.color.red
|
|
}
|
|
pipes_to_clamp[#pipes_to_clamp + 1] = neighbour
|
|
else
|
|
-- Clamp the neighbour if it's part of an existing pipeline
|
|
pipes_to_clamp[#pipes_to_clamp + 1] = check_sub_neighbours(neighbour.neighbours, neighbour, entity)
|
|
end
|
|
elseif not last_pipe or (last_pipe and Position.distance(entity.position, last_pipe.position) ~= 1) then -- Catches all other cases
|
|
pipes_to_clamp[#pipes_to_clamp + 1] = check_sub_neighbours(neighbour.neighbours, neighbour, entity)
|
|
end
|
|
end
|
|
elseif not pdata.disable_auto_clamp and neighbour.type == 'storage-tank' then
|
|
-- If it's not a pipe, we need to clamp our own pipe instead.
|
|
local neighbour_fluid = get_pipe_info(neighbour).fluid_name
|
|
-- NOTES: Try simple entity placement to prevent spam.
|
|
if current_fluid then
|
|
if current_fluid ~= neighbour_fluid then
|
|
entity.surface.create_entity {
|
|
name = 'flying-text',
|
|
position = entity.position,
|
|
text = {'pipe-tools.mismatch'},
|
|
time_to_live = 120,
|
|
speed = 0,
|
|
color = utils.color.red
|
|
}
|
|
clamp_self = neighbour
|
|
end
|
|
elseif last_pipe and neighbour ~= last_pipe and Position.distance(entity.position, last_pipe.position) == 1 then
|
|
if last_pipe_data.fluid_name and neighbour_fluid and (last_pipe_data.fluid_name ~= neighbour_fluid) then
|
|
-- Last tracked fluid
|
|
entity.surface.create_entity {
|
|
name = 'flying-text',
|
|
position = entity.position,
|
|
text = {'pipe-tools.mismatch'},
|
|
time_to_live = 120,
|
|
speed = 0,
|
|
color = utils.color.red
|
|
}
|
|
clamp_self = neighbour
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for _, entities in pairs(pipes_to_clamp) do
|
|
clamp_pipe(entities, player, false, true, entity)
|
|
end
|
|
if clamp_self then
|
|
clamp_pipe(entity, player, false, true, clamp_self)
|
|
end
|
|
end
|
|
|
|
local function un_clamp_pipe(entity, player, area_unclamp)
|
|
local old_entity_unit_number = entity.unit_number
|
|
local pos = entity.position
|
|
local filter_table = entity.fluidbox.get_filter(1)
|
|
local event_data = {
|
|
entity = entity,
|
|
player_index = player.index
|
|
}
|
|
script.raise_event(defines.events.script_raised_destroy, event_data)
|
|
local new =
|
|
entity.surface.create_entity {
|
|
name = entity.prototype.mineable_properties.products[1].name,
|
|
position = pos,
|
|
force = entity.force,
|
|
fast_replace = true,
|
|
spill = false
|
|
}
|
|
if not area_unclamp then
|
|
new.surface.create_entity {
|
|
name = 'flying-text',
|
|
position = pos,
|
|
text = {'pipe-tools.unclamped'},
|
|
color = utils.color.yellow
|
|
}
|
|
end
|
|
new.last_user = player
|
|
new.fluidbox.set_filter(1, filter_table)
|
|
local event = {
|
|
created_entity = new,
|
|
entity = new,
|
|
player_index = player.index,
|
|
clamped = true,
|
|
replaced_entity_unit_number = old_entity_unit_number
|
|
}
|
|
script.raise_event(defines.events.script_raised_built, event)
|
|
if entity then
|
|
entity.destroy()
|
|
end
|
|
pipe_autoclamp_clamp(event, true)
|
|
end
|
|
|
|
local function toggle_pipe_clamp(event)
|
|
local player = game.players[event.player_index]
|
|
local pforce = player.force
|
|
|
|
local tool = utils.selection_tool_event[event.name]
|
|
if tool and event.item ~= 'picker-pipe-clamper' then
|
|
return
|
|
end
|
|
|
|
local selected = player.selected
|
|
local clamp = event.name == defines.events.on_player_selected_area or (not tool and selected and selected.type == 'pipe')
|
|
local entities = event.entities or {selected}
|
|
local player_held_type = player.cursor_stack and player.cursor_stack.valid_for_read and player.cursor_stack.prototype and player.cursor_stack.prototype.place_result and player.cursor_stack.prototype.place_result.type
|
|
|
|
for _, entity in pairs(entities) do
|
|
if entity.valid then
|
|
local name, type = entity.name, entity.type
|
|
|
|
if (player_held_type == 'pipe' or player_held_type == 'pipe-to-ground') and entity.force == pforce then
|
|
if clamp then
|
|
if entity.type == 'pipe' and not not_clampable(name) then
|
|
clamp_pipe(entity, player, not tool and clamp, false, false, tool)
|
|
end
|
|
else
|
|
if type == 'pipe-to-ground' and name:find('%-clamped%-') then
|
|
un_clamp_pipe(entity, player, tool)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function on_built_entity(event)
|
|
if event.created_entity and event.created_entity.type == 'pipe' and not not_clampable(event.created_entity.name) then
|
|
local _, pdata = Player.get(event.player_index)
|
|
-- Store position ahead of time. Entity can be invalidated (replaced) during the following function before storing it's position.
|
|
local position_to_save = event.created_entity.position
|
|
pipe_autoclamp_clamp(event, false)
|
|
pdata.last_pipe_position = position_to_save
|
|
end
|
|
end
|
|
|
|
local function on_player_rotated_entity(event)
|
|
if event.entity and event.entity.name:find('%-clamped%-') then
|
|
pipe_autoclamp_clamp(event, true)
|
|
end
|
|
end
|
|
|
|
local function toggle_auto_clamp(event)
|
|
local player, pdata = Player.get(event.player_index)
|
|
if utils.truthy[event.parameter] then
|
|
pdata.disable_auto_clamp = false
|
|
elseif utils.falsey[event.parameter] then
|
|
pdata.disable_auto_clamp = true
|
|
else
|
|
pdata.disable_auto_clamp = not pdata.disable_auto_clamp
|
|
end
|
|
player.print({'pipe-tools.auto-clamp', pdata.disable_auto_clamp and {'pipe-tools.off'} or {'pipe-tools.on'}})
|
|
return pdata.disable_auto_clamp
|
|
end
|
|
|
|
if settings.startup['picker-tool-pipe-clamps'].value then
|
|
Event.register('picker-toggle-pipe-clamp', toggle_pipe_clamp)
|
|
Event.register({defines.events.on_player_selected_area, defines.events.on_player_alt_selected_area}, toggle_pipe_clamp)
|
|
Event.register(defines.events.on_built_entity, on_built_entity)
|
|
Event.register(defines.events.on_player_rotated_entity, on_player_rotated_entity)
|
|
--Event.register('picker-auto-clamp-toggle', toggle_auto_clamp)
|
|
commands.add_command('autoclamp', {'autoclamp-commands.toggle-autoclamp'}, toggle_auto_clamp)
|
|
end
|
|
--remote.add_interface(script.mod_name, require('__stdlib__/stdlib/scripts/interface'))
|