sutratman/mods/stm/build_order.lua

114 lines
3.4 KiB
Lua

--- Represents a thing to be built.
BuildOrder = serializable.define('BuildOrder', function()
return {
pos = nil,
total = 0,
remaining = 0,
min = nil,
max = nil,
spec = {}
}
end)
-- wait N standard realtime seconds before a task can be reclaimed
local TASK_TIMEOUT = 3 * stm.TIME_SCALE
--- Creates a build order from a schematic function.
-- @param bounding rectangle's min point
-- @param rectangle's max point
-- @param spec function, table, or string describing the nodes to be set
-- during construction.
-- If `spec` is a string it must be a filename relative to /schematics/.
-- If the file ends in .lua, the file is evaluated with dofile and its result
-- is expected to be one of the other supported spec types (a function or
-- table).
--
-- If `spec` is a table it is assumed to have values of the following form,
-- specifying positions and node names for the build order:
--
-- { pos = <vector>, name = <string> }
--
-- If `spec` is a function, it must take arguments of the form `(min, max)`, where:
--
-- - `min` is the minimum position of the build order in world coords
-- - `max` is the maximum position of the build order in world coords
-- - the function is expected to return a table as described above
-- @return the newly created BuildOrder
function BuildOrder.create(min, max, spec)
local result = BuildOrder.new()
if type(spec) == 'string' then
spec = dofile(stm.base_path .. "/schematics/" .. spec)
end
if type(spec) == 'function' then
spec = spec(min,max)
end
result.min = min
result.max = max
-- TODO: truncate spec to given min/max extents?
for k,v in pairs(spec) do
result:push(v.pos, v.name)
end
return result
end
--- Add a block with the given `name` at `pos`
function BuildOrder:push(pos, name)
self.total = self.total + 1
self.remaining = self.remaining + 1
local id = stm.get_uuid()
self.spec[id] = { pos = pos, name = name, id = id }
end
--- Takes the unclaimed task closest to pos
function BuildOrder:take_task(pos)
local free_tasks = { }
local best_dist = math.huge
local best_task = nil
for k,task in pairs(self.spec) do
if not task.taken or stm.data.time - task.taken > TASK_TIMEOUT then
local dist = vector.subtract(pos, task.pos)
local dist_squared = dist.x * dist.x + dist.z * dist.z
if dist_squared < best_dist then
best_dist = dist_squared
best_task = task
end
end
end
if not best_task then return end
best_task.taken = stm.data.time
return best_task
end
--- Mark a given task as complete
function BuildOrder:complete_task(id)
self.remaining = self.remaining - 1
self.spec[id] = nil
end
--- Returns true if the entire build order is complete
function BuildOrder:is_complete()
return self.remaining == 0
end
--- Find all positions adjacent to this build order.
-- The returned positions can be passed to map_data.get_all_surface_pos for
-- pathing to the build site. Only the x/z components are valid in the
-- results, and only one position is returned per peripheral x/z pair.
-- @return a table of adjacent positions
function BuildOrder:find_adjacent_positions()
local results = { }
for x=self.min.x-1,self.max.x+1 do
table.insert(results, vector.new(x, 0, self.min.z))
table.insert(results, vector.new(x, 0, self.max.z))
end
for z=self.min.z,self.max.z do
table.insert(results, vector.new(self.min.x, 0, z))
table.insert(results, vector.new(self.max.x, 0, z))
end
return results
end