clean up buildorder.create interface

master
kaen 2016-08-20 18:56:58 -07:00
parent 9f6237edf1
commit 0947d91ae8
9 changed files with 95 additions and 47 deletions

View File

@ -6,7 +6,7 @@ test:
lua ./unittest/run.lua
doc:
ldoc -f markdown -a mods/vxl/
ldoc -f markdown -a mods/stm/
check:
luacheck mods/vxl/
luacheck mods/stm/

View File

@ -14,24 +14,41 @@ end)
local TASK_TIMEOUT = 3 * stm.TIME_SCALE
--- Creates a build order from a schematic function.
-- @param min vector for the bounding rectangle's min point
-- @param max vector for the bounding rectangle's max point
-- @param fn callback function taking arguments `(x,y,z)` and returning a
-- string such as "default:dirt" or "air"
-- @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, fn)
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
for x = min.x,max.x do
for y = min.y,max.y do
for z = min.z,max.z do
local name = fn(x,y,z)
if name then
result:push(vector.new(x,y,z), name)
end
end
end
-- 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

View File

@ -51,6 +51,7 @@ function Character:simulate(dt)
if not ok then
print('error performing task', err)
stm.dump(self.tasks)
assert(false)
end
self:update_physics(dt)
end

View File

@ -0,0 +1,8 @@
return function(min,max)
local build_fn = function(x,y,z)
if y >= min.y then return 'air' end
return 'default:dirt'
end
return stm.walk_aabb(min, max, build_fn)
end

View File

@ -0,0 +1,23 @@
return function(min,max)
local build_fn = function(x,y,z)
if x == min.x or x == max.x or
y == max.y or
z == min.z or z == max.z
then
return "air"
elseif
x == min.x+1 or x == max.x-1 or
y == max.y-1 or
z == min.z+1 or z == max.z-1
then
local midx = math.floor(min.x + (max.x - min.x)*.5)
local midz = math.floor(min.z + (max.z - min.z)*.5)
if x == midx and z > midz and y <= min.y + 1 then
return "air"
end
return "default:stone"
end
return "air"
end
return stm.walk_aabb(min, max, build_fn)
end

View File

@ -32,18 +32,13 @@ return {
-- found a good spot
local loc = Location.new({ type = Location.TYPE_MUNICIPALITY })
loc.pos = MapData.get_surface_pos(pos)
loc.pos = vector.new(pos.x, y_min, pos.z)
loc.min = vector.new(pos.x - size, y_min, pos.z - size)
loc.max = vector.new(pos.x + size, y_max, pos.z + size)
Location.register(loc)
local build_fn = function(x,y,z)
if y >= loc.pos.y then return 'air' end
return 'default:dirt'
end
-- create initial build orders for this location
local order = BuildOrder.create(loc.min, loc.max, build_fn)
local order = BuildOrder.create(loc.min, loc.max, 'flatland.lua')
BuildOrder.register(order)
loc:add_order(order)

View File

@ -23,10 +23,6 @@ return {
end
char.municipality = loc.id
char:push_task('move', { dest = loc:get_position(), distance = 5 })
state.state = MOVE
elseif state.state == MOVE then
state.state = REQUEST_LOCATION
elseif state.state == REQUEST_LOCATION then
@ -43,29 +39,11 @@ return {
if not residence then return false end
residence.type = Location.TYPE_RESIDENCE
char.residence = residence.id
char:push_task('move', { dest = residence:get_position() })
state.state = ENQUEUE_RESIDENCE
elseif state.state == ENQUEUE_RESIDENCE then
local residence = Location.get(char.residence)
local order = BuildOrder.create(residence.min, residence.max, function(x,y,z)
if x == residence.min.x or x == residence.max.x or
y == residence.max.y or
z == residence.min.z or z == residence.max.z
then
return "air"
elseif
x == residence.min.x+1 or x == residence.max.x-1 or
y == residence.max.y-1 or
z == residence.min.z+1 or z == residence.max.z-1
then
if x == residence.pos.x and z > residence.pos.z and y <= residence.min.y + 1 then
return "air"
end
return "default:stone"
end
return "air"
end)
local order = BuildOrder.create(residence.min, residence.max, 'house.lua')
BuildOrder.register(order)
char:push_task("build_lazily", { order = order.id })

View File

@ -126,4 +126,30 @@ function stm.count_pairs(t)
return result
end
--- Iterates over all integer positions between `a` and `b`, applying `fn`
-- @param a min extents of the aabb
-- @param b max extents of the aabb
-- @param fn callback function taking arguments `(x,y,z)` and returning a string
-- @return an array of tables where each element has the form `{ pos = <vector>, name = <result for that position> }`
function stm.walk_aabb(a,b,fn)
local result = { }
for x = math.min(a.x,b.x),math.max(a.x,b.x),1 do
for y = math.min(a.y,b.y),math.max(a.y,b.y),1 do
for z = math.min(a.z,b.z),math.max(a.z,b.z),1 do
local name = fn(x,y,z)
if name then
table.insert(result, { pos = vector.new(x,y,z), name = name })
end
end
end
end
return result
end
if minetest then
stm.base_path = minetest.get_modpath('stm')
else
stm.base_path = './mods/stm/'
end
math.randomseed(os.time())

View File

@ -101,5 +101,5 @@ function TestCharacter:testGravity()
c:simulate(step)
end
assert(math.abs(c.pos.y - 0.5) < 0.0001)
assert(math.abs(c.pos.y - 0.5) < 0.015)
end