2017-01-07 23:27:44 +01:00
|
|
|
local dprint = townchest.dprint_off --debug
|
2016-11-03 19:34:27 +01:00
|
|
|
local smartfs = townchest.smartfs
|
2016-10-17 21:30:53 +02:00
|
|
|
|
2017-01-07 23:27:44 +01:00
|
|
|
local ASYNC_WAIT=0.2 -- schould be > 0 to restrict performance consumption
|
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
|
2017-01-07 23:27:44 +01:00
|
|
|
infotext = "Nodes in plan: "..self.plan.data.nodecount
|
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)
|
2017-01-07 23:27:44 +01:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
self.plan = townchest.plan.new(self)
|
2016-11-20 22:05:58 +01:00
|
|
|
|
|
|
|
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
|
2017-01-07 23:27:44 +01:00
|
|
|
if not self.info.filename then
|
|
|
|
-- something wrong, back to file selection
|
|
|
|
minetest.after(0, self.set_form, self, "file_open")
|
|
|
|
self.current_stage = "select"
|
|
|
|
self:persist_info()
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
self.plan.data = townchest.files.readfile(self.info.filename)
|
|
|
|
|
|
|
|
if not self.plan.data 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
|
2017-01-07 23:27:44 +01:00
|
|
|
self.plan.data = {}
|
|
|
|
self.plan.data.min_pos = { x=1, y=1, z=1 }
|
|
|
|
self.plan.data.max_pos = { x=self.info.genblock.x, y=self.info.genblock.y, z=self.info.genblock.z}
|
|
|
|
self.plan.data.nodecount = 0
|
|
|
|
self.plan.data.ground_y = 0
|
|
|
|
self.plan.data.nodenames = {}
|
|
|
|
self.plan.data.scm_data_cache = {}
|
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
if self.info.genblock.variant == 1 then
|
2017-01-07 23:27:44 +01:00
|
|
|
-- nothing special, just let fill them with air
|
2016-11-21 00:33:30 +01:00
|
|
|
elseif self.info.genblock.variant == 2 then
|
2017-01-07 23:27:44 +01:00
|
|
|
table.insert(self.plan.data.nodenames, "default:cobble") -- index 1
|
2016-11-20 22:05:58 +01:00
|
|
|
-- Fill with stone
|
2017-01-07 23:27:44 +01:00
|
|
|
for x = 1, self.info.genblock.x do
|
|
|
|
for y = 1, self.info.genblock.y do
|
|
|
|
for z = 1, self.info.genblock.z do
|
|
|
|
self.plan:add_node({x=x,y=y,z=z, name_id = 1})
|
2016-11-13 01:30:04 +01:00
|
|
|
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
|
2017-01-07 23:27:44 +01:00
|
|
|
table.insert(self.plan.data.nodenames, "default:cobble") -- index 1
|
|
|
|
for x = 1, self.info.genblock.x do
|
|
|
|
for y = 1, self.info.genblock.y do
|
|
|
|
for z = 1, self.info.genblock.z do
|
|
|
|
if x == 1 or x == self.info.genblock.x or
|
|
|
|
y == 1 or y == self.info.genblock.y or
|
|
|
|
z == 1 or z == self.info.genblock.z then
|
|
|
|
self.plan:add_node({x=x,y=y,z=z, name_id = 1})
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-11-20 22:05:58 +01:00
|
|
|
|
|
|
|
-- build ground level under chest
|
2017-01-07 23:27:44 +01:00
|
|
|
self.plan.data.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
|
2017-01-07 23:27:44 +01:00
|
|
|
table.insert(self.plan.data.nodenames, "default:cobble") -- index 1
|
|
|
|
local y = self.plan.data.min_pos.y
|
|
|
|
self.plan.data.max_pos.y = self.plan.data.min_pos.y
|
|
|
|
for x = 1, self.info.genblock.x do
|
|
|
|
for z = 1, self.info.genblock.z do
|
|
|
|
self.plan:add_node({x=x,y=y,z=z, name_id = 1})
|
2016-11-20 22:05:58 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
-- build ground level under chest
|
2017-01-07 23:27:44 +01:00
|
|
|
self.plan.data.ground_y = 1
|
2016-11-13 01:30:04 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
|
2017-01-07 23:27:44 +01:00
|
|
|
-- TODO: go to customizing screen
|
|
|
|
self.infotext = "Build preparation"
|
|
|
|
self:set_form("status")
|
|
|
|
self:run_async(self.prepare_building_plan)
|
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()
|
2017-01-07 23:27:44 +01:00
|
|
|
minetest.after(ASYNC_WAIT, 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: Post-processing of plan preparation
|
2016-11-07 23:43:36 +01:00
|
|
|
--------------------------------------
|
2017-01-07 23:27:44 +01:00
|
|
|
function self.prepare_building_plan(self)
|
|
|
|
self.plan:flood_with_air()
|
|
|
|
|
|
|
|
-- self.plan:do_mapping() -- on demand called
|
2016-11-21 00:33:30 +01:00
|
|
|
self.current_stage = "ready"
|
|
|
|
self:set_form("build_status")
|
2017-01-07 23:27:44 +01:00
|
|
|
self:run_async(self.instant_build_chunk) --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
|
|
|
|
--------------------------------------
|
2017-01-07 23:27:44 +01:00
|
|
|
function self.instant_build_chunk(self)
|
2016-11-21 00:33:30 +01:00
|
|
|
if not self.info.instantbuild == true then --instantbuild disabled
|
2016-11-07 23:43:36 +01:00
|
|
|
return
|
|
|
|
end
|
2017-01-07 23:27:44 +01:00
|
|
|
dprint("--- Instant build is running")
|
|
|
|
|
|
|
|
local startingpos = self.plan:get_random_node_pos()
|
|
|
|
if not startingpos then
|
|
|
|
self.info.instantbuild = false
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
local chunk_pos = self.plan:get_world_pos(startingpos)
|
|
|
|
dprint("---build chunk", minetest.pos_to_string(startingpos))
|
|
|
|
|
|
|
|
-- TODO: in customizing switchable implementation
|
|
|
|
--[[ --- implementation with VoxelArea - bad gameplay responsivity :( - back to per-node update
|
|
|
|
-- work on VoxelArea
|
|
|
|
local vm = minetest.get_voxel_manip()
|
|
|
|
local minp, maxp = vm:read_from_map(chunk_pos, chunk_pos)
|
|
|
|
local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
|
|
|
|
local data = vm:get_data()
|
|
|
|
local param2_data = vm:get_param2_data()
|
|
|
|
local light_fix = {}
|
|
|
|
local meta_fix = {}
|
|
|
|
-- for idx in a:iterp(vector.add(minp, 8), vector.subtract(maxp, 8)) do -- do not touch for beter light update
|
|
|
|
for idx, origdata in pairs(data) do -- do not touch for beter light update
|
|
|
|
local wpos = a:position(idx)
|
|
|
|
local pos = self.plan:get_plan_pos(wpos)
|
|
|
|
if wpos.x ~= self.pos.x or wpos.y ~= self.pos.y or wpos.z ~= self.pos.z then --skip chest pos
|
|
|
|
local node = self.plan:prepare_node_for_build(pos, wpos)
|
|
|
|
if node and node.content_id then
|
|
|
|
-- write to voxel
|
|
|
|
data[idx] = node.content_id
|
|
|
|
param2_data[idx] = node.param2
|
|
|
|
|
|
|
|
-- mark for light update
|
|
|
|
assert(node.node_def, dump(node))
|
|
|
|
if node.node_def.light_source and node.node_def.light_source > 0 then
|
|
|
|
table.insert(light_fix, {pos = wpos, node = node})
|
|
|
|
end
|
|
|
|
if node.meta then
|
|
|
|
table.insert(meta_fix, {pos = wpos, node = node})
|
|
|
|
end
|
|
|
|
self.plan:remove_node(node)
|
|
|
|
--TODO: metadata
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self.plan:remove_node(pos) --if exists
|
|
|
|
end
|
|
|
|
|
|
|
|
-- store the changed map data
|
|
|
|
vm:set_data(data)
|
|
|
|
vm:set_param2_data(param2_data)
|
|
|
|
vm:calc_lighting()
|
|
|
|
vm:update_liquids()
|
|
|
|
vm:write_to_map()
|
|
|
|
vm:update_map()
|
|
|
|
|
|
|
|
-- fix the lights
|
|
|
|
dprint("fix lights", #light_fix)
|
|
|
|
for _, fix in ipairs(light_fix) do
|
|
|
|
minetest.env:add_node(fix.pos, fix.node)
|
|
|
|
end
|
|
|
|
|
|
|
|
dprint("process meta", #meta_fix)
|
|
|
|
for _, fix in ipairs(meta_fix) do
|
|
|
|
minetest.env:get_meta(fix.pos):from_table(fix.node.meta)
|
|
|
|
end
|
|
|
|
]]
|
|
|
|
|
|
|
|
-- implementation using usual "add_node"
|
|
|
|
local chunk_nodes = self.plan:get_nodes_for_chunk(self.plan:get_plan_pos(chunk_pos))
|
|
|
|
dprint("Instant build of chunk: nodes:", #chunk_nodes)
|
|
|
|
for idx, nodeplan in ipairs(chunk_nodes) do
|
2017-01-08 02:47:33 +01:00
|
|
|
if nodeplan.wpos.x ~= self.pos.x or nodeplan.wpos.y ~= self.pos.y or nodeplan.wpos.z ~= self.pos.z then --skip chest pos
|
|
|
|
if nodeplan.node then
|
|
|
|
minetest.env:add_node(nodeplan.wpos, nodeplan.node)
|
|
|
|
if nodeplan.node.meta then
|
|
|
|
minetest.env:get_meta(nodeplan.wpos):from_table(nodeplan.node.meta)
|
2016-11-07 23:43:36 +01:00
|
|
|
end
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
|
|
|
end
|
2017-01-08 02:47:33 +01:00
|
|
|
self.plan:remove_node(nodeplan.pos)
|
2016-10-17 21:30:53 +02:00
|
|
|
end
|
2017-01-07 23:27:44 +01:00
|
|
|
|
|
|
|
-- chunk done handle next chunk call
|
|
|
|
dprint("instant nodes left:", self.plan.data.nodecount)
|
2016-11-21 00:33:30 +01:00
|
|
|
self:update_info("build_status")
|
2017-01-07 23:27:44 +01:00
|
|
|
if self.plan.data.nodecount > 0 then
|
2016-11-21 00:33:30 +01:00
|
|
|
--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
|
2017-01-07 23:27:44 +01:00
|
|
|
self.info.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
|
2017-01-07 23:27:44 +01:00
|
|
|
|
2016-11-21 00:33:30 +01:00
|
|
|
if chestinfo.taskname then -- file selected but no plan. Restore the plan
|
|
|
|
self.current_stage = "restore"
|
2017-01-07 23:27:44 +01:00
|
|
|
self:persist_info()
|
2016-11-21 00:33:30 +01:00
|
|
|
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
|