diff --git a/README.md b/README.md index 89869bd..4f71482 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,8 @@ License: LGPLv2 - plan_obj.data.groundnode_count - count of nodes found for ground_y determination (internal) - plan_obj.data.min_pos - minimal {x,y,z} vector - plan_obj.data.max_pos - maximal {x,y,z} vector + - plan_obj.facedir - Plan rotation - x+ axis supported only (values 0-3) + - plan_obj.mirrored - (bool) Mirrored build - mirror to z-axis. Note: if you need x-axis mirror - just rotate the building by 2 in addition ## Node object ### class-methods @@ -83,6 +85,7 @@ License: LGPLv2 - node_obj:remove_from_plan() - remove this node from plan - node_obj:get_under() - returns the node under this one if exists in plan - node_obj:get_above() - returns the node above this one if exists in plan + - node_obj:rotate_facedir(facedir) - rotate the node - is internally used for plan rotation in get_mapped() - supported 0-3 (x+ axis) only ### object-attributes - node_obj.name - original node name without mapping diff --git a/mapping.lua b/mapping.lua index 26ce634..1e4df3f 100644 --- a/mapping.lua +++ b/mapping.lua @@ -8,114 +8,144 @@ local mapping = {} mapping.c_free_item = "default:cloud" ----------------------------------------------- --- door compatibility. Seems the old doors was facedir and now the wallmounted values should be used +-- door compatibility. Seems the old doors was facedir and now the wallmounted values should be used (custom_function) ----------------------------------------------- -local function __param2_wallmounted_to_facedir(nodeinfo, pos, wpos) - if nodeinfo.param2 == 0 then -- +y? - nodeinfo.param2 = 0 - elseif nodeinfo.param2 == 1 then -- -y? - nodeinfo.param2 = 1 - elseif nodeinfo.param2 == 2 then --unsure - nodeinfo.param2 = 3 - elseif nodeinfo.param2 == 3 then --unsure - nodeinfo.param2 = 1 - elseif nodeinfo.param2 == 4 then --unsure - nodeinfo.param2 = 2 - elseif nodeinfo.param2 == 5 then --unsure - nodeinfo.param2 = 0 +local function __param2_wallmounted_to_facedir(mapped, node) + if mapped.param2 == 0 then -- +y? + mapped.param2 = 0 + elseif mapped.param2 == 1 then -- -y? + mapped.param2 = 1 + elseif mapped.param2 == 2 then --unsure + mapped.param2 = 3 + elseif mapped.param2 == 3 then --unsure + mapped.param2 = 1 + elseif mapped.param2 == 4 then --unsure + mapped.param2 = 2 + elseif mapped.param2 == 5 then --unsure + mapped.param2 = 0 end end - -local function __torches_compat(nodeinfo, pos, wpos) - +----------------------------------------------- +-- Torches compatibility (custom_function) +----------------------------------------------- +local function __torches_compat(mapped, node) -- from default:3dtorch-lbm - if nodeinfo.param2 == 0 then - nodeinfo.name = "default:torch_ceiling" - elseif nodeinfo.param2 == 1 then - nodeinfo.name = "default:torch" + if mapped.param2 == 0 then + mapped.name = "default:torch_ceiling" + elseif mapped.param2 == 1 then + mapped.name = "default:torch" else - nodeinfo.name = "default:torch_wall" + mapped.name = "default:torch_wall" end end -local u = {} -local unknown_nodes_data = u --- Fallback nodes replacement of unknown nodes --- Maybe it is beter to use aliases for unknown notes. But anyway -u["xpanes:pane_glass_10"] = { name = "xpanes:pane_10" } -u["xpanes:pane_glass_5"] = { name = "xpanes:pane_5" } -u["beds:bed_top_blue"] = { name = "beds:bed_top" } -u["beds:bed_bottom_blue"] = { name = "beds:bed_bottom" } -u["homedecor:table_lamp_max"] = { name = "homedecor:table_lamp_white_max" } -u["homedecor:refrigerator"] = { name = "homedecor:refrigerator_steel" } +----------------------------------------------- +-- Unknown nodes mapping +----------------------------------------------- +local unknown_nodes_data = { + -- Fallback / Compatibility nodes replacement for ingame unknown nodes + ["xpanes:pane_glass_10"] = { name = "xpanes:pane_10" }, + ["xpanes:pane_glass_5"] = { name = "xpanes:pane_5" }, + ["beds:bed_top_blue"] = { name = "beds:bed_top" }, + ["beds:bed_bottom_blue"] = { name = "beds:bed_bottom" }, -u["ethereal:green_dirt"] = { name = "default:dirt_with_grass" } + ["homedecor:table_lamp_max"] = { name = "homedecor:table_lamp_white_max" }, + ["homedecor:refrigerator"] = { name = "homedecor:refrigerator_steel" }, -u["doors:door_wood_b_c"] = {name = "doors:door_wood_a", {["meta"] = {["fields"] = {["state"] = "1"}}}, custom_function = __param2_wallmounted_to_facedir } --closed -u["doors:door_wood_b_o"] = {name = "doors:door_wood_b", {["meta"] = {["fields"] = {["state"] = "3"}}}, custom_function = __param2_wallmounted_to_facedir } --open -u["doors:door_wood_b_1"] = {name = "doors:door_wood_a", {["meta"] = {["fields"] = {["state"] = "0"}}}} --Left door closed -u["doors:door_wood_b_2"] = {name = "doors:door_wood_b", {["meta"] = {["fields"] = {["state"] = "2"}}}} --right door closed -u["doors:door_wood_a_c"] = {name = "doors:hidden" } -u["doors:door_wood_a_o"] = {name = "doors:hidden" } -u["doors:door_wood_t_1"] = {name = "doors:hidden" } -u["doors:door_wood_t_2"] = {name = "doors:hidden" } + ["ethereal:green_dirt"] = { name = "default:dirt_with_grass" }, -u["doors:door_glass_b_c"] = {name = "doors:door_glass_a", {["meta"] = {["fields"] = {["state"] = "1"}}}, custom_function = __param2_wallmounted_to_facedir } --closed -u["doors:door_glass_b_o"] = {name = "doors:door_glass_b", {["meta"] = {["fields"] = {["state"] = "3"}}}, custom_function = __param2_wallmounted_to_facedir } --open -u["doors:door_glass_b_1"] = {name = "doors:door_glass_a", {["meta"] = {["fields"] = {["state"] = "0"}}}} --Left door closed -u["doors:door_glass_b_2"] = {name = "doors:door_glass_b", {["meta"] = {["fields"] = {["state"] = "2"}}}} --right door closed -u["doors:door_glass_a_c"] = {name = "doors:hidden" } -u["doors:door_glass_a_o"] = {name = "doors:hidden" } -u["doors:door_glass_t_1"] = {name = "doors:hidden" } -u["doors:door_glass_t_2"] = {name = "doors:hidden" } + ["doors:door_wood_b_c"] = {name = "doors:door_wood_a", meta = {fields = {state = 1}}, custom_function = __param2_wallmounted_to_facedir }, --closed + ["doors:door_wood_b_o"] = {name = "doors:door_wood_b", meta = {fields = {state = 3}}, custom_function = __param2_wallmounted_to_facedir }, --open + ["doors:door_wood_b_1"] = {name = "doors:door_wood_a", meta = {fields = {state = 0}}}, --Left door closed + ["doors:door_wood_b_2"] = {name = "doors:door_wood_b", meta = {fields = {state = 2}}}, --right door closed + ["doors:door_wood_a_c"] = {name = "doors:hidden" }, + ["doors:door_wood_a_o"] = {name = "doors:hidden" }, + ["doors:door_wood_t_1"] = {name = "doors:hidden" }, + ["doors:door_wood_t_2"] = {name = "doors:hidden" }, -u["doors:door_steel_b_c"] = {name = "doors:door_steel_a", {["meta"] = {["fields"] = {["state"] = "1"}}}, custom_function = __param2_wallmounted_to_facedir } --closed -u["doors:door_steel_b_o"] = {name = "doors:door_steel_b", {["meta"] = {["fields"] = {["state"] = "3"}}}, custom_function = __param2_wallmounted_to_facedir } --open -u["doors:door_steel_b_1"] = {name = "doors:door_steel_a", {["meta"] = {["fields"] = {["state"] = "0"}}}} --Left door closed -u["doors:door_steel_b_2"] = {name = "doors:door_steel_b", {["meta"] = {["fields"] = {["state"] = "2"}}}} --right door closed -u["doors:door_steel_a_c"] = {name = "doors:hidden" } -u["doors:door_steel_a_o"] = {name = "doors:hidden" } -u["doors:door_steel_t_1"] = {name = "doors:hidden" } -u["doors:door_steel_t_2"] = {name = "doors:hidden" } + ["doors:door_glass_b_c"] = {name = "doors:door_glass_a", meta = {fields = {state = 1}}, custom_function = __param2_wallmounted_to_facedir }, --closed + ["doors:door_glass_b_o"] = {name = "doors:door_glass_b", meta = {fields = {state = 3}}, custom_function = __param2_wallmounted_to_facedir }, --open + ["doors:door_glass_b_1"] = {name = "doors:door_glass_a", meta = {fields = {state = 0}}}, --Left door closed + ["doors:door_glass_b_2"] = {name = "doors:door_glass_b", meta = {fields = {state = 2}}}, --right door closed + ["doors:door_glass_a_c"] = {name = "doors:hidden" }, + ["doors:door_glass_a_o"] = {name = "doors:hidden" }, + ["doors:door_glass_t_1"] = {name = "doors:hidden" }, + ["doors:door_glass_t_2"] = {name = "doors:hidden" }, -u["fallback"] = {name = "air" } + ["doors:door_steel_b_c"] = {name = "doors:door_steel_a", meta = {fields = {state = 1}}, custom_function = __param2_wallmounted_to_facedir }, --closed + ["doors:door_steel_b_o"] = {name = "doors:door_steel_b", meta = {fields = {state = 3}}, custom_function = __param2_wallmounted_to_facedir }, --open + ["doors:door_steel_b_1"] = {name = "doors:door_steel_a", meta = {fields = {state = 0}}}, --Left door closed + ["doors:door_steel_b_2"] = {name = "doors:door_steel_b", meta = {fields = {state = 2}}}, --right door closed + ["doors:door_steel_a_c"] = {name = "doors:hidden" }, + ["doors:door_steel_a_o"] = {name = "doors:hidden" }, + ["doors:door_steel_t_1"] = {name = "doors:hidden" }, + ["doors:door_steel_t_2"] = {name = "doors:hidden" }, -local c = {} -local default_replacements = c + ["fallback"] = {name = "air" }, +} + +----------------------------------------------- +-- Default Replacements and adjustments +----------------------------------------------- +local default_replacements = { -- "name" and "cost_item" are optional. -- if name is missed it will not be changed --- if cost_item is missed it will be determinated as usual (from changed name) +-- if cost_item is missed it will be determinated as usual (name or drop) -- a crazy sample is: instead of cobble place goldblock, use wood as payment -- c["default:cobble"] = { name = "default:goldblock", cost_item = "default:wood" } -c["beds:bed_top"] = { cost_item = mapping.c_free_item } -- the bottom of the bed is payed, so buld the top for free + ["beds:bed_top"] = { cost_item = mapping.c_free_item }, -- the bottom of the bed is payed, so buld the top for free --- it is hard to get a source in survival, so we use buckets. Note, the bucket is lost after usage by NPC -c["default:lava_source"] = { cost_item = "bucket:bucket_lava" } -c["default:river_water_source"] = { cost_item = "bucket:bucket_river_water" } -c["default:water_source"] = { cost_item = "bucket:bucket_water" } + -- it is hard to get a source in survival, so we use buckets. Note, the bucket is lost after usage by NPC + ["default:lava_source"] = { cost_item = "bucket:bucket_lava" }, + ["default:river_water_source"] = { cost_item = "bucket:bucket_river_water" }, + ["default:water_source"] = { cost_item = "bucket:bucket_water" }, --- does not sense to set flowing water because it flow away without the source (and will be generated trough source) -c["default:water_flowing"] = { name = "air" } -c["default:lava_flowing"] = { name = "air" } -c["default:river_water_flowing"] = { name = "air" } + -- does not sense to set flowing water because it flow away without the source (and will be generated trough source) + ["default:water_flowing"] = { name = "air" }, + ["default:lava_flowing"] = { name = "air" }, + ["default:river_water_flowing"] = { name = "air" }, --- pay different dirt types by the sane dirt -c["default:dirt_with_dry_grass"] = { cost_item = "default:dirt" } -c["default:dirt_with_grass"] = { cost_item = "default:dirt" } -c["default:dirt_with_snow"] = { cost_item = "default:dirt" } + -- pay different dirt types by the sane dirt + ["default:dirt_with_dry_grass"] = { cost_item = "default:dirt" }, + ["default:dirt_with_grass"] = { cost_item = "default:dirt" }, + ["default:dirt_with_snow"] = { cost_item = "default:dirt" }, --- Changed with MTG-0.4.16 -c["xpanes:pane_5"] = { name = "xpanes:pane_flat", param2 = 0 } --unsure -c["xpanes:pane_10"] = { name = "xpanes:pane_flat", param2 = 1 } --unsure + -- Changed with MTG-0.4.16 + ["xpanes:pane_5"] = { name = "xpanes:pane_flat", param2 = 0 }, --unsure + ["xpanes:pane_10"] = { name = "xpanes:pane_flat", param2 = 1 }, --unsure -c["default:torch"] = { custom_function = __torches_compat } -c["torches:wall"] = { name = "default:torch_wall" } + ["default:torch"] = { custom_function = __torches_compat }, + ["torches:wall"] = { name = "default:torch_wall" }, +} ----------------------------------------------- --- copy table of mapping entry +-- Handle doors mirroring (_a vs _b) +----------------------------------------------- +function __mirror_doors(mr) + if not mr.node_def.door then + return + end + local node_name = mr.name + if node_name:sub(-1) == 'a' then + node_name = node_name:sub(1,-2)..'b' + else + node_name = node_name:sub(1,-2)..'a' + end + if minetest.registered_nodes[node_name] then + mr.node_def = minetest.registered_nodes[node_name] + mr.name = node_name + if mr.meta and mr.meta.fields and mr.meta.fields.state then + mr.meta.fields.state = (mr.meta.fields.state + 2) % 4 + end + end +end + + +----------------------------------------------- +-- merge entry ----------------------------------------------- local function merge_map_entry(entry1, entry2) if entry2 then @@ -164,12 +194,11 @@ end ----------------------------------------------- function mapping.map_unknown(name) local map = unknown_nodes_data[name] - if not map or map.name == name then -- no fallback mapping. don't use the node + if not map or map.name == name or not minetest.registered_nodes[map.name] then dprint("mapping failed:", name, dump(map)) print("unknown nodes in building", name) return unknown_nodes_data["fallback"] end - dprint("mapped", name, "to", map.name) return merge_map_entry(map) end @@ -177,35 +206,20 @@ end ----------------------------------------------- -- Take filters and actions on nodes before building ----------------------------------------------- -function mapping.map(name) +function mapping.map(name, plan) -- get mapped registred node name for further mappings + local mr = {name = name} local node_chk = minetest.registered_nodes[name] --do fallback mapping if not registred node if not node_chk then - local fallback = mapping.map_unknown(name) - if fallback then - dprint("map fallback:", dump(fallback)) - local fbmapped = mapping.map(fallback.name) - if fbmapped then - return merge_map_entry(fbmapped, fallback) --merge fallback values into the mapped node - end - end - dprint("unmapped node", name) - return + mr = merge_map_entry(mapping.map_unknown(name), mr) end -- get default replacement local map = default_replacements[name] - local mr -- mapped return table - if not map then - mr = {} - mr.name = name - else - mr = merge_map_entry(map) - if mr.name == nil then - mr.name = name - end + if map then + mr = merge_map_entry(map, mr) end --disabled by mapping @@ -215,6 +229,11 @@ function mapping.map(name) local node_def = minetest.registered_nodes[mr.name] mr.node_def = node_def + + if plan and plan.mirrored then + __mirror_doors(mr) + end + -- determine cost_item if not mr.cost_item then --Check for price or if it is free @@ -246,6 +265,7 @@ end ------------------------------------------ -- Cache some node content ID +------------------------------------------ mapping._protected_content_ids = {} -- this nodes detects other buildings mapping._over_surface_content_ids = {} -- this nodes detects surface mapping._volatile_contend_ids = {} -- this nodes will not be removed before placing new one diff --git a/node.lua b/node.lua index 0b891e0..6019b97 100644 --- a/node.lua +++ b/node.lua @@ -43,6 +43,32 @@ function node_class:get_world_pos() return self._world_pos end +------------------------------------- +-- Handle rotation +-------------------------------------- +function node_class:rotate_facedir(facedir) + -- rotate wallmounted + local mapped = self.mapped + if mapped.node_def.paramtype2 == "wallmounted" then + local param2_dir = mapped.param2 % 8 + local param2_color = mapped.param2 - param2_dir + if self.plan.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.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 -------------------------------------- @@ -53,7 +79,7 @@ function node_class:get_mapped() local mappedinfo = self.nodeinfo.mapped if not mappedinfo then - mappedinfo = mapping.map(self.name) + mappedinfo = mapping.map(self.name, self.plan) self.nodeinfo.mapped = mappedinfo self.mapped = nil end @@ -75,13 +101,15 @@ function node_class:get_mapped() mapped.prob = mapped.prob or self.data.prob if mapped.custom_function ~= nil then - mapped.custom_function(mapped, self._plan_pos, self:get_world_pos()) + 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.facedir) return mapped end @@ -125,5 +153,97 @@ 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} + +--[[ +--- 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 diff --git a/plan.lua b/plan.lua index 8b72d79..30c9fb6 100644 --- a/plan.lua +++ b/plan.lua @@ -26,14 +26,17 @@ function plan.new(plan_id , anchor_pos) self.__index = plan_class self.plan_id = plan_id self.anchor_pos = anchor_pos - self.data = {} - self.data.min_pos = {} - self.data.max_pos = {} - self.data.groundnode_count = 0 - self.data.ground_y = -1 --if nothing defined, it is under the building - self.data.scm_data_cache = {} - self.data.nodeinfos = {} - self.data.nodecount = 0 + self.facedir = 0 + self.mirrored = false + self.data = { + min_pos = {}, + max_pos = {}, + groundnode_count = 0, + ground_y = -1, --if nothing defined, it is under the building + scm_data_cache = {}, + nodeinfos = {}, + nodecount = 0, + } self.status = "new" return self -- the plan object end @@ -220,12 +223,40 @@ end -------------------------------------- --Get world position relative to plan position -------------------------------------- -function plan_class:get_world_pos(pos, anchor_pos) +function plan_class:get_world_pos(plan_pos, anchor_pos) local apos = anchor_pos or self.anchor_pos - return { x=pos.x+apos.x, - y=pos.y+apos.y - self.data.ground_y - 1, - z=pos.z+apos.z - } + local pos + if self.mirrored then + pos = table.copy(plan_pos) + pos.x = -pos.x + else + pos = plan_pos + end + local facedir_rotated = { + [0] = function(pos,apos) return { + x=pos.x+apos.x, + y=pos.y+apos.y, + z=pos.z+apos.z, + }end, + [1] = function(pos,apos) return { + x=pos.z+apos.x, + y=pos.y+apos.y, + z=-pos.x+apos.z, + } end, + [2] = function(pos,apos) return { + x=-pos.x+apos.x, + y=pos.y+apos.y, + z=-pos.z+apos.z, + } end, + [3] = function(pos,apos) return { + x=-pos.z+apos.x, + y=pos.y+apos.y, + z=pos.x+apos.z, + } end, + } + local ret = facedir_rotated[self.facedir](pos, apos) + ret.y = ret.y - self.data.ground_y - 1 + return ret end -------------------------------------- @@ -295,12 +326,37 @@ end -------------------------------------- --Get plan position relative to world position -------------------------------------- -function plan_class:get_plan_pos(pos, anchor_pos) +function plan_class:get_plan_pos(world_pos, anchor_pos) local apos = anchor_pos or self.anchor_pos - return { x=pos.x-apos.x, - y=pos.y-apos.y + self.data.ground_y + 1, + local facedir_rotated = { + [0] = function(pos,apos) return { + x=pos.x-apos.x, + y=pos.y-apos.y, z=pos.z-apos.z - } + } end, + [1] = function(pos,apos) return { + x=-(pos.z-apos.z), + y=pos.y-apos.y, + z=(pos.x-apos.x), + } end, + [2] = function(pos,apos) return { + x=-(pos.x-apos.x), + y=pos.y-apos.y, + z=-(pos.z-apos.z), + } end, + [3] = function(pos,apos) return { + x=pos.z-apos.z, + y=pos.y-apos.y, + z=-(pos.x-apos.x), + } end, + } + local ret = facedir_rotated[self.facedir](world_pos, apos) + + if self.mirrored then + ret.x = -ret.x + end + ret.y = ret.y + self.data.ground_y + 1 + return ret end --------------------------------------