174 lines
5.0 KiB
Lua

local function direction_to_vector(direction)
if direction == defines.direction.north then
return { x = 0, y = -1 }
end
if direction == defines.direction.northeast then
return { x = 1, y = -1 }
end
if direction == defines.direction.east then
return { x = 1, y = 0 }
end
if direction == defines.direction.southeast then
return { x = 1, y = 1 }
end
if direction == defines.direction.south then
return { x = 0, y = 1 }
end
if direction == defines.direction.southwest then
return { x = -1, y = 1 }
end
if direction == defines.direction.west then
return { x = -1, y = 0 }
end
if direction == defines.direction.northwest then
return { x = -1, y = -1 }
end
return { x = 0, y = -1 } -- north
end
local function vector_to_direction(vector)
if vector.x == 0 and vector.y < 0 then
return defines.direction.north
end
if vector.x > 0 and vector.y < 0 then
return defines.direction.northeast
end
if vector.x > 0 and vector.y == 0 then
return defines.direction.east
end
if vector.x > 0 and vector.y > 0 then
return defines.direction.southeast
end
if vector.x == 0 and vector.y > 0 then
return defines.direction.south
end
if vector.x < 0 and vector.y > 0 then
return defines.direction.southwest
end
if vector.x < 0 and vector.y == 0 then
return defines.direction.west
end
if vector.x < 0 and vector.y < 0 then
return defines.direction.northwest
end
return defines.direction.north
end
local function multiply_matrix_vector(matrix, vector)
return {
x = matrix[1].x * vector.x + matrix[2].x * vector.y,
y = matrix[1].y * vector.x + matrix[2].y * vector.y,
}
end
local function multiply_matrix_matrix(matrix1, matrix2)
return {
multiply_matrix_vector(matrix1, matrix2[1]),
multiply_matrix_vector(matrix1, matrix2[2]),
}
end
return function(blueprint_entities, event)
local cursor_position = event.position
local rotation = event.direction or defines.direction.north
-- first, rotate (and/or flip) the blueprint and compute it's size
local rotated_entities = {}
-- column-major tranformation matrix
local location_tranform = {
{ x = 1, y = 0 },
{ x = 0, y = 1 },
}
if event.flip_horizontal then
location_tranform[1].x = -1
end
if event.flip_vertical then
location_tranform[2].y = -1
end
for _i=2,rotation,2 do
-- multiply by the rotation matrix from the left
local rotation_matrix = {
{ x = 0, y = 1 },
{ x = -1, y = 0 }
}
location_tranform = multiply_matrix_matrix(rotation_matrix, location_tranform)
end
local minX = 1000000000
local minY = 1000000000
local maxX = -1000000000
local maxY = -1000000000
for _,entity in pairs(blueprint_entities) do
local direction = defines.direction.north
if game.entity_prototypes[entity.name].supports_direction then
local direction_vector = direction_to_vector(entity.direction or defines.direction.north)
direction = vector_to_direction(multiply_matrix_vector(location_tranform, direction_vector))
end
local position = multiply_matrix_vector(location_tranform, entity.position)
table.insert(rotated_entities, {
name = entity.name,
position = position,
direction = direction,
items = entity.items
})
local collision_box = game.entity_prototypes[entity.name].collision_box
local width = (collision_box.right_bottom.x - collision_box.left_top.x)
local height = (collision_box.right_bottom.y - collision_box.left_top.y)
if direction == defines.direction.east or direction == defines.direction.west then
local swap = width
width = height
height = swap
end
minX = math.min(minX, math.floor(position.x - width / 2 + 0.5))
minY = math.min(minY, math.floor(position.y - height / 2 + 0.5))
maxX = math.max(maxX, math.floor(position.x + width / 2 + 0.5))
maxY = math.max(maxY, math.floor(position.y + height / 2 + 0.5))
end
local blueprint_width = maxX - minX
local blueprint_height = maxY - minY
-- then, calculate the "align" so that entity pos + aligh = real position
local alignX = math.floor(cursor_position.x - blueprint_width / 2 + 0.5) - minX
local alignY = math.floor(cursor_position.y - blueprint_height / 2 + 0.5) - minY
-- finally, add align to entity positions
local final_entities = {}
for _,entity in pairs(rotated_entities) do
if game.entity_prototypes[entity.name].module_inventory_size then
local modules = {}
for name,count in pairs(entity.items or {}) do
if game.item_prototypes[name].type == "module" then
for _i=1,count,1 do
table.insert(modules, {
valid_for_read = true,
name = name
})
end
end
end
table.insert(final_entities, {
name = entity.name,
position = {
x = entity.position.x + alignX,
y = entity.position.y + alignY,
},
direction = entity.direction,
modules = modules
})
end
end
return final_entities
end