71 lines
3.0 KiB
Lua

local abs = math.abs
local sqrt = math.sqrt
local acos = math.acos
local round_up = math.ceil
local full_circle_in_radians = math.pi * 2
local item_size_on_belt = 0.25 -- in tiles
local inserter_angle_precision = 0.001 -- hardcoded game mechanic, or so it seems
-- belt speed in tiles per tick
-- inserters can put items in the vicinity of their drop spot,
-- as long as there is no item center directly beneath them
-- e.g. they can place the second item on the very next tick
-- also, the inserter can put down an item either before or after
-- the belt moves in a given tick (usually before)
-- picking items up is vastly more complex and therefore not implemented
local function get_belt_penalty(belt_speed, stack_size)
local penalty = 0
stack_size = stack_size - 1
local item_center_offset = belt_speed
local acted = true
while stack_size > 0 do
if item_center_offset > 0 then
stack_size = stack_size - 1
item_center_offset = item_center_offset - item_size_on_belt
acted = true
end
item_center_offset = item_center_offset + belt_speed
if not acted and (item_center_offset > 0) then
stack_size = stack_size - 1
item_center_offset = item_center_offset - item_size_on_belt
acted = true
end
penalty = penalty + 1
acted = false
end
return penalty
end
local function calc(rotation_speed, extension_speed, pickup_vector, drop_vector, stack_size, pickup_belt_speed, drop_belt_speed)
local pickup_x, pickup_y = pickup_vector[1], pickup_vector[2]
local drop_x, drop_y = drop_vector[1], drop_vector[2]
local pickup_length = sqrt(pickup_x * pickup_x + pickup_y * pickup_y)
local drop_length = sqrt(drop_x * drop_x + drop_y * drop_y)
-- get angle from the dot product
local angle = 0
if pickup_length > 0 and drop_length > 0 then
angle = acos((pickup_x * drop_x + pickup_y * drop_y) / (pickup_length * drop_length))
end
-- convert it to circles
-- the inserter doesn't have to swing all the way, only within a certain angle of destination
angle = angle / full_circle_in_radians - inserter_angle_precision
-- rotation speed is in circles per tick
local ticks_per_cycle = 2 * round_up(angle / rotation_speed)
local extension_time = 2 * round_up(abs(pickup_length - drop_length) / extension_speed)
if ticks_per_cycle < extension_time then
ticks_per_cycle = extension_time
end
-- inserters can not pick up and put down items in the same tick, so the full cycle is at least two ticks
if ticks_per_cycle < 2 then
ticks_per_cycle = 2
end
if pickup_belt_speed and (stack_size > 1) then
ticks_per_cycle = ticks_per_cycle + get_belt_penalty(pickup_belt_speed, stack_size)
end
if drop_belt_speed and (stack_size > 1) then
ticks_per_cycle = ticks_per_cycle + get_belt_penalty(drop_belt_speed, stack_size)
end
return stack_size * 60 / ticks_per_cycle -- 60 = ticks per second
end
return calc