This may or may not be useful to this project, but I thought I'd donate it here in case you thought it may be. Attempting to work with blueprints (specifically of the ctrl-c variety being stamped on existing entities) is tough. We don't get many events with a lot of info. Just on_pre_build. So you pretty much have to do your own BP position calculations. These are a couple of functions that I put together to get a bounding box of all the BP entities, and how to find the center of where the BP will be placed. If helpful, feel free to use them freely in flib.
---@param entities BlueprintEntity[] # E.g. from LuaPlayer.get_blueprint_entities()
---@return BoundingBox # Box that contains all blueprint entities
---@return uint # The building grid size needed to build this blueprint
function M.get_blueprint_bounding_box(entities)
local box = flib_box.from_position(entities[1].position, true)
local names = {}
for _, e in ipairs(entities) do
names[e.name] = true
end
local name_filter = {}
for k, _ in pairs(names) do
table.insert(name_filter, k)
end
-- Define a bounding box the size of the blueprint to be placed
local grid_size = 1
---@diagnostic disable-next-line:missing-fields
local protos = game.get_filtered_entity_prototypes{{filter = "name", name = name_filter}}
for _, entity in pairs(entities) do
local collision_box = protos[entity.name].collision_box
grid_size = math.max(grid_size, protos[entity.name].building_grid_bit_shift)
box = flib_box.expand_to_contain_box(
box,
flib_box.from_dimensions(
entity.position,
flib_box.width(collision_box),
flib_box.height(collision_box)
)
)
end
-- Expand bounding box to be full tiles based on the entity with the largest building grid size
box.left_top.x = grid_size * math.floor(box.left_top.x / grid_size)
box.left_top.y = grid_size * math.floor(box.left_top.y / grid_size)
box.right_bottom.x = grid_size * math.ceil(box.right_bottom.x / grid_size)
box.right_bottom.y = grid_size * math.ceil(box.right_bottom.y / grid_size)
return box, grid_size
end
---@param box BoundingBox # Bounding box of blueprint to place
---@param pos MapPosition # Position to center new bounding box around
---@param grid_size uint? # Building grid size to base centering. Default: 1
---@return MapPosition # Center position of where blueprint will be placed
function M.get_placed_blueprint_center(box, pos, grid_size)
local grid_size = grid_size or 1
local pos_x = pos.x or pos[1]
local pos_y = pos.y or pos[2]
pos_x = (flib_box.width(box) / grid_size) % 2 == 0
and math.floor(pos_x / grid_size + .5 ) * grid_size
or math.floor(pos_x / grid_size) * grid_size + grid_size / 2
pos_y = (flib_box.height(box) / grid_size) % 2 == 0
and math.floor(pos_y / grid_size + .5 ) * grid_size
or math.floor(pos_y / grid_size) * grid_size + grid_size / 2
if pos.x then
return { x = pos_x, y = pos_y }
else
return { pos_x, pos_y }
end
end
This may or may not be useful to this project, but I thought I'd donate it here in case you thought it may be. Attempting to work with blueprints (specifically of the ctrl-c variety being stamped on existing entities) is tough. We don't get many events with a lot of info. Just on_pre_build. So you pretty much have to do your own BP position calculations. These are a couple of functions that I put together to get a bounding box of all the BP entities, and how to find the center of where the BP will be placed. If helpful, feel free to use them freely in flib.