2016-11-21 00:33:30 +01:00
|
|
|
local dprint = townchest.dprint --debug
|
2016-11-03 19:34:27 +01:00
|
|
|
local smartfs = townchest.smartfs
|
2016-10-17 21:30:53 +02:00
|
|
|
|
|
|
|
local preparing_plan_chunk = 10000
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- class attributes and methods
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
townchest.chest = {
|
|
|
|
-- chest list
|
|
|
|
list = {},
|
|
|
|
-- get current chest
|
|
|
|
get = function(pos)
|
|
|
|
local key = minetest.pos_to_string(pos)
|
|
|
|
local self = nil
|
|
|
|
if townchest.chest.list[key] then
|
|
|
|
self = townchest.chest.list[key]
|
|
|
|
else
|
|
|
|
self = townchest.chest.new()
|
|
|
|
self.key = key
|
|
|
|
self.pos = pos
|
|
|
|
self.meta = minetest.env:get_meta(pos) --just pointer
|
|
|
|
townchest.chest.list[key] = self
|
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
-- update chest info
|
|
|
|
self.info = minetest.deserialize(self.meta:get_string("chestinfo")) --get add info
|
|
|
|
if not self.info then
|
|
|
|
self.info = {}
|
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
return self
|
|
|
|
end,
|
|
|
|
-- create - initial cleaned up chest after is placed
|
|
|
|
create = function(pos)
|
|
|
|
local key = minetest.pos_to_string(pos)
|
|
|
|
dprint("clean key", key)
|
|
|
|
townchest.chest.list[key] = nil --delete old reference
|
|
|
|
local self = townchest.chest.get(pos)
|
|
|
|
self.info = nil
|
|
|
|
dprint("cleaned chest object", self)
|
|
|
|
return self
|
|
|
|
end,
|
2016-11-07 23:43:36 +01:00
|
|
|
}
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2016-11-13 01:30:04 +01:00
|
|
|
|
2016-10-17 21:30:53 +02:00
|
|
|
--------------------------------------
|
2016-11-07 23:43:36 +01:00
|
|
|
-- object definition / constructor
|
2016-10-17 21:30:53 +02:00
|
|
|
--------------------------------------
|
2016-11-07 23:43:36 +01:00
|
|
|
townchest.chest.new = function()
|
2016-11-21 00:33:30 +01:00
|
|
|
local self = {}
|
2016-11-13 01:30:04 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- save persistant chest info to the chest metadata
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.persist_info(self) -- the read info is in get method
|
|
|
|
self.meta:set_string("chestinfo", minetest.serialize(self.info))
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- set_infotext - Update node infotext
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.set_infotext(self, formname)
|
2016-11-07 23:43:36 +01:00
|
|
|
local infotext
|
|
|
|
if formname == "file_open" then
|
|
|
|
infotext = "please select a building"
|
|
|
|
elseif formname == "build_status" then
|
2016-11-21 00:33:30 +01:00
|
|
|
infotext = "Nodes in plan: "..self.plan.building_size
|
2016-11-07 23:43:36 +01:00
|
|
|
else
|
2016-11-21 00:33:30 +01:00
|
|
|
infotext = self.infotext or ""
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
2016-11-21 00:33:30 +01:00
|
|
|
self.meta:set_string("infotext", infotext)
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- set_form - set formspec to specific widget
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.set_form(self, formname)
|
|
|
|
self:set_infotext(formname)
|
|
|
|
self:persist_info() -- the form read data from persistance handler
|
|
|
|
smartfs.get(formname):attach_to_node(self.pos)
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-13 01:30:04 +01:00
|
|
|
-- update informations on formspecs
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.update_info(self, formname)
|
|
|
|
self:set_infotext(formname)
|
|
|
|
self:persist_info()
|
|
|
|
-- send no data, but triger onInput
|
|
|
|
smartfs.nodemeta_on_receive_fields(self.pos, formname, {})
|
2016-11-07 23:43:36 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-13 01:30:04 +01:00
|
|
|
-- Create the task that should be managed by chest
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.set_rawdata(self, taskname)
|
|
|
|
self.plan = townchest.plan.new(self)
|
2016-11-20 22:05:58 +01:00
|
|
|
local we = {}
|
|
|
|
|
|
|
|
if taskname then
|
2016-11-21 00:33:30 +01:00
|
|
|
self.info.taskname = taskname
|
2016-11-20 22:05:58 +01:00
|
|
|
end
|
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
if self.info.taskname == "file" then
|
2016-11-13 01:30:04 +01:00
|
|
|
-- check if file could be read
|
2016-11-21 00:33:30 +01:00
|
|
|
we = townchest.files.readfile(self.info.filename)
|
2016-11-13 01:30:04 +01:00
|
|
|
if not we or #we == 0 then
|
2016-11-21 00:33:30 +01:00
|
|
|
self.infotext = "No building found in ".. self.info.filename
|
|
|
|
self:set_form("status")
|
|
|
|
self.current_stage = "select"
|
|
|
|
self.info.filename = nil
|
|
|
|
self:persist_info()
|
|
|
|
minetest.after(3, self.set_form, self, "file_open") --back to file selection
|
2016-11-13 01:30:04 +01:00
|
|
|
return
|
|
|
|
end
|
2016-11-20 22:05:58 +01:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
elseif self.info.taskname == "generate" then
|
|
|
|
if self.info.genblock.variant == 1 then
|
2016-11-20 22:05:58 +01:00
|
|
|
-- Fill with air
|
2016-11-21 00:33:30 +01:00
|
|
|
for x = 0, self.info.genblock.x-1 do
|
|
|
|
for y = 0, self.info.genblock.y-1 do
|
|
|
|
for z = 0, self.info.genblock.z-1 do
|
2016-11-20 22:05:58 +01:00
|
|
|
table.insert(we, {x=x,y=y,z=z, name = "air"})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-11-21 00:33:30 +01:00
|
|
|
elseif self.info.genblock.variant == 2 then
|
2016-11-20 22:05:58 +01:00
|
|
|
-- Fill with stone
|
2016-11-21 00:33:30 +01:00
|
|
|
for x = 0, self.info.genblock.x-1 do
|
|
|
|
for y = 0, self.info.genblock.y-1 do
|
|
|
|
for z = 0, self.info.genblock.z-1 do
|
2016-11-13 01:30:04 +01:00
|
|
|
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-11-20 22:05:58 +01:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
elseif self.info.genblock.variant == 3 then
|
2016-11-20 22:05:58 +01:00
|
|
|
-- Build a box
|
2016-11-21 00:33:30 +01:00
|
|
|
for x = 0, self.info.genblock.x-1 do
|
|
|
|
for y = 0, self.info.genblock.y-1 do
|
|
|
|
for z = 0, self.info.genblock.z-1 do
|
|
|
|
if x == 0 or x == self.info.genblock.x-1 or
|
|
|
|
y == 0 or y == self.info.genblock.y-1 or
|
|
|
|
z == 0 or z == self.info.genblock.z-1 then
|
2016-11-13 01:30:04 +01:00
|
|
|
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-11-20 22:05:58 +01:00
|
|
|
|
|
|
|
-- build ground level under chest
|
2016-11-21 00:33:30 +01:00
|
|
|
self.plan.relative.ground_y = 1
|
2016-11-20 22:05:58 +01:00
|
|
|
|
|
|
|
-- Build a plate
|
2016-11-21 00:33:30 +01:00
|
|
|
elseif self.info.genblock.variant == 4 then
|
2016-11-20 22:05:58 +01:00
|
|
|
local y = 0
|
2016-11-21 00:33:30 +01:00
|
|
|
for x = 0, self.info.genblock.x-1 do
|
|
|
|
for z = 0, self.info.genblock.z-1 do
|
2016-11-20 22:05:58 +01:00
|
|
|
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
-- build ground level under chest
|
2016-11-21 00:33:30 +01:00
|
|
|
self.plan.relative.ground_y = 1
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
self.rawdata = we
|
2016-11-20 22:05:58 +01:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
self:run_async(self.prepare_building_plan_chain)
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
2016-11-07 23:43:36 +01:00
|
|
|
|
|
|
|
|
2016-11-13 01:30:04 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- Call a task semi-async trough minetest.after()
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.run_async(self, func)
|
2016-11-13 01:30:04 +01:00
|
|
|
local function async_call(pos)
|
|
|
|
local chest = townchest.chest.get(pos)
|
2016-11-21 00:33:30 +01:00
|
|
|
chest.info = minetest.deserialize(chest.meta:get_string("chestinfo")) --get add info
|
|
|
|
if not chest.info then -- chest removed during the load, stop processing
|
|
|
|
townchest.chest.list[chest.key] = nil
|
2016-11-07 23:43:36 +01:00
|
|
|
return
|
|
|
|
end
|
2016-11-13 01:30:04 +01:00
|
|
|
if func(chest) then --call the next chain / repeat function call
|
|
|
|
chest:run_async(func)
|
|
|
|
end
|
2016-11-07 23:43:36 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
self:persist_info()
|
|
|
|
minetest.after(0.2, async_call, self.pos)
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-13 01:30:04 +01:00
|
|
|
-- Async task: create building plan from rawdata
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.prepare_building_plan_chain(self)
|
2016-11-13 01:30:04 +01:00
|
|
|
local chunksize, lastchunk
|
|
|
|
-- go trough all file entries
|
2016-11-21 00:33:30 +01:00
|
|
|
if #self.rawdata > preparing_plan_chunk then
|
2016-11-13 01:30:04 +01:00
|
|
|
chunksize = preparing_plan_chunk
|
|
|
|
lastchunk = true
|
|
|
|
else
|
2016-11-21 00:33:30 +01:00
|
|
|
chunksize = #self.rawdata
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
for i=#self.rawdata, #self.rawdata-chunksize+1, -1 do
|
2016-11-13 01:30:04 +01:00
|
|
|
-- map to the internal node format
|
2016-11-21 00:33:30 +01:00
|
|
|
local wenode = self.rawdata[i]
|
2016-11-13 01:30:04 +01:00
|
|
|
if wenode and wenode.x and wenode.y and wenode.z and wenode.name then
|
2016-11-21 00:33:30 +01:00
|
|
|
self.plan:adjust_flatting_requrement(wenode)
|
|
|
|
local node = townchest.nodes.new(self.rawdata[i]):map() --mapped
|
2016-11-13 01:30:04 +01:00
|
|
|
if node and node.x and node.y and node.z then
|
2016-11-21 00:33:30 +01:00
|
|
|
self.plan:add_node(node)
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
|
|
|
end
|
2016-11-21 00:33:30 +01:00
|
|
|
self.rawdata[i] = nil
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2016-11-13 01:30:04 +01:00
|
|
|
if lastchunk then
|
|
|
|
dprint("next processing chunk")
|
2016-11-21 00:33:30 +01:00
|
|
|
self.infotext = "Preparing, nodes left: "..#self.rawdata
|
|
|
|
self:set_form("status")
|
2016-11-13 01:30:04 +01:00
|
|
|
return true --repeat async call
|
|
|
|
else
|
|
|
|
dprint("reading of building done. Save them to the chest metadata")
|
2016-11-21 00:33:30 +01:00
|
|
|
self.infotext = "Reading done, preparing"
|
|
|
|
self:set_form("status")
|
|
|
|
self:run_async(self.prepare_building_plan_chain_postprocess)
|
2016-11-13 01:30:04 +01:00
|
|
|
return false
|
2016-11-07 23:43:36 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-13 01:30:04 +01:00
|
|
|
-- Async task: Post-processing of plan preparation
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.prepare_building_plan_chain_postprocess(self)
|
|
|
|
self.plan:prepare()
|
|
|
|
self.current_stage = "ready"
|
|
|
|
self:set_form("build_status")
|
|
|
|
self:persist_info()
|
|
|
|
self:run_async(self.instant_build_chain) --just trigger, there is a check if active
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
2016-11-07 23:43:36 +01:00
|
|
|
|
2016-11-13 01:30:04 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- Async Task: Do a instant build step
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.instant_build_chain(self)
|
|
|
|
if not self.info.instantbuild == true then --instantbuild disabled
|
2016-11-07 23:43:36 +01:00
|
|
|
return
|
|
|
|
end
|
|
|
|
dprint("Instant build is running")
|
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
local startingnode = self.plan:get_nodes(1)
|
2016-11-07 23:43:36 +01:00
|
|
|
-- go trough all file entries
|
|
|
|
if startingnode[1] then -- the one node given
|
2016-11-21 00:33:30 +01:00
|
|
|
dprint("start building chunk for", minetest.pos_to_string(startingnode[1]))
|
|
|
|
minetest.forceload_block(self.plan:get_world_pos(startingnode[1]))
|
|
|
|
for idx, node in ipairs(self.plan:get_nodes_from_chunk(startingnode[1])) do
|
|
|
|
local wpos = self.plan:get_world_pos(node)
|
|
|
|
if wpos.x ~= self.pos.x or wpos.y ~= self.pos.y or wpos.z ~= self.pos.z then --skip chest pos
|
2016-11-07 23:43:36 +01:00
|
|
|
--- Place node
|
|
|
|
minetest.env:add_node(wpos, node)
|
|
|
|
if node.meta then
|
|
|
|
minetest.env:get_meta(wpos):from_table(node.meta)
|
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
2016-11-21 00:33:30 +01:00
|
|
|
self.plan:set_node_processed(node)
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
2016-11-21 00:33:30 +01:00
|
|
|
minetest.forceload_free_block(self.plan:get_world_pos(startingnode[1]))
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
2016-11-21 00:33:30 +01:00
|
|
|
self:update_info("build_status")
|
|
|
|
if self.plan.building_size > 0 then
|
|
|
|
--start next plan chain
|
2016-11-13 01:30:04 +01:00
|
|
|
return true
|
2016-11-07 23:43:36 +01:00
|
|
|
else
|
2016-11-21 00:33:30 +01:00
|
|
|
-- finished. disable processing
|
|
|
|
self.instantbuild = false
|
2016-11-13 01:30:04 +01:00
|
|
|
return false
|
2016-11-07 23:43:36 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- Check if the chest is ready to build something
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.npc_build_allowed(self)
|
|
|
|
if self.current_stage ~= "ready" then
|
2016-11-07 23:43:36 +01:00
|
|
|
return false
|
2016-11-21 00:33:30 +01:00
|
|
|
else
|
|
|
|
return self.info.npc_build
|
2016-11-07 23:43:36 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.update_statistics(self)
|
|
|
|
if self.current_stage == "ready" then --update building status in case of ready (or build in process after ready)
|
|
|
|
self:update_info("build_status")
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
|
|
|
-- restore chest state after shutdown (and maybe suspend if implemented)
|
|
|
|
--------------------------------------
|
2016-11-21 00:33:30 +01:00
|
|
|
function self.restore(self)
|
|
|
|
local chestinfo = minetest.deserialize(self.meta:get_string("chestinfo")) --get add info
|
2016-11-07 23:43:36 +01:00
|
|
|
if not chestinfo then
|
|
|
|
dprint("no chestinfo - asume the chest is removed")
|
|
|
|
return -- chest removed during the load
|
|
|
|
end
|
|
|
|
dprint("restoral info", dump(chestinfo))
|
2016-11-21 00:33:30 +01:00
|
|
|
if self.current_stage then
|
|
|
|
dprint("restoral not necessary, current stage is", self.current_stage)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if chestinfo.taskname then -- file selected but no plan. Restore the plan
|
|
|
|
self.current_stage = "restore"
|
|
|
|
self:set_rawdata(chestinfo.taskname)
|
|
|
|
else
|
|
|
|
self:set_form("file_open")
|
2016-11-07 23:43:36 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- retrun the chest object in townchest.chest.new()
|
2016-11-21 00:33:30 +01:00
|
|
|
return self
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|