schemlib/node.lua
2018-07-30 22:27:09 +02:00

277 lines
7.8 KiB
Lua

local mapping = schemlib.mapping
--------------------------------------
-- Node class
--------------------------------------
local node_class = {}
local node_class_mt = {__index = node_class}
local node = {}
node.node_class = node_class
-------------------------------------
-- Create new node
--------------------------------------
function node.new(data)
local self = setmetatable({}, node_class_mt)
self.name = data.name
assert(self.name, "No name given for node object")
self.data = {}
-- compat: param2
self.data.param2 = data.param2 or 0
self.data.prob = data.prob
-- metadata is only of intrest if it is not empty
if data.meta then
if (data.meta.fields and next(data.meta.fields)) or
(data.meta.inventory and next(data.meta.inventory)) then
self.data.meta = data.meta
end
end
return self
end
-------------------------------------
-- Get node position in the world
--------------------------------------
function node_class:get_world_pos()
if not self._world_pos then
self._world_pos = self.plan:get_world_pos(self._plan_pos)
end
return self._world_pos
end
-------------------------------------
-- Handle rotation
--------------------------------------
function node_class:rotate_facedir(facedir)
-- rotate wallmounted
local mapped = self.mapped
mapped.param2_plan_rotation = mapped.param2
if mapped.node_def.paramtype2 == "wallmounted" then
local param2_dir = mapped.param2 % 8
local param2_color = mapped.param2 - param2_dir
if self.plan.data.mirrored then
param2_dir = node.rotation_wallmounted_mirrored_map[param2_dir]
end
mapped.param2 = node.rotation_wallmounted_map[facedir][param2_dir] + param2_color
elseif mapped.node_def.paramtype2 == "facedir" then
-- rotate facedir
local param2_dir = mapped.param2 % 32
local param2_color = mapped.param2 - param2_dir
if self.plan.data.mirrored then
param2_dir = node.rotation_facedir_mirrored_map[param2_dir]
end
mapped.param2 = node.rotation_facedir_map[facedir][param2_dir] + param2_color
end
end
-------------------------------------
-- Get all information to build the node
--------------------------------------
function node_class:get_mapped()
if self.mapped == 'unknown' then
return
end
local mappedinfo = self.plan.mapping_cache[self.name]
if not mappedinfo then
mappedinfo = mapping.map(self.name, self.plan)
self.plan.mapping_cache[self.name] = mappedinfo
self.mapped = nil
end
if not mappedinfo or mappedinfo == 'unknown' then
self.plan.mapping_cache[self.name] = 'unknown'
self.mapped = 'unknown'
return
end
if self.mapped then
return self.mapped
end
local mapped = table.copy(mappedinfo)
mapped.name = mapped.name or self.data.name
mapped.param2 = mapped.param2 or self.data.param2
mapped.meta = mapped.meta or self.data.meta
mapped.prob = mapped.prob or self.data.prob
if mapped.custom_function then
mapped.custom_function(mapped, self)
mapped.custom_function = nil
end
mapped.content_id = minetest.get_content_id(mapped.name)
self.mapped = mapped
self.cost_item = mapped.cost_item -- workaround / backwards compatibility to npcf_builder
self:rotate_facedir(self.plan.data.facedir)
return mapped
end
--------------------------------------
-- get node under this one if exists
--------------------------------------
function node_class:get_under()
return self.plan:get_node({x=self._plan_pos.x, y=self._plan_pos.y-1, z=self._plan_pos.z})
end
--------------------------------------
-- get node above this one if exists
--------------------------------------
function node_class:get_above()
return self.plan:get_node({x=self._plan_pos.x, y=self._plan_pos.y+1, z=self._plan_pos.z})
end
--------------------------------------
-- get plan_pos of attached node, or nil if not attached
--------------------------------------
function node_class:get_attached_to()
local mapped = self:get_mapped()
local attached_wallmounted
if mapped.name == "doors:hidden" then
attached_wallmounted = 1 --bellow
elseif mapped.node_def.groups.attached_node then
if mapped.node_def.paramtype2 == "wallmounted" then
attached_wallmounted = mapped.param2_plan_rotation
else
attached_wallmounted = 1 --bellow
end
end
if attached_wallmounted then
local dir = node.wallmounted_to_dir[attached_wallmounted]
return vector.add(self._plan_pos, dir)
end
end
--------------------------------------
-- add/build a node
--------------------------------------
function node_class:place()
local mapped = self:get_mapped()
local world_pos = self:get_world_pos()
if mapped then
minetest.add_node(world_pos, mapped)
if mapped.meta then
minetest.get_meta(world_pos):from_table(mapped.meta)
end
end
if not self.final_node_name then
self:remove_from_plan()
end
end
--------------------------------------
-- Delete node from plan
--------------------------------------
function node_class:remove_from_plan()
self.plan:del_node(self._plan_pos)
end
--------------------------------------
-- Precalculated rotation mapping
--------------------------------------
node.rotation_wallmounted_map = {
[0] = {1,2,3,4,5,[0] = 0},
[1] = {1,5,4,2,3,[0] = 0},
[2] = {1,3,2,5,4,[0] = 0},
[3] = {1,4,5,3,2,[0] = 0},
}
node.rotation_wallmounted_mirrored_map = {1,3,2,4,5,[0] = 0}
node.rotation_facedir_map = {
[0] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,[0] = 0},
[1] = { 2, 3, 0,13,14,15,12,17,18,19,16, 9,10,11, 8, 5, 6, 7, 4,23,20,21,22,[0] = 1},
[2] = { 3, 0, 1,10,11, 8, 9, 6, 7, 4, 5,18,19,16,17,14,15,12,13,22,23,20,21,[0] = 2},
[3] = { 0, 1, 2,19,16,17,18,15,12,13,14, 7, 4, 5, 6,11, 8, 9,10,21,22,23,20,[0] = 3},
}
node.rotation_facedir_mirrored_map = {3, 2, 1, 4, 7, 6, 5, 8, 11,10,9,16,19,18,17,12,15,14,13,20,23,22,21,[0] = 0}
node.wallmounted_to_dir = {
[0] = {x=0,y=1,z=0},
[1] = {x=0,y=-1,z=0},
[2] = {x=1,y=0,z=0},
[3] = {x=-1,y=0,z=0},
[4] = {x=0,y=0,z=1},
[5] = {x=0,y=0,z=-1},
}
--[[
--- Temporary code to calculate the wallmounted map
node.rotation_wallmounted_map = {}
for rotate = 0, 3 do -- facedir
node.rotation_wallmounted_cache[rotate] = {}
for wallmounted = 0, 5 do
local facedir, new_wallmounted
if wallmounted > 1 then
if wallmounted == 2 then
facedir = 1
elseif wallmounted == 3 then
facedir = 3
elseif wallmounted == 4 then
facedir = 0
elseif wallmounted == 5 then
facedir = 2
end
facedir = (facedir + rotate) % 4 --rotate
if facedir == 1 then
new_wallmounted = 2
elseif facedir == 3 then
new_wallmounted = 3
elseif facedir == 0 then
new_wallmounted = 4
elseif facedir == 2 then
new_wallmounted = 5
end
else
new_wallmounted = wallmounted
end
node.rotation_wallmounted_cache[rotate][wallmounted] = new_wallmounted
end
end
print(dump(node.rotation_wallmounted_cache))
]]
--[[
local direction_map = {1, 3, 2, 4}
local direction_map_mirror = {1, 4, 2, 3}
for rotate = 0, 3 do
node.rotation_facedir_map[rotate] = {}
for wal_direction = 0, 5 do
for wal_rotate = 0, 3 do
local oldwal = wal_direction*4+wal_rotate
if wal_direction == 0 then -- y+
new_wal_direction = 0
new_wal_rotate = (wal_rotate + rotate) % 4
elseif wal_direction == 5 then -- y-
new_wal_direction = 5
new_wal_rotate = (4+wal_rotate - rotate) % 4
else
new_wal_direction = direction_map[(direction_map[wal_direction] + rotate-1)%4+1]
new_wal_rotate = (wal_rotate + rotate) % 4
if rotate == 0 then
local new_wal_rotate_mirror = new_wal_rotate
if new_wal_rotate_mirror == 1 then
new_wal_rotate_mirror = 3
elseif new_wal_rotate_mirror == 3 then
new_wal_rotate_mirror = 1
end
local new_wal_direction_mirror = direction_map_mirror[(direction_map[wal_direction] + rotate-1)%4+1]
print(rotate, oldwal, (new_wal_direction_mirror*4+new_wal_rotate_mirror))
end
end
node.rotation_facedir_map[rotate][oldwal] = new_wal_direction*4 + new_wal_rotate
end
end
end
print(dump(node.rotation_facedir_map))
-- ]]
-------------------
return node