townchest/chest.lua

324 lines
9.6 KiB
Lua
Raw Normal View History

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
--------------------------------------
-- 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-10-17 21:30:53 +02:00
2016-10-17 21:30:53 +02:00
--------------------------------------
-- object definition / constructor
2016-10-17 21:30:53 +02:00
--------------------------------------
townchest.chest.new = function()
2016-11-21 00:33:30 +01:00
local self = {}
--------------------------------------
-- 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
--------------------------------------
-- set_infotext - Update node infotext
--------------------------------------
2016-11-21 00:33:30 +01:00
function self.set_infotext(self, formname)
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
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
--------------------------------------
-- 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
--------------------------------------
-- update informations on formspecs
--------------------------------------
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, {})
end
2016-10-17 21:30:53 +02:00
--------------------------------------
-- Create the task that should be managed by chest
--------------------------------------
2016-11-21 00:33:30 +01:00
function self.set_rawdata(self, taskname)
self.plan = townchest.plan.new(self)
local we = {}
if taskname then
2016-11-21 00:33:30 +01:00
self.info.taskname = taskname
end
2016-11-21 00:33:30 +01:00
if self.info.taskname == "file" then
-- check if file could be read
2016-11-21 00:33:30 +01:00
we = townchest.files.readfile(self.info.filename)
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
return
end
2016-11-21 00:33:30 +01:00
elseif self.info.taskname == "generate" then
if self.info.genblock.variant == 1 then
-- 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
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
-- 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
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
end
end
end
2016-11-21 00:33:30 +01:00
elseif self.info.genblock.variant == 3 then
-- 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
table.insert(we, {x=x,y=y,z=z, name = "default:cobble"})
end
end
end
end
-- build ground level under chest
2016-11-21 00:33:30 +01:00
self.plan.relative.ground_y = 1
-- Build a plate
2016-11-21 00:33:30 +01:00
elseif self.info.genblock.variant == 4 then
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
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
end
2016-10-17 21:30:53 +02:00
end
2016-11-21 00:33:30 +01:00
self.rawdata = we
2016-11-21 00:33:30 +01:00
self:run_async(self.prepare_building_plan_chain)
end
--------------------------------------
-- Call a task semi-async trough minetest.after()
--------------------------------------
2016-11-21 00:33:30 +01:00
function self.run_async(self, func)
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
return
end
if func(chest) then --call the next chain / repeat function call
chest:run_async(func)
end
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
--------------------------------------
-- Async task: create building plan from rawdata
--------------------------------------
2016-11-21 00:33:30 +01:00
function self.prepare_building_plan_chain(self)
local chunksize, lastchunk
-- go trough all file entries
2016-11-21 00:33:30 +01:00
if #self.rawdata > preparing_plan_chunk then
chunksize = preparing_plan_chunk
lastchunk = true
else
2016-11-21 00:33:30 +01:00
chunksize = #self.rawdata
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
-- map to the internal node format
2016-11-21 00:33:30 +01:00
local wenode = self.rawdata[i]
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
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)
end
end
2016-11-21 00:33:30 +01:00
self.rawdata[i] = nil
end
2016-10-17 21:30:53 +02: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")
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)
return false
end
2016-10-17 21:30:53 +02:00
end
--------------------------------------
-- Async task: Post-processing of plan preparation
--------------------------------------
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
end
--------------------------------------
-- 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
return
end
dprint("Instant build is running")
2016-11-21 00:33:30 +01:00
local startingnode = self.plan:get_nodes(1)
-- 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
--- 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
return true
else
2016-11-21 00:33:30 +01:00
-- finished. disable processing
self.instantbuild = false
return false
end
2016-10-17 21:30:53 +02:00
end
--------------------------------------
-- 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
return false
2016-11-21 00:33:30 +01:00
else
return self.info.npc_build
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
--------------------------------------
-- 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
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")
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