diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..1592dbb
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,13 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..5b026e1
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,29 @@
+Tower Crane Mod
+===============
+
+
+
+Usage
+-----
+
+1) Place the crane base.
+ The crane arm will later be build in the same direction yoi are currently looking
+
+2) Right-click the crane base node and set the crane dimensions in height and width (between 8 and 24).
+ The crane will be build according to the settings.
+
+3) Right-click the crane switch node to place the hook in front of the crane mast
+
+4) Enter the hook by right-clicking the hook
+
+5) "Fly" within the construction area (height, width) by means of the (default) controls
+ - Move mouse: Look around
+ - W, A, S, D: Move
+ - Space: move up
+ - Shift: move down
+
+6) Leave the hook by right-clicking the hook or right-clicking the crane switch node
+
+7) To destroy the crane, destroy the base node.
+
+
diff --git a/depends.txt b/depends.txt
new file mode 100644
index 0000000..4ad96d5
--- /dev/null
+++ b/depends.txt
@@ -0,0 +1 @@
+default
diff --git a/description.txt b/description.txt
new file mode 100644
index 0000000..000880f
--- /dev/null
+++ b/description.txt
@@ -0,0 +1 @@
+The Tower Crane Mod forms a constrution area below the crane arm which gives the crane owner "fly privs".
diff --git a/init.lua b/init.lua
new file mode 100644
index 0000000..bf94b10
--- /dev/null
+++ b/init.lua
@@ -0,0 +1,525 @@
+--[[
+
+ Tower Crane Mod
+ ===============
+
+ v0.01 by JoSt
+
+ Copyright (C) 2017 Joachim Stolberg
+ LGPLv2.1+
+ See LICENSE.txt for more information
+
+ History:
+ 2017-06-04 v0.01 first version
+]]--
+
+
+towercrane = {}
+
+--##################################################################################################
+--## Tower Crane Hook
+--##################################################################################################
+local hook = {
+ physical = true,
+ collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
+ collide_with_objects = false,
+ visual = "cube",
+ visual_size = {x=0.4, y=0.4},
+ textures = {
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ },
+ groups = {cracky=1},
+ -- local variabels
+ driver = nil,
+ speed_forward=0,
+ speed_right=0,
+ speed_up=0,
+}
+----------------------------------------------------------------------------------------------------
+-- Enter/leave the Hook
+----------------------------------------------------------------------------------------------------
+function hook:on_rightclick(clicker)
+ if self.driver and clicker == self.driver then -- leave?
+ clicker:set_detach()
+ self.driver = nil
+ elseif not self.driver then -- enter?
+ self.driver = clicker
+ clicker:set_attach(self.object, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
+ end
+end
+
+----------------------------------------------------------------------------------------------------
+-- Hook control
+----------------------------------------------------------------------------------------------------
+function hook:on_step(dtime)
+ -- remove hook from last visit
+ if self.pos1 == nil or self.pos2 == nil then
+ self.object:remove()
+ return
+ end
+ if self.driver then
+ local ctrl = self.driver:get_player_control()
+ local yaw = self.driver:get_look_horizontal()
+ local pos = self.driver:getpos()
+ local max_speed = 5
+ local velocity = 0.5
+
+ if ctrl.up then -- forward
+ self.speed_forward = math.min(self.speed_forward + velocity, max_speed)
+ elseif ctrl.down then -- backward
+ self.speed_forward = math.max(self.speed_forward - velocity, -max_speed)
+ elseif self.speed_forward > 0 then
+ self.speed_forward = self.speed_forward - velocity
+ elseif self.speed_forward < 0 then
+ self.speed_forward = self.speed_forward + velocity
+ end
+
+ if ctrl.right then -- right
+ self.speed_right = math.min(self.speed_right + velocity, max_speed)
+ elseif ctrl.left then -- left
+ self.speed_right = math.max(self.speed_right - velocity, -max_speed)
+ elseif self.speed_right > 0 then
+ self.speed_right = self.speed_right - velocity
+ elseif self.speed_right < 0 then
+ self.speed_right = self.speed_right + velocity
+ end
+
+ if ctrl.jump then -- up
+ self.speed_up = math.min(self.speed_up + velocity, 5)
+ elseif ctrl.sneak then -- down
+ self.speed_up = math.max(self.speed_up - velocity, -5)
+ elseif self.speed_up > 0 then
+ self.speed_up = self.speed_up - velocity
+ elseif self.speed_up < 0 then
+ self.speed_up = self.speed_up + velocity
+ end
+
+ -- calculate the direction vector
+ local vx = math.cos(yaw+math.pi/2) * self.speed_forward + math.cos(yaw) * self.speed_right
+ local vz = math.sin(yaw+math.pi/2) * self.speed_forward + math.sin(yaw) * self.speed_right
+
+ -- check if outside of the construction area
+ if pos.x < self.pos1.x then vx= velocity end
+ if pos.x > self.pos2.x then vx= -velocity end
+ if pos.y < self.pos1.y then self.speed_up= velocity end
+ if pos.y > self.pos2.y then self.speed_up= -velocity end
+ if pos.z < self.pos1.z then vz= velocity end
+ if pos.z > self.pos2.z then vz= -velocity end
+
+ self.object:setvelocity({x=vx, y=self.speed_up,z=vz})
+ else
+ self.object:setvelocity({x=0, y=0,z=0})
+ end
+end
+
+----------------------------------------------------------------------------------------------------
+-- LuaEntitySAO (non-player moving things): see http://dev.minetest.net/LuaEntitySAO
+----------------------------------------------------------------------------------------------------
+minetest.register_entity("towercrane:hook", hook)
+
+
+
+--##################################################################################################
+--## Tower Crane
+--##################################################################################################
+
+local function turnright(dir)
+ local facedir = minetest.dir_to_facedir(dir)
+ return minetest.facedir_to_dir((facedir + 1) % 4)
+end
+
+local function turnleft(dir)
+ local facedir = minetest.dir_to_facedir(dir)
+ return minetest.facedir_to_dir((facedir + 3) % 4)
+end
+
+----------------------------------------------------------------------------------------------------
+-- Constuct mast and arm
+----------------------------------------------------------------------------------------------------
+local function construct_crane(pos, dir, height, width, owner)
+ pos.y = pos.y + 1
+ minetest.env:add_node(pos, {name="towercrane:mast_ctrl_off", param2=minetest.dir_to_facedir(dir)})
+ local meta = minetest.get_meta(pos)
+ meta:set_string("dir", minetest.pos_to_string(dir))
+ meta:set_string("owner", owner)
+ meta:set_int("height", height)
+ meta:set_int("width", width)
+
+ for i = 1,height+1 do
+ pos.y = pos.y + 1
+ minetest.env:add_node(pos, {name="towercrane:mast"})
+ end
+
+ pos.y = pos.y - 2
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:add_node(pos, {name="towercrane:arm2"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:add_node(pos, {name="towercrane:arm"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:add_node(pos, {name="towercrane:balance"})
+ pos.x = pos.x + 3 * dir.x
+ pos.z = pos.z + 3 * dir.z
+
+ for i = 1,width do
+ pos.x = pos.x + dir.x
+ pos.z = pos.z + dir.z
+ if i % 2 == 0 then
+ minetest.env:add_node(pos, {name="towercrane:arm2"})
+ else
+ minetest.env:add_node(pos, {name="towercrane:arm"})
+ end
+
+ end
+end
+
+----------------------------------------------------------------------------------------------------
+-- Remove the crane
+----------------------------------------------------------------------------------------------------
+local function dig_crane(pos, dir, height, width)
+ pos.y = pos.y + 1
+ minetest.env:remove_node(pos, {name="towercrane:mast_ctrl_off"})
+
+ for i = 1,height+1 do
+ pos.y = pos.y + 1
+ minetest.env:remove_node(pos, {name="towercrane:mast"})
+ end
+
+ pos.y = pos.y - 2
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:remove_node(pos, {name="towercrane:arm2"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:remove_node(pos, {name="towercrane:arm"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:remove_node(pos, {name="towercrane:balance"})
+ pos.x = pos.x + 3 * dir.x
+ pos.z = pos.z + 3 * dir.z
+
+ for i = 1,width do
+ pos.x = pos.x + dir.x
+ pos.z = pos.z + dir.z
+ if i % 2 == 0 then
+ minetest.env:remove_node(pos, {name="towercrane:arm2"})
+ else
+ minetest.env:remove_node(pos, {name="towercrane:arm"})
+ end
+ end
+end
+
+----------------------------------------------------------------------------------------------------
+-- Place the hook in front of the base
+----------------------------------------------------------------------------------------------------
+local function place_hook(pos, dir)
+ pos.y = pos.y - 1
+ pos.x = pos.x + dir.x
+ pos.z = pos.z + dir.z
+ return minetest.add_entity(pos, "towercrane:hook")
+end
+
+----------------------------------------------------------------------------------------------------
+-- Register Crane base
+----------------------------------------------------------------------------------------------------
+minetest.register_node("towercrane:base", {
+ description = "Tower Crane Base",
+ inventory_image = "towercrane_invent.png",
+ tiles = {
+ "towercrane_base_top.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {cracky=3},
+ formspec = set_formspec,
+
+ -- set meta data (form for crane height and width, dir of the arm)
+ after_place_node = function(pos, placer)
+ local meta = minetest.get_meta(pos)
+ local owner = placer:get_player_name()
+ meta:set_string("owner", owner)
+ local formspec = "size[5,4]"..
+ "label[0,0;Construction area size]" ..
+ "field[1,1.5;3,1;size;height,width;]" ..
+ "button_exit[1,2;2,1;exit;Save]"
+ meta:set_string("formspec", formspec)
+
+ local fdir = minetest.dir_to_facedir(placer:get_look_dir(), false)
+ local dir = minetest.facedir_to_dir(fdir)
+ meta:set_string("dir", minetest.pos_to_string(dir))
+ end,
+
+ -- evaluate user input (height, width), destroyed old crane and build a new one with
+ -- the given size
+ on_receive_fields = function(pos, formname, fields, player)
+ if fields.size == nil then
+ return
+ end
+ local meta = minetest.get_meta(pos)
+
+ if not player or not player:is_player() then
+ return
+ end
+ local owner = meta:get_string("owner")
+ if player:get_player_name() ~= owner then
+ return
+ end
+
+ local dir = minetest.string_to_pos(meta:get_string("dir"))
+ local height = meta:get_int("height")
+ local width = meta:get_int("width")
+ local org_pos = table.copy(pos)
+ if dir ~= nil and height ~= nil and width ~= nil then
+ dig_crane(pos, dir, height, width)
+ end
+ --check for correct size format
+ size = string.split(fields.size, ",")
+ if #size == 2 then
+ local height = tonumber(size[1])
+ local width = tonumber(size[2])
+ if height ~= nil and width ~= nil then
+ height = math.max(height, 8)
+ height = math.min(height, 24)
+ width = math.max(width, 8)
+ width = math.min(width, 24)
+ meta:set_int("height", height)
+ meta:set_int("width", width)
+ meta:set_string("infotext", "Crane size: " .. height .. "," .. width)
+ if dir ~= nil then
+ construct_crane(org_pos, dir, height, width, owner)
+ end
+ end
+ end
+ end,
+
+ -- remove mast and arm if base gets destroyed
+ on_destruct = function(pos)
+ local meta = minetest.get_meta(pos)
+ local dir = minetest.string_to_pos(meta:get_string("dir"))
+ local height = meta:get_int("height")
+ local width = meta:get_int("width")
+
+ if dir ~= nil and height ~= nil and width ~= nil then
+ dig_crane(pos, dir, height, width)
+ end
+ end,
+})
+
+----------------------------------------------------------------------------------------------------
+-- Register Crane balance
+----------------------------------------------------------------------------------------------------
+minetest.register_node("towercrane:balance", {
+ description = "Tower Crane Balance",
+ tiles = {
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+----------------------------------------------------------------------------------------------------
+-- Register Crane mast
+----------------------------------------------------------------------------------------------------
+minetest.register_node("towercrane:mast", {
+ description = "Tower Crane Mast",
+ drawtype = "glasslike_framed",
+ tiles = {
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+----------------------------------------------------------------------------------------------------
+-- Register Crane Switch (on)
+----------------------------------------------------------------------------------------------------
+minetest.register_node("towercrane:mast_ctrl_on", {
+ description = "Tower Crane Mast Ctrl On",
+ drawtype = "node",
+ tiles = {
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl_on.png",
+ "towercrane_mast_ctrl_on.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ },
+ on_rightclick = function (pos, node, clicker)
+ local meta = minetest.get_meta(pos)
+ if not clicker or not clicker:is_player() then
+ return
+ end
+ if clicker:get_player_name() ~= meta:get_string("owner") then
+ return
+ end
+ node.name = "towercrane:mast_ctrl_off"
+ minetest.swap_node(pos, node)
+
+ local id = minetest.hash_node_position(pos)
+ if towercrane.id then
+ towercrane.id:remove()
+ towercrane.id = nil
+ else
+
+ end
+ end,
+
+ on_construct = function(pos)
+ local meta = minetest.get_meta(pos)
+ meta:set_string("infotext", "Switch crane on/off")
+ end,
+
+ after_place_node = function(pos, placer, itemstack, pointed_thing)
+ local meta = minetest.get_meta(pos)
+ local owner = placer:get_player_name()
+ meta:set_string("owner", owner)
+ end,
+
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+----------------------------------------------------------------------------------------------------
+-- Register Crane Switch (off)
+----------------------------------------------------------------------------------------------------
+minetest.register_node("towercrane:mast_ctrl_off", {
+ description = "Tower Crane Mast Ctrl Off",
+ drawtype = "node",
+ tiles = {
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl_off.png",
+ "towercrane_mast_ctrl_off.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ },
+ on_rightclick = function (pos, node, clicker)
+ -- switch switch on, calculate the construction area, and place the hook
+ local meta = minetest.get_meta(pos)
+ -- only the owner is allowed to switch
+ if not clicker or not clicker:is_player() then
+ return
+ end
+ if clicker:get_player_name() ~= meta:get_string("owner") then
+ return
+ end
+ -- swap to the other node
+ node.name = "towercrane:mast_ctrl_on"
+ minetest.swap_node(pos, node)
+ local dir = minetest.string_to_pos(meta:get_string("dir"))
+ if pos ~= nil and dir ~= nil then
+ -- store hook instance in 'towercrane'
+ local id = minetest.hash_node_position(pos)
+ towercrane.id = place_hook(table.copy(pos), dir)
+
+ -- calculate the construction area dimension (pos, pos2)
+ local height = meta:get_int("height")
+ local width = meta:get_int("width")
+
+ -- pos1 = close/right
+ dir = turnright(dir)
+ local pos1 = vector.add(pos, vector.multiply(dir, width/2))
+ dir = turnleft(dir)
+ local pos1 = vector.add(pos1, vector.multiply(dir, 1))
+ pos1.y = pos.y - 1
+
+ -- pos2 = far/left
+ local pos2 = vector.add(pos1, vector.multiply(dir, width-1))
+ dir = turnleft(dir)
+ pos2 = vector.add(pos2, vector.multiply(dir, width))
+ pos2.y = pos.y - 4 + height
+
+ -- normalize x/z so that pos2 > pos1
+ if pos2.x < pos1.x then
+ pos2.x, pos1.x = pos1.x, pos2.x
+ end
+ if pos2.z < pos1.z then
+ pos2.z, pos1.z = pos1.z, pos2.z
+ end
+
+ -- store pos1/pos2 in the hook (LuaEntitySAO)
+ towercrane.id:get_luaentity().pos1 = pos1
+ towercrane.id:get_luaentity().pos2 = pos2
+ end
+ end,
+
+ on_construct = function(pos)
+ -- add infotext
+ local meta = minetest.get_meta(pos)
+ meta:set_string("infotext", "Switch crane on/off")
+ end,
+
+ after_place_node = function(pos, placer, itemstack, pointed_thing)
+ -- store owner for dig protection
+ local meta = minetest.get_meta(pos)
+ local owner = placer:get_player_name()
+ meta:set_string("owner", owner)
+ end,
+
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+----------------------------------------------------------------------------------------------------
+-- Register Crane arm 1
+----------------------------------------------------------------------------------------------------
+minetest.register_node("towercrane:arm", {
+ description = "Tower Crane Arm",
+ drawtype = "glasslike_framed",
+ tiles = {
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+----------------------------------------------------------------------------------------------------
+-- Register Crane arm 2
+----------------------------------------------------------------------------------------------------
+minetest.register_node("towercrane:arm2", {
+ description = "Tower Crane Arm2",
+ drawtype = "glasslike_framed",
+ tiles = {
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
diff --git a/init.lua.org b/init.lua.org
new file mode 100644
index 0000000..2180ae1
--- /dev/null
+++ b/init.lua.org
@@ -0,0 +1,483 @@
+--[[
+
+ Tower Crane (Liebherr) Mod
+ by JoSt
+
+ Copyright (C) 2017 Joachim Stolberg
+ LGPLv2.1+
+ See LICENSE for more information
+
+]]--
+
+
+towercrane = {}
+
+local hook = {
+ physical = true, -- should be true, otherwise you dive into the underground
+ collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2}, -- Groesse des Wuerfels bei Kollisionen
+ collide_with_objects = false,
+ visual = "cube", -- Object als Wuerfel
+ visual_size = {x=0.4, y=0.4}, -- Groesse des sichtbaren Wuerfels
+ textures = {
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ "towercrane_hook.png",
+ },
+ groups = {cracky=1},
+ -- local variabels
+ driver = nil,
+ speed_forward=0,
+ speed_right=0,
+ speed_up=0,
+}
+
+-- ":" methode von "hook" (self ist versteckt)
+function hook:on_rightclick(clicker)
+ if self.driver and clicker == self.driver then -- aussteigen?
+ clicker:set_detach()
+ self.driver = nil
+ elseif not self.driver then -- einsteigen?
+ self.driver = clicker
+ clicker:set_attach(self.object, "", {x=0,y=0,z=0}, {x=0,y=0,z=0})
+ end
+end
+
+function hook:on_step(dtime)
+ -- remove hook from last visit
+ if self.pos1 == nil or self.pos2 == nil then
+ self.object:remove()
+ return
+ end
+ if self.driver then
+ local ctrl = self.driver:get_player_control()
+ local yaw = self.driver:get_look_horizontal()
+ local pos = self.driver:getpos()
+ local max_speed = 5
+ local velocity = 0.5
+
+ if ctrl.up then -- forward
+ self.speed_forward = math.min(self.speed_forward + velocity, max_speed)
+ elseif ctrl.down then -- backward
+ self.speed_forward = math.max(self.speed_forward - velocity, -max_speed)
+ elseif self.speed_forward > 0 then
+ self.speed_forward = self.speed_forward - velocity
+ elseif self.speed_forward < 0 then
+ self.speed_forward = self.speed_forward + velocity
+ end
+
+ if ctrl.right then -- right
+ self.speed_right = math.min(self.speed_right + velocity, max_speed)
+ elseif ctrl.left then -- left
+ self.speed_right = math.max(self.speed_right - velocity, -max_speed)
+ elseif self.speed_right > 0 then
+ self.speed_right = self.speed_right - velocity
+ elseif self.speed_right < 0 then
+ self.speed_right = self.speed_right + velocity
+ end
+
+ if ctrl.jump then -- up
+ self.speed_up = math.min(self.speed_up + velocity, 5)
+ elseif ctrl.sneak then -- down
+ self.speed_up = math.max(self.speed_up - velocity, -5)
+ elseif self.speed_up > 0 then
+ self.speed_up = self.speed_up - velocity
+ elseif self.speed_up < 0 then
+ self.speed_up = self.speed_up + velocity
+ end
+
+ -- calculate the direction vector
+ local vx = math.cos(yaw+math.pi/2) * self.speed_forward + math.cos(yaw) * self.speed_right
+ local vz = math.sin(yaw+math.pi/2) * self.speed_forward + math.sin(yaw) * self.speed_right
+
+ -- check if outside of the construction area
+ if pos.x < self.pos1.x then vx= velocity end
+ if pos.x > self.pos2.x then vx= -velocity end
+ if pos.y < self.pos1.y then self.speed_up= velocity end
+ if pos.y > self.pos2.y then self.speed_up= -velocity end
+ if pos.z < self.pos1.z then vz= velocity end
+ if pos.z > self.pos2.z then vz= -velocity end
+
+ self.object:setvelocity({x=vx, y=self.speed_up,z=vz})
+ else
+ self.object:setvelocity({x=0, y=0,z=0})
+ end
+end
+
+-- LuaEntitySAO (non-player moving things): see http://dev.minetest.net/LuaEntitySAO
+minetest.register_entity("towercrane:hook", hook)
+
+----------------------------------------------------------------------------------------------------
+----------------------------------------------------------------------------------------------------
+----------------------------------------------------------------------------------------------------
+local function turnright(dir)
+ local facedir = minetest.dir_to_facedir(dir)
+ return minetest.facedir_to_dir((facedir + 1) % 4)
+end
+
+local function turnleft(dir)
+ local facedir = minetest.dir_to_facedir(dir)
+ return minetest.facedir_to_dir((facedir + 3) % 4)
+end
+
+local function construct_crane(pos, dir, height, width, owner)
+ pos.y = pos.y + 1
+ minetest.env:add_node(pos, {name="towercrane:mast_ctrl_off", param2=minetest.dir_to_facedir(dir)})
+ local meta = minetest.get_meta(pos)
+ meta:set_string("dir", minetest.pos_to_string(dir))
+ meta:set_string("owner", owner)
+ meta:set_int("height", height)
+ meta:set_int("width", width)
+
+ for i = 1,height+1 do
+ pos.y = pos.y + 1
+ minetest.env:add_node(pos, {name="towercrane:mast"})
+ end
+
+ pos.y = pos.y - 2
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:add_node(pos, {name="towercrane:arm2"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:add_node(pos, {name="towercrane:arm"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:add_node(pos, {name="towercrane:balance"})
+ pos.x = pos.x + 3 * dir.x
+ pos.z = pos.z + 3 * dir.z
+
+ for i = 1,width do
+ pos.x = pos.x + dir.x
+ pos.z = pos.z + dir.z
+ if i % 2 == 0 then
+ minetest.env:add_node(pos, {name="towercrane:arm2"})
+ else
+ minetest.env:add_node(pos, {name="towercrane:arm"})
+ end
+
+ end
+end
+
+local function dig_crane(pos, dir, height, width)
+ pos.y = pos.y + 1
+ minetest.env:remove_node(pos, {name="towercrane:mast_ctrl_off"})
+
+ for i = 1,height+1 do
+ pos.y = pos.y + 1
+ minetest.env:remove_node(pos, {name="towercrane:mast"})
+ end
+
+ pos.y = pos.y - 2
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:remove_node(pos, {name="towercrane:arm2"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:remove_node(pos, {name="towercrane:arm"})
+ pos.x = pos.x - dir.x
+ pos.z = pos.z - dir.z
+ minetest.env:remove_node(pos, {name="towercrane:balance"})
+ pos.x = pos.x + 3 * dir.x
+ pos.z = pos.z + 3 * dir.z
+
+ for i = 1,width do
+ pos.x = pos.x + dir.x
+ pos.z = pos.z + dir.z
+ if i % 2 == 0 then
+ minetest.env:remove_node(pos, {name="towercrane:arm2"})
+ else
+ minetest.env:remove_node(pos, {name="towercrane:arm"})
+ end
+
+ end
+end
+
+local function place_hook(pos, dir)
+ pos.y = pos.y - 1
+ pos.x = pos.x + dir.x
+ pos.z = pos.z + dir.z
+ return minetest.add_entity(pos, "towercrane:hook")
+end
+
+minetest.register_node("towercrane:base", {
+ description = "Tower Crane Base",
+ inventory_image = "towercrane_invent.png",
+ tiles = {
+ "towercrane_base_top.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {cracky=1},
+ formspec = set_formspec,
+
+
+ after_place_node = function(pos, placer)
+ local meta = minetest.get_meta(pos)
+ local owner = placer:get_player_name()
+ meta:set_string("owner", owner)
+ local formspec = "size[5,4]"..
+ "label[0,0;Construction area size]" ..
+ "field[1,1.5;3,1;size;height,width;]" ..
+ "button_exit[1,2;2,1;exit;Save]"
+ meta:set_string("formspec", formspec)
+
+ local fdir = minetest.dir_to_facedir(placer:get_look_dir(), false)
+ local dir = minetest.facedir_to_dir(fdir)
+ meta:set_string("dir", minetest.pos_to_string(dir))
+ end,
+
+ on_receive_fields = function(pos, formname, fields, player)
+ if fields.size == nil then
+ return
+ end
+ local meta = minetest.get_meta(pos)
+
+ if not player or not player:is_player() then
+ return
+ end
+ local owner = meta:get_string("owner")
+ if player:get_player_name() ~= owner then
+ return
+ end
+
+ local dir = minetest.string_to_pos(meta:get_string("dir"))
+ local height = meta:get_int("height")
+ local width = meta:get_int("width")
+ local org_pos = table.copy(pos)
+ if dir ~= nil and height ~= nil and width ~= nil then
+ dig_crane(pos, dir, height, width)
+ end
+ --check for correct size format
+ size = string.split(fields.size, ",")
+ if #size == 2 then
+ local height = tonumber(size[1])
+ local width = tonumber(size[2])
+ if height ~= nil and width ~= nil then
+ height = math.max(height, 8)
+ height = math.min(height, 24)
+ width = math.max(width, 8)
+ width = math.min(width, 24)
+ meta:set_int("height", height)
+ meta:set_int("width", width)
+ meta:set_string("infotext", "Crane size: " .. height .. "," .. width)
+ if dir ~= nil then
+ construct_crane(org_pos, dir, height, width, owner)
+ end
+ end
+ end
+ end,
+
+ on_destruct = function(pos)
+ local meta = minetest.get_meta(pos)
+ local dir = minetest.string_to_pos(meta:get_string("dir"))
+ local height = meta:get_int("height")
+ local width = meta:get_int("width")
+
+ if dir ~= nil and height ~= nil and width ~= nil then
+ dig_crane(pos, dir, height, width)
+ end
+ end,
+})
+
+minetest.register_on_leaveplayer(function(ObjectRef, timed_out)
+ print("Schade")
+ end
+)
+
+
+minetest.register_node("towercrane:balance", {
+ description = "Tower Crane Balance",
+ tiles = {
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ "towercrane_base.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+minetest.register_node("towercrane:mast", {
+ description = "Tower Crane Mast",
+ drawtype = "glasslike_framed",
+ tiles = {
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ "towercrane_mast.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+minetest.register_node("towercrane:mast_ctrl_on", {
+ description = "Tower Crane Mast Ctrl On",
+ drawtype = "node",
+ tiles = {
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl_on.png",
+ "towercrane_mast_ctrl_on.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ },
+ on_rightclick = function (pos, node, clicker)
+ local meta = minetest.get_meta(pos)
+ if not clicker or not clicker:is_player() then
+ return
+ end
+ if clicker:get_player_name() ~= meta:get_string("owner") then
+ return
+ end
+ node.name = "towercrane:mast_ctrl_off"
+ minetest.swap_node(pos, node)
+
+ local id = minetest.hash_node_position(pos)
+ if towercrane.id then
+ towercrane.id:remove()
+ towercrane.id = nil
+ else
+
+ end
+ end,
+
+ on_construct = function(pos)
+ local meta = minetest.get_meta(pos)
+ meta:set_string("infotext", "Switch crane on/off")
+ end,
+
+ after_place_node = function(pos, placer, itemstack, pointed_thing)
+ local meta = minetest.get_meta(pos)
+ local owner = placer:get_player_name()
+ meta:set_string("owner", owner)
+ end,
+
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+minetest.register_node("towercrane:mast_ctrl_off", {
+ description = "Tower Crane Mast Ctrl Off",
+ drawtype = "node",
+ tiles = {
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl_off.png",
+ "towercrane_mast_ctrl_off.png",
+ "towercrane_mast_ctrl.png",
+ "towercrane_mast_ctrl.png",
+ },
+ on_rightclick = function (pos, node, clicker)
+ -- switch switch on, calculate the construction area, and place the hook
+ local meta = minetest.get_meta(pos)
+ -- only the owner is allowed to switch
+ if not clicker or not clicker:is_player() then
+ return
+ end
+ if clicker:get_player_name() ~= meta:get_string("owner") then
+ return
+ end
+ -- swap to the other node
+ node.name = "towercrane:mast_ctrl_on"
+ minetest.swap_node(pos, node)
+ local dir = minetest.string_to_pos(meta:get_string("dir"))
+ if pos ~= nil and dir ~= nil then
+ -- store hook instance in 'towercrane'
+ local id = minetest.hash_node_position(pos)
+ towercrane.id = place_hook(table.copy(pos), dir)
+
+ -- calculate the construction area dimension (pos, pos2)
+ local height = meta:get_int("height")
+ local width = meta:get_int("width")
+
+ -- pos1 = close/right
+ dir = turnright(dir)
+ local pos1 = vector.add(pos, vector.multiply(dir, width/2))
+ dir = turnleft(dir)
+ local pos1 = vector.add(pos1, vector.multiply(dir, 1))
+ pos1.y = pos.y - 1
+
+ -- pos2 = far/left
+ local pos2 = vector.add(pos1, vector.multiply(dir, width-1))
+ dir = turnleft(dir)
+ pos2 = vector.add(pos2, vector.multiply(dir, width))
+ pos2.y = pos.y - 4 + height
+
+ -- normalize x/z so that pos2 > pos1
+ if pos2.x < pos1.x then
+ pos2.x, pos1.x = pos1.x, pos2.x
+ end
+ if pos2.z < pos1.z then
+ pos2.z, pos1.z = pos1.z, pos2.z
+ end
+
+ -- store pos1/pos2 in the hook (LuaEntitySAO)
+ towercrane.id:get_luaentity().pos1 = pos1
+ towercrane.id:get_luaentity().pos2 = pos2
+ end
+ end,
+
+ on_construct = function(pos)
+ -- add infotext
+ local meta = minetest.get_meta(pos)
+ meta:set_string("infotext", "Switch crane on/off")
+ end,
+
+ after_place_node = function(pos, placer, itemstack, pointed_thing)
+ -- store owner for dig protection
+ local meta = minetest.get_meta(pos)
+ local owner = placer:get_player_name()
+ meta:set_string("owner", owner)
+ end,
+
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+minetest.register_node("towercrane:arm", {
+ description = "Tower Crane Arm",
+ drawtype = "glasslike_framed",
+ tiles = {
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ "towercrane_arm.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
+
+minetest.register_node("towercrane:arm2", {
+ description = "Tower Crane Arm2",
+ drawtype = "glasslike_framed",
+ tiles = {
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ "towercrane_arm2.png",
+ },
+ paramtype2 = "facedir",
+ is_ground_content = false,
+ groups = {crumbly=0, not_in_creative_inventory=1},
+})
diff --git a/lua_api.dtxt2 b/lua_api.dtxt2
new file mode 100644
index 0000000..975fd64
--- /dev/null
+++ b/lua_api.dtxt2
@@ -0,0 +1,4676 @@
+# Minetest Lua Modding API Reference 0.4.16
+
+* More information at
+* Developer Wiki:
+
+Introduction
+------------
+Content and functionality can be added to Minetest 0.4 by using Lua
+scripting in run-time loaded mods.
+
+A mod is a self-contained bunch of scripts, textures and other related
+things that is loaded by and interfaces with Minetest.
+
+Mods are contained and ran solely on the server side. Definitions and media
+files are automatically transferred to the client.
+
+If you see a deficiency in the API, feel free to attempt to add the
+functionality in the engine and API. You can send such improvements as
+source code patches to .
+
+Programming in Lua
+------------------
+If you have any difficulty in understanding this, please read
+[Programming in Lua](http://www.lua.org/pil/).
+
+Startup
+-------
+Mods are loaded during server startup from the mod load paths by running
+the `init.lua` scripts in a shared environment.
+
+Paths
+-----
+* `RUN_IN_PLACE=1` (Windows release, local build)
+ * `$path_user`:
+ * Linux: ``
+ * Windows: ``
+ * `$path_share`
+ * Linux: ``
+ * Windows: ``
+* `RUN_IN_PLACE=0`: (Linux release)
+ * `$path_share`
+ * Linux: `/usr/share/minetest`
+ * Windows: `/minetest-0.4.x`
+ * `$path_user`:
+ * Linux: `$HOME/.minetest`
+ * Windows: `C:/users//AppData/minetest` (maybe)
+
+Games
+-----
+Games are looked up from:
+
+* `$path_share/games/gameid/`
+* `$path_user/games/gameid/`
+
+where `gameid` is unique to each game.
+
+The game directory contains the file `game.conf`, which contains these fields:
+
+ name =
+
+e.g.
+
+ name = Minetest
+
+The game directory can contain the file minetest.conf, which will be used
+to set default settings when running the particular game.
+It can also contain a settingtypes.txt in the same format as the one in builtin.
+This settingtypes.txt will be parsed by the menu and the settings will be displayed
+in the "Games" category in the settings tab.
+
+### Menu images
+
+Games can provide custom main menu images. They are put inside a `menu` directory
+inside the game directory.
+
+The images are named `$identifier.png`, where `$identifier` is
+one of `overlay,background,footer,header`.
+If you want to specify multiple images for one identifier, add additional images named
+like `$identifier.$n.png`, with an ascending number $n starting with 1, and a random
+image will be chosen from the provided ones.
+
+
+Mod load path
+-------------
+Generic:
+
+* `$path_share/games/gameid/mods/`
+* `$path_share/mods/`
+* `$path_user/games/gameid/mods/`
+* `$path_user/mods/` (User-installed mods)
+* `$worldpath/worldmods/`
+
+In a run-in-place version (e.g. the distributed windows version):
+
+* `minetest-0.4.x/games/gameid/mods/`
+* `minetest-0.4.x/mods/` (User-installed mods)
+* `minetest-0.4.x/worlds/worldname/worldmods/`
+
+On an installed version on Linux:
+
+* `/usr/share/minetest/games/gameid/mods/`
+* `$HOME/.minetest/mods/` (User-installed mods)
+* `$HOME/.minetest/worlds/worldname/worldmods`
+
+Mod load path for world-specific games
+--------------------------------------
+It is possible to include a game in a world; in this case, no mods or
+games are loaded or checked from anywhere else.
+
+This is useful for e.g. adventure worlds.
+
+This happens if the following directory exists:
+
+ $world/game/
+
+Mods should be then be placed in:
+
+ $world/game/mods/
+
+Modpack support
+----------------
+Mods can be put in a subdirectory, if the parent directory, which otherwise
+should be a mod, contains a file named `modpack.txt`. This file shall be
+empty, except for lines starting with `#`, which are comments.
+
+Mod directory structure
+------------------------
+
+ mods
+ |-- modname
+ | |-- depends.txt
+ | |-- screenshot.png
+ | |-- description.txt
+ | |-- settingtypes.txt
+ | |-- init.lua
+ | |-- models
+ | |-- textures
+ | | |-- modname_stuff.png
+ | | `-- modname_something_else.png
+ | |-- sounds
+ | |-- media
+ | `--
+ `-- another
+
+
+### modname
+The location of this directory can be fetched by using
+`minetest.get_modpath(modname)`.
+
+### `depends.txt`
+List of mods that have to be loaded before loading this mod.
+
+A single line contains a single modname.
+
+Optional dependencies can be defined by appending a question mark
+to a single modname. Their meaning is that if the specified mod
+is missing, that does not prevent this mod from being loaded.
+
+### `screenshot.png`
+A screenshot shown in the mod manager within the main menu. It should
+have an aspect ratio of 3:2 and a minimum size of 300×200 pixels.
+
+### `description.txt`
+A File containing description to be shown within mainmenu.
+
+### `settingtypes.txt`
+A file in the same format as the one in builtin. It will be parsed by the
+settings menu and the settings will be displayed in the "Mods" category.
+
+### `init.lua`
+The main Lua script. Running this script should register everything it
+wants to register. Subsequent execution depends on minetest calling the
+registered callbacks.
+
+`minetest.settings` can be used to read custom or existing settings at load
+time, if necessary. (See `Settings`)
+
+### `models`
+Models for entities or meshnodes.
+
+### `textures`, `sounds`, `media`
+Media files (textures, sounds, whatever) that will be transferred to the
+client and will be available for use by the mod.
+
+Naming convention for registered textual names
+----------------------------------------------
+Registered names should generally be in this format:
+
+ `modname:`
+
+`` can have these characters:
+
+ a-zA-Z0-9_
+
+This is to prevent conflicting names from corrupting maps and is
+enforced by the mod loader.
+
+### Example
+In the mod `experimental`, there is the ideal item/node/entity name `tnt`.
+So the name should be `experimental:tnt`.
+
+Enforcement can be overridden by prefixing the name with `:`. This can
+be used for overriding the registrations of some other mod.
+
+Example: Any mod can redefine `experimental:tnt` by using the name
+
+ :experimental:tnt
+
+when registering it.
+(also that mod is required to have `experimental` as a dependency)
+
+The `:` prefix can also be used for maintaining backwards compatibility.
+
+### Aliases
+Aliases can be added by using `minetest.register_alias(name, convert_to)` or
+`minetest.register_alias_force(name, convert_to)`.
+
+This will make Minetest to convert things called name to things called
+`convert_to`.
+
+The only difference between `minetest.register_alias` and
+`minetest.register_alias_force` is that if an item called `name` exists,
+`minetest.register_alias` will do nothing while
+`minetest.register_alias_force` will unregister it.
+
+This can be used for maintaining backwards compatibility.
+
+This can be also used for setting quick access names for things, e.g. if
+you have an item called `epiclylongmodname:stuff`, you could do
+
+ minetest.register_alias("stuff", "epiclylongmodname:stuff")
+
+and be able to use `/giveme stuff`.
+
+Textures
+--------
+Mods should generally prefix their textures with `modname_`, e.g. given
+the mod name `foomod`, a texture could be called:
+
+ foomod_foothing.png
+
+Textures are referred to by their complete name, or alternatively by
+stripping out the file extension:
+
+* e.g. `foomod_foothing.png`
+* e.g. `foomod_foothing`
+
+Texture modifiers
+-----------------
+There are various texture modifiers that can be used
+to generate textures on-the-fly.
+
+### Texture overlaying
+Textures can be overlaid by putting a `^` between them.
+
+Example:
+
+ default_dirt.png^default_grass_side.png
+
+`default_grass_side.png` is overlayed over `default_dirt.png`.
+The texture with the lower resolution will be automatically upscaled to
+the higher resolution texture.
+
+### Texture grouping
+Textures can be grouped together by enclosing them in `(` and `)`.
+
+Example: `cobble.png^(thing1.png^thing2.png)`
+
+A texture for `thing1.png^thing2.png` is created and the resulting
+texture is overlaid on top of `cobble.png`.
+
+### Escaping
+Modifiers that accept texture names (e.g. `[combine`) accept escaping to allow
+passing complex texture names as arguments. Escaping is done with backslash and
+is required for `^` and `:`.
+
+Example: `cobble.png^[lowpart:50:color.png\^[mask\:trans.png`
+
+The lower 50 percent of `color.png^[mask:trans.png` are overlaid
+on top of `cobble.png`.
+
+### Advanced texture modifiers
+
+#### `[crack::
`
+* `` = animation frame count
+* `
` = current animation frame
+
+Draw a step of the crack animation on the texture.
+
+Example:
+
+ default_cobble.png^[crack:10:1
+
+#### `[combine:x:,=:,=:...`
+* `` = width
+* `` = height
+* `` = x position
+* `` = y position
+* `` = texture to combine
+
+Creates a texture of size `` times `` and blits the listed files to their
+specified coordinates.
+
+Example:
+
+ [combine:16x32:0,0=default_cobble.png:0,16=default_wood.png
+
+#### `[resize:x`
+Resizes the texture to the given dimensions.
+
+Example:
+
+ default_sandstone.png^[resize:16x16
+
+#### `[opacity:`
+Makes the base image transparent according to the given ratio.
+
+`r` must be between 0 and 255.
+0 means totally transparent. 255 means totally opaque.
+
+Example:
+
+ default_sandstone.png^[opacity:127
+
+#### `[invert:`
+Inverts the given channels of the base image.
+Mode may contain the characters "r", "g", "b", "a".
+Only the channels that are mentioned in the mode string will be inverted.
+
+Example:
+
+ default_apple.png^[invert:rgb
+
+#### `[brighten`
+Brightens the texture.
+
+Example:
+
+ tnt_tnt_side.png^[brighten
+
+#### `[noalpha`
+Makes the texture completely opaque.
+
+Example:
+
+ default_leaves.png^[noalpha
+
+#### `[makealpha:,,`
+Convert one color to transparency.
+
+Example:
+
+ default_cobble.png^[makealpha:128,128,128
+
+#### `[transform`
+* `` = transformation(s) to apply
+
+Rotates and/or flips the image.
+
+`` can be a number (between 0 and 7) or a transform name.
+Rotations are counter-clockwise.
+
+ 0 I identity
+ 1 R90 rotate by 90 degrees
+ 2 R180 rotate by 180 degrees
+ 3 R270 rotate by 270 degrees
+ 4 FX flip X
+ 5 FXR90 flip X then rotate by 90 degrees
+ 6 FY flip Y
+ 7 FYR90 flip Y then rotate by 90 degrees
+
+Example:
+
+ default_stone.png^[transformFXR90
+
+#### `[inventorycube{{{`
+Escaping does not apply here and `^` is replaced by `&` in texture names instead.
+
+Create an inventory cube texture using the side textures.
+
+Example:
+
+ [inventorycube{grass.png{dirt.png&grass_side.png{dirt.png&grass_side.png
+
+Creates an inventorycube with `grass.png`, `dirt.png^grass_side.png` and
+`dirt.png^grass_side.png` textures
+
+#### `[lowpart::`
+Blit the lower ``% part of `` on the texture.
+
+Example:
+
+ base.png^[lowpart:25:overlay.png
+
+#### `[verticalframe::`
+* `` = animation frame count
+* `` = current animation frame
+
+Crops the texture to a frame of a vertical animation.
+
+Example:
+
+ default_torch_animated.png^[verticalframe:16:8
+
+#### `[mask:`
+Apply a mask to the base image.
+
+The mask is applied using binary AND.
+
+#### `[sheet:x:,`
+Retrieves a tile at position x,y from the base image
+which it assumes to be a tilesheet with dimensions w,h.
+
+
+#### `[colorize::`
+Colorize the textures with the given color.
+`` is specified as a `ColorString`.
+`` is an int ranging from 0 to 255 or the word "`alpha`". If
+it is an int, then it specifies how far to interpolate between the
+colors where 0 is only the texture color and 255 is only ``. If
+omitted, the alpha of `` will be used as the ratio. If it is
+the word "`alpha`", then each texture pixel will contain the RGB of
+`` and the alpha of `` multiplied by the alpha of the
+texture pixel.
+
+#### `[multiply:`
+Multiplies texture colors with the given color.
+`` is specified as a `ColorString`.
+Result is more like what you'd expect if you put a color on top of another
+color. Meaning white surfaces get a lot of your new color while black parts don't
+change very much.
+
+Hardware coloring
+-----------------
+The goal of hardware coloring is to simplify the creation of
+colorful nodes. If your textures use the same pattern, and they only
+differ in their color (like colored wool blocks), you can use hardware
+coloring instead of creating and managing many texture files.
+All of these methods use color multiplication (so a white-black texture
+with red coloring will result in red-black color).
+
+### Static coloring
+This method is useful if you wish to create nodes/items with
+the same texture, in different colors, each in a new node/item definition.
+
+#### Global color
+When you register an item or node, set its `color` field (which accepts a
+`ColorSpec`) to the desired color.
+
+An `ItemStack`s static color can be overwritten by the `color` metadata
+field. If you set that field to a `ColorString`, that color will be used.
+
+#### Tile color
+Each tile may have an individual static color, which overwrites every
+other coloring methods. To disable the coloring of a face,
+set its color to white (because multiplying with white does nothing).
+You can set the `color` property of the tiles in the node's definition
+if the tile is in table format.
+
+### Palettes
+For nodes and items which can have many colors, a palette is more
+suitable. A palette is a texture, which can contain up to 256 pixels.
+Each pixel is one possible color for the node/item.
+You can register one node/item, which can have up to 256 colors.
+
+#### Palette indexing
+When using palettes, you always provide a pixel index for the given
+node or `ItemStack`. The palette is read from left to right and from
+top to bottom. If the palette has less than 256 pixels, then it is
+stretched to contain exactly 256 pixels (after arranging the pixels
+to one line). The indexing starts from 0.
+
+Examples:
+* 16x16 palette, index = 0: the top left corner
+* 16x16 palette, index = 4: the fifth pixel in the first row
+* 16x16 palette, index = 16: the pixel below the top left corner
+* 16x16 palette, index = 255: the bottom right corner
+* 2 (width)x4 (height) palette, index=31: the top left corner.
+ The palette has 8 pixels, so each pixel is stretched to 32 pixels,
+ to ensure the total 256 pixels.
+* 2x4 palette, index=32: the top right corner
+* 2x4 palette, index=63: the top right corner
+* 2x4 palette, index=64: the pixel below the top left corner
+
+#### Using palettes with items
+When registering an item, set the item definition's `palette` field to
+a texture. You can also use texture modifiers.
+
+The `ItemStack`'s color depends on the `palette_index` field of the
+stack's metadata. `palette_index` is an integer, which specifies the
+index of the pixel to use.
+
+#### Linking palettes with nodes
+When registering a node, set the item definition's `palette` field to
+a texture. You can also use texture modifiers.
+The node's color depends on its `param2`, so you also must set an
+appropriate `drawtype`:
+* `drawtype = "color"` for nodes which use their full `param2` for
+ palette indexing. These nodes can have 256 different colors.
+ The palette should contain 256 pixels.
+* `drawtype = "colorwallmounted"` for nodes which use the first
+ five bits (most significant) of `param2` for palette indexing.
+ The remaining three bits are describing rotation, as in `wallmounted`
+ draw type. Division by 8 yields the palette index (without stretching the
+ palette). These nodes can have 32 different colors, and the palette
+ should contain 32 pixels.
+ Examples:
+ * `param2 = 17` is 2 * 8 + 1, so the rotation is 1 and the third (= 2 + 1)
+ pixel will be picked from the palette.
+ * `param2 = 35` is 4 * 8 + 3, so the rotation is 3 and the fifth (= 4 + 1)
+ pixel will be picked from the palette.
+* `drawtype = "colorfacedir"` for nodes which use the first
+ three bits of `param2` for palette indexing. The remaining
+ five bits are describing rotation, as in `facedir` draw type.
+ Division by 32 yields the palette index (without stretching the
+ palette). These nodes can have 8 different colors, and the
+ palette should contain 8 pixels.
+ Examples:
+ * `param2 = 17` is 0 * 32 + 17, so the rotation is 17 and the
+ first (= 0 + 1) pixel will be picked from the palette.
+ * `param2 = 35` is 1 * 32 + 3, so the rotation is 3 and the
+ second (= 1 + 1) pixel will be picked from the palette.
+
+To colorize a node on the map, set its `param2` value (according
+to the node's draw type).
+
+### Conversion between nodes in the inventory and the on the map
+Static coloring is the same for both cases, there is no need
+for conversion.
+
+If the `ItemStack`'s metadata contains the `color` field, it will be
+lost on placement, because nodes on the map can only use palettes.
+
+If the `ItemStack`'s metadata contains the `palette_index` field, you
+currently must manually convert between it and the node's `param2` with
+custom `on_place` and `on_dig` callbacks.
+
+### Colored items in craft recipes
+Craft recipes only support item strings, but fortunately item strings
+can also contain metadata. Example craft recipe registration:
+
+ local stack = ItemStack("wool:block")
+ dyed:get_meta():set_int("palette_index", 3) -- add index
+ minetest.register_craft({
+ output = dyed:to_string(), -- convert to string
+ type = "shapeless",
+ recipe = {
+ "wool:block",
+ "dye:red",
+ },
+ })
+
+Metadata field filtering in the `recipe` field are not supported yet,
+so the craft output is independent of the color of the ingredients.
+
+Soft texture overlay
+--------------------
+Sometimes hardware coloring is not enough, because it affects the
+whole tile. Soft texture overlays were added to Minetest to allow
+the dynamic coloring of only specific parts of the node's texture.
+For example a grass block may have colored grass, while keeping the
+dirt brown.
+
+These overlays are 'soft', because unlike texture modifiers, the layers
+are not merged in the memory, but they are simply drawn on top of each
+other. This allows different hardware coloring, but also means that
+tiles with overlays are drawn slower. Using too much overlays might
+cause FPS loss.
+
+To define an overlay, simply set the `overlay_tiles` field of the node
+definition. These tiles are defined in the same way as plain tiles:
+they can have a texture name, color etc.
+To skip one face, set that overlay tile to an empty string.
+
+Example (colored grass block):
+
+ minetest.register_node("default:dirt_with_grass", {
+ description = "Dirt with Grass",
+ -- Regular tiles, as usual
+ -- The dirt tile disables palette coloring
+ tiles = {{name = "default_grass.png"},
+ {name = "default_dirt.png", color = "white"}},
+ -- Overlay tiles: define them in the same style
+ -- The top and bottom tile does not have overlay
+ overlay_tiles = {"", "",
+ {name = "default_grass_side.png", tileable_vertical = false}},
+ -- Global color, used in inventory
+ color = "green",
+ -- Palette in the world
+ paramtype2 = "color",
+ palette = "default_foilage.png",
+ })
+
+Sounds
+------
+Only Ogg Vorbis files are supported.
+
+For positional playing of sounds, only single-channel (mono) files are
+supported. Otherwise OpenAL will play them non-positionally.
+
+Mods should generally prefix their sounds with `modname_`, e.g. given
+the mod name "`foomod`", a sound could be called:
+
+ foomod_foosound.ogg
+
+Sounds are referred to by their name with a dot, a single digit and the
+file extension stripped out. When a sound is played, the actual sound file
+is chosen randomly from the matching sounds.
+
+When playing the sound `foomod_foosound`, the sound is chosen randomly
+from the available ones of the following files:
+
+* `foomod_foosound.ogg`
+* `foomod_foosound.0.ogg`
+* `foomod_foosound.1.ogg`
+* (...)
+* `foomod_foosound.9.ogg`
+
+Examples of sound parameter tables:
+
+ -- Play locationless on all clients
+ {
+ gain = 1.0, -- default
+ fade = 0.0, -- default, change to a value > 0 to fade the sound in
+ }
+ -- Play locationless to one player
+ {
+ to_player = name,
+ gain = 1.0, -- default
+ fade = 0.0, -- default, change to a value > 0 to fade the sound in
+ }
+ -- Play locationless to one player, looped
+ {
+ to_player = name,
+ gain = 1.0, -- default
+ loop = true,
+ }
+ -- Play in a location
+ {
+ pos = {x = 1, y = 2, z = 3},
+ gain = 1.0, -- default
+ max_hear_distance = 32, -- default, uses an euclidean metric
+ }
+ -- Play connected to an object, looped
+ {
+ object = ,
+ gain = 1.0, -- default
+ max_hear_distance = 32, -- default, uses an euclidean metric
+ loop = true,
+ }
+
+Looped sounds must either be connected to an object or played locationless to
+one player using `to_player = name,`
+
+### `SimpleSoundSpec`
+* e.g. `""`
+* e.g. `"default_place_node"`
+* e.g. `{}`
+* e.g. `{name = "default_place_node"}`
+* e.g. `{name = "default_place_node", gain = 1.0}`
+
+## Registered definitions of stuff
+-------------------------------
+Anything added using certain `minetest.register_*` functions get added to
+the global `minetest.registered_*` tables.
+
+* `minetest.register_entity(name, prototype table)`
+ * added to `minetest.registered_entities[name]`
+
+* `minetest.register_node(name, node definition)`
+ * added to `minetest.registered_items[name]`
+ * added to `minetest.registered_nodes[name]`
+
+* `minetest.register_tool(name, item definition)`
+ * added to `minetest.registered_items[name]`
+
+* `minetest.register_craftitem(name, item definition)`
+ * added to `minetest.registered_items[name]`
+
+* `minetest.unregister_item(name)`
+ * Unregisters the item name from engine, and deletes the entry with key
+ * `name` from `minetest.registered_items` and from the associated item
+ * table according to its nature: `minetest.registered_nodes[]` etc
+
+* `minetest.register_biome(biome definition)`
+ * returns an integer uniquely identifying the registered biome
+ * added to `minetest.registered_biome` with the key of `biome.name`
+ * if `biome.name` is nil, the key is the returned ID
+
+* `minetest.register_ore(ore definition)`
+ * returns an integer uniquely identifying the registered ore
+ * added to `minetest.registered_ores` with the key of `ore.name`
+ * if `ore.name` is nil, the key is the returned ID
+
+* `minetest.register_decoration(decoration definition)`
+ * returns an integer uniquely identifying the registered decoration
+ * added to `minetest.registered_decorations` with the key of `decoration.name`
+ * if `decoration.name` is nil, the key is the returned ID
+
+* `minetest.register_schematic(schematic definition)`
+ * returns an integer uniquely identifying the registered schematic
+ * added to `minetest.registered_schematic` with the key of `schematic.name`
+ * if `schematic.name` is nil, the key is the returned ID
+ * if the schematic is loaded from a file, schematic.name is set to the filename
+ * if the function is called when loading the mod, and schematic.name is a relative
+ path, then the current mod path will be prepended to the schematic filename
+
+* `minetest.clear_registered_biomes()`
+ * clears all biomes currently registered
+
+* `minetest.clear_registered_ores()`
+ * clears all ores currently registered
+
+* `minetest.clear_registered_decorations()`
+ * clears all decorations currently registered
+
+* `minetest.clear_registered_schematics()`
+ * clears all schematics currently registered
+
+Note that in some cases you will stumble upon things that are not contained
+in these tables (e.g. when a mod has been removed). Always check for
+existence before trying to access the fields.
+
+Example: If you want to check the drawtype of a node, you could do:
+
+ local function get_nodedef_field(nodename, fieldname)
+ if not minetest.registered_nodes[nodename] then
+ return nil
+ end
+ return minetest.registered_nodes[nodename][fieldname]
+ end
+ local drawtype = get_nodedef_field(nodename, "drawtype")
+
+Example: `minetest.get_item_group(name, group)` has been implemented as:
+
+ function minetest.get_item_group(name, group)
+ if not minetest.registered_items[name] or not
+ minetest.registered_items[name].groups[group] then
+ return 0
+ end
+ return minetest.registered_items[name].groups[group]
+ end
+
+## Nodes
+-----
+Nodes are the bulk data of the world: cubes and other things that take the
+space of a cube. Huge amounts of them are handled efficiently, but they
+are quite static.
+
+The definition of a node is stored and can be accessed by name in
+
+ minetest.registered_nodes[node.name]
+
+See "Registered definitions of stuff".
+
+Nodes are passed by value between Lua and the engine.
+They are represented by a table:
+
+ {name="name", param1=num, param2=num}
+
+`param1` and `param2` are 8-bit integers ranging from 0 to 255. The engine uses
+them for certain automated functions. If you don't use these functions, you can
+use them to store arbitrary values.
+
+The functions of `param1` and `param2` are determined by certain fields in the
+node definition:
+
+`param1` is reserved for the engine when `paramtype != "none"`:
+
+ paramtype = "light"
+ ^ The value stores light with and without sun in its upper and lower 4 bits
+ respectively. Allows light to propagate from or through the node with
+ light value falling by 1 per node. This is essential for a light source
+ node to spread its light.
+
+`param2` is reserved for the engine when any of these are used:
+
+ liquidtype == "flowing"
+ ^ The level and some flags of the liquid is stored in param2
+ drawtype == "flowingliquid"
+ ^ The drawn liquid level is read from param2
+ drawtype == "torchlike"
+ drawtype == "signlike"
+ paramtype2 == "wallmounted"
+ ^ The rotation of the node is stored in param2. You can make this value
+ by using minetest.dir_to_wallmounted().
+ paramtype2 == "facedir"
+ ^ The rotation of the node is stored in param2. Furnaces and chests are
+ rotated this way. Can be made by using minetest.dir_to_facedir().
+ Values range 0 - 23
+ facedir / 4 = axis direction:
+ 0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y-
+ facedir modulo 4 = rotation around that axis
+ paramtype2 == "leveled"
+ paramtype2 == "degrotate"
+ ^ The rotation of this node is stored in param2. Plants are rotated this way.
+ Values range 0 - 179. The value stored in param2 is multiplied by two to
+ get the actual rotation of the node.
+ paramtype2 == "meshoptions"
+ ^ Only valid for "plantlike". The value of param2 becomes a bitfield which can
+ be used to change how the client draws plantlike nodes. Bits 0, 1 and 2 form
+ a mesh selector. Currently the following meshes are choosable:
+ 0 = a "x" shaped plant (ordinary plant)
+ 1 = a "+" shaped plant (just rotated 45 degrees)
+ 2 = a "*" shaped plant with 3 faces instead of 2
+ 3 = a "#" shaped plant with 4 faces instead of 2
+ 4 = a "#" shaped plant with 4 faces that lean outwards
+ 5-7 are unused and reserved for future meshes.
+ Bits 3 through 7 are optional flags that can be combined and give these
+ effects:
+ bit 3 (0x08) - Makes the plant slightly vary placement horizontally
+ bit 4 (0x10) - Makes the plant mesh 1.4x larger
+ bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max)
+ bits 6-7 are reserved for future use.
+ paramtype2 == "color"
+ ^ `param2` tells which color is picked from the palette.
+ The palette should have 256 pixels.
+ paramtype2 == "colorfacedir"
+ ^ Same as `facedir`, but with colors.
+ The first three bits of `param2` tells which color
+ is picked from the palette.
+ The palette should have 8 pixels.
+ paramtype2 == "colorwallmounted"
+ ^ Same as `wallmounted`, but with colors.
+ The first five bits of `param2` tells which color
+ is picked from the palette.
+ The palette should have 32 pixels.
+ paramtype2 == "glasslikeliquidlevel"
+ ^ Only valid for "glasslike_framed" or "glasslike_framed_optional" drawtypes.
+ param2 defines 64 levels of internal liquid.
+ Liquid texture is defined using `special_tiles = {"modname_tilename.png"},`
+
+Nodes can also contain extra data. See "Node Metadata".
+
+Node drawtypes
+---------------
+There are a bunch of different looking node types.
+
+Look for examples in `games/minimal` or `games/minetest_game`.
+
+* `normal`
+* `airlike`
+* `liquid`
+* `flowingliquid`
+* `glasslike`
+* `glasslike_framed`
+* `glasslike_framed_optional`
+* `allfaces`
+* `allfaces_optional`
+* `torchlike`
+* `signlike`
+* `plantlike`
+* `firelike`
+* `fencelike`
+* `raillike`
+* `nodebox` -- See below. (**Experimental!**)
+* `mesh` -- use models for nodes
+
+`*_optional` drawtypes need less rendering time if deactivated (always client side).
+
+Node boxes
+-----------
+Node selection boxes are defined using "node boxes"
+
+The `nodebox` node drawtype allows defining visual of nodes consisting of
+arbitrary number of boxes. It allows defining stuff like stairs. Only the
+`fixed` and `leveled` box type is supported for these.
+
+Please note that this is still experimental, and may be incompatibly
+changed in the future.
+
+A nodebox is defined as any of:
+
+ {
+ -- A normal cube; the default in most things
+ type = "regular"
+ }
+ {
+ -- A fixed box (facedir param2 is used, if applicable)
+ type = "fixed",
+ fixed = box OR {box1, box2, ...}
+ }
+ {
+ -- A box like the selection box for torches
+ -- (wallmounted param2 is used, if applicable)
+ type = "wallmounted",
+ wall_top = box,
+ wall_bottom = box,
+ wall_side = box
+ }
+ {
+ -- A node that has optional boxes depending on neighbouring nodes'
+ -- presence and type. See also `connects_to`.
+ type = "connected",
+ fixed = box OR {box1, box2, ...}
+ connect_top = box OR {box1, box2, ...}
+ connect_bottom = box OR {box1, box2, ...}
+ connect_front = box OR {box1, box2, ...}
+ connect_left = box OR {box1, box2, ...}
+ connect_back = box OR {box1, box2, ...}
+ connect_right = box OR {box1, box2, ...}
+ }
+
+A `box` is defined as:
+
+ {x1, y1, z1, x2, y2, z2}
+
+A box of a regular node would look like:
+
+ {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
+
+`type = "leveled"` is same as `type = "fixed"`, but `y2` will be automatically
+set to level from `param2`.
+
+
+Meshes
+------
+If drawtype `mesh` is used, tiles should hold model materials textures.
+Only static meshes are implemented.
+For supported model formats see Irrlicht engine documentation.
+
+
+Noise Parameters
+----------------
+Noise Parameters, or commonly called "`NoiseParams`", define the properties of
+perlin noise.
+
+### `offset`
+Offset that the noise is translated by (i.e. added) after calculation.
+
+### `scale`
+Factor that the noise is scaled by (i.e. multiplied) after calculation.
+
+### `spread`
+Vector containing values by which each coordinate is divided by before calculation.
+Higher spread values result in larger noise features.
+
+A value of `{x=250, y=250, z=250}` is common.
+
+### `seed`
+Random seed for the noise. Add the world seed to a seed offset for world-unique noise.
+In the case of `minetest.get_perlin()`, this value has the world seed automatically added.
+
+### `octaves`
+Number of times the noise gradient is accumulated into the noise.
+
+Increase this number to increase the amount of detail in the resulting noise.
+
+A value of `6` is common.
+
+### `persistence`
+Factor by which the effect of the noise gradient function changes with each successive octave.
+
+Values less than `1` make the details of successive octaves' noise diminish, while values
+greater than `1` make successive octaves stronger.
+
+A value of `0.6` is common.
+
+### `lacunarity`
+Factor by which the noise feature sizes change with each successive octave.
+
+A value of `2.0` is common.
+
+### `flags`
+Leave this field unset for no special handling.
+
+Currently supported are `defaults`, `eased` and `absvalue`.
+
+#### `defaults`
+Specify this if you would like to keep auto-selection of eased/not-eased while specifying
+some other flags.
+
+#### `eased`
+Maps noise gradient values onto a quintic S-curve before performing interpolation.
+This results in smooth, rolling noise. Disable this (`noeased`) for sharp-looking noise.
+If no flags are specified (or defaults is), 2D noise is eased and 3D noise is not eased.
+
+#### `absvalue`
+Accumulates the absolute value of each noise gradient result.
+
+Noise parameters format example for 2D or 3D perlin noise or perlin noise maps:
+
+ np_terrain = {
+ offset = 0,
+ scale = 1,
+ spread = {x=500, y=500, z=500},
+ seed = 571347,
+ octaves = 5,
+ persist = 0.63,
+ lacunarity = 2.0,
+ flags = "defaults, absvalue"
+ }
+ ^ A single noise parameter table can be used to get 2D or 3D noise,
+ when getting 2D noise spread.z is ignored.
+
+
+Ore types
+---------
+These tell in what manner the ore is generated.
+
+All default ores are of the uniformly-distributed scatter type.
+
+### `scatter`
+Randomly chooses a location and generates a cluster of ore.
+
+If `noise_params` is specified, the ore will be placed if the 3D perlin noise at
+that point is greater than the `noise_threshold`, giving the ability to create
+a non-equal distribution of ore.
+
+### `sheet`
+Creates a sheet of ore in a blob shape according to the 2D perlin noise
+described by `noise_params` and `noise_threshold`. This is essentially an
+improved version of the so-called "stratus" ore seen in some unofficial mods.
+
+This sheet consists of vertical columns of uniform randomly distributed height,
+varying between the inclusive range `column_height_min` and `column_height_max`.
+If `column_height_min` is not specified, this parameter defaults to 1.
+If `column_height_max` is not specified, this parameter defaults to `clust_size`
+for reverse compatibility. New code should prefer `column_height_max`.
+
+The `column_midpoint_factor` parameter controls the position of the column at which
+ore eminates from. If 1, columns grow upward. If 0, columns grow downward. If 0.5,
+columns grow equally starting from each direction. `column_midpoint_factor` is a
+decimal number ranging in value from 0 to 1. If this parameter is not specified,
+the default is 0.5.
+
+The ore parameters `clust_scarcity` and `clust_num_ores` are ignored for this ore type.
+
+### `puff`
+Creates a sheet of ore in a cloud-like puff shape.
+
+As with the `sheet` ore type, the size and shape of puffs are described by
+`noise_params` and `noise_threshold` and are placed at random vertical positions
+within the currently generated chunk.
+
+The vertical top and bottom displacement of each puff are determined by the noise
+parameters `np_puff_top` and `np_puff_bottom`, respectively.
+
+
+### `blob`
+Creates a deformed sphere of ore according to 3d perlin noise described by
+`noise_params`. The maximum size of the blob is `clust_size`, and
+`clust_scarcity` has the same meaning as with the `scatter` type.
+
+### `vein`
+Creates veins of ore varying in density by according to the intersection of two
+instances of 3d perlin noise with diffferent seeds, both described by
+`noise_params`. `random_factor` varies the influence random chance has on
+placement of an ore inside the vein, which is `1` by default. Note that
+modifying this parameter may require adjusting `noise_threshold`.
+The parameters `clust_scarcity`, `clust_num_ores`, and `clust_size` are ignored
+by this ore type. This ore type is difficult to control since it is sensitive
+to small changes. The following is a decent set of parameters to work from:
+
+ noise_params = {
+ offset = 0,
+ scale = 3,
+ spread = {x=200, y=200, z=200},
+ seed = 5390,
+ octaves = 4,
+ persist = 0.5,
+ flags = "eased",
+ },
+ noise_threshold = 1.6
+
+**WARNING**: Use this ore type *very* sparingly since it is ~200x more
+computationally expensive than any other ore.
+
+Ore attributes
+--------------
+See section "Flag Specifier Format".
+
+Currently supported flags:
+`absheight`, `puff_cliffs`, `puff_additive_composition`.
+
+### `absheight`
+Also produce this same ore between the height range of `-y_max` and `-y_min`.
+
+Useful for having ore in sky realms without having to duplicate ore entries.
+
+### `puff_cliffs`
+If set, puff ore generation will not taper down large differences in displacement
+when approaching the edge of a puff. This flag has no effect for ore types other
+than `puff`.
+
+### `puff_additive_composition`
+By default, when noise described by `np_puff_top` or `np_puff_bottom` results in a
+negative displacement, the sub-column at that point is not generated. With this
+attribute set, puff ore generation will instead generate the absolute difference in
+noise displacement values. This flag has no effect for ore types other than `puff`.
+
+Decoration types
+----------------
+The varying types of decorations that can be placed.
+
+### `simple`
+Creates a 1 times `H` times 1 column of a specified node (or a random node from
+a list, if a decoration list is specified). Can specify a certain node it must
+spawn next to, such as water or lava, for example. Can also generate a
+decoration of random height between a specified lower and upper bound.
+This type of decoration is intended for placement of grass, flowers, cacti,
+papyri, waterlilies and so on.
+
+### `schematic`
+Copies a box of `MapNodes` from a specified schematic file (or raw description).
+Can specify a probability of a node randomly appearing when placed.
+This decoration type is intended to be used for multi-node sized discrete
+structures, such as trees, cave spikes, rocks, and so on.
+
+
+Schematic specifier
+--------------------
+A schematic specifier identifies a schematic by either a filename to a
+Minetest Schematic file (`.mts`) or through raw data supplied through Lua,
+in the form of a table. This table specifies the following fields:
+
+* The `size` field is a 3D vector containing the dimensions of the provided schematic. (required)
+* The `yslice_prob` field is a table of {ypos, prob} which sets the `ypos`th vertical slice
+ of the schematic to have a `prob / 256 * 100` chance of occuring. (default: 255)
+* The `data` field is a flat table of MapNode tables making up the schematic,
+ in the order of `[z [y [x]]]`. (required)
+ Each MapNode table contains:
+ * `name`: the name of the map node to place (required)
+ * `prob` (alias `param1`): the probability of this node being placed (default: 255)
+ * `param2`: the raw param2 value of the node being placed onto the map (default: 0)
+ * `force_place`: boolean representing if the node should forcibly overwrite any
+ previous contents (default: false)
+
+About probability values:
+
+* A probability value of `0` or `1` means that node will never appear (0% chance).
+* A probability value of `254` or `255` means the node will always appear (100% chance).
+* If the probability value `p` is greater than `1`, then there is a
+ `(p / 256 * 100)` percent chance that node will appear when the schematic is
+ placed on the map.
+
+
+Schematic attributes
+--------------------
+See section "Flag Specifier Format".
+
+Currently supported flags: `place_center_x`, `place_center_y`, `place_center_z`,
+ `force_placement`.
+
+* `place_center_x`: Placement of this decoration is centered along the X axis.
+* `place_center_y`: Placement of this decoration is centered along the Y axis.
+* `place_center_z`: Placement of this decoration is centered along the Z axis.
+* `force_placement`: Schematic nodes other than "ignore" will replace existing nodes.
+
+
+HUD element types
+-----------------
+The position field is used for all element types.
+
+To account for differing resolutions, the position coordinates are the percentage
+of the screen, ranging in value from `0` to `1`.
+
+The name field is not yet used, but should contain a description of what the
+HUD element represents. The direction field is the direction in which something
+is drawn.
+
+`0` draws from left to right, `1` draws from right to left, `2` draws from
+top to bottom, and `3` draws from bottom to top.
+
+The `alignment` field specifies how the item will be aligned. It ranges from `-1` to `1`,
+with `0` being the center, `-1` is moved to the left/up, and `1` is to the right/down.
+Fractional values can be used.
+
+The `offset` field specifies a pixel offset from the position. Contrary to position,
+the offset is not scaled to screen size. This allows for some precisely-positioned
+items in the HUD.
+
+**Note**: `offset` _will_ adapt to screen DPI as well as user defined scaling factor!
+
+Below are the specific uses for fields in each type; fields not listed for that type are ignored.
+
+**Note**: Future revisions to the HUD API may be incompatible; the HUD API is still
+in the experimental stages.
+
+### `image`
+Displays an image on the HUD.
+
+* `scale`: The scale of the image, with 1 being the original texture size.
+ Only the X coordinate scale is used (positive values).
+ Negative values represent that percentage of the screen it
+ should take; e.g. `x=-100` means 100% (width).
+* `text`: The name of the texture that is displayed.
+* `alignment`: The alignment of the image.
+* `offset`: offset in pixels from position.
+
+### `text`
+Displays text on the HUD.
+
+* `scale`: Defines the bounding rectangle of the text.
+ A value such as `{x=100, y=100}` should work.
+* `text`: The text to be displayed in the HUD element.
+* `number`: An integer containing the RGB value of the color used to draw the text.
+ Specify `0xFFFFFF` for white text, `0xFF0000` for red, and so on.
+* `alignment`: The alignment of the text.
+* `offset`: offset in pixels from position.
+
+### `statbar`
+Displays a horizontal bar made up of half-images.
+
+* `text`: The name of the texture that is used.
+* `number`: The number of half-textures that are displayed.
+ If odd, will end with a vertically center-split texture.
+* `direction`
+* `offset`: offset in pixels from position.
+* `size`: If used, will force full-image size to this value (override texture pack image size)
+
+### `inventory`
+* `text`: The name of the inventory list to be displayed.
+* `number`: Number of items in the inventory to be displayed.
+* `item`: Position of item that is selected.
+* `direction`
+* `offset`: offset in pixels from position.
+
+### `waypoint`
+Displays distance to selected world position.
+
+* `name`: The name of the waypoint.
+* `text`: Distance suffix. Can be blank.
+* `number:` An integer containing the RGB value of the color used to draw the text.
+* `world_pos`: World position of the waypoint.
+
+Representations of simple things
+--------------------------------
+
+### Position/vector
+
+ {x=num, y=num, z=num}
+
+For helper functions see "Vector helpers".
+
+### `pointed_thing`
+* `{type="nothing"}`
+* `{type="node", under=pos, above=pos}`
+* `{type="object", ref=ObjectRef}`
+
+Flag Specifier Format
+---------------------
+Flags using the standardized flag specifier format can be specified in either of
+two ways, by string or table.
+
+The string format is a comma-delimited set of flag names; whitespace and
+unrecognized flag fields are ignored. Specifying a flag in the string sets the
+flag, and specifying a flag prefixed by the string `"no"` explicitly
+clears the flag from whatever the default may be.
+
+In addition to the standard string flag format, the schematic flags field can
+also be a table of flag names to boolean values representing whether or not the
+flag is set. Additionally, if a field with the flag name prefixed with `"no"`
+is present, mapped to a boolean of any value, the specified flag is unset.
+
+E.g. A flag field of value
+
+ {place_center_x = true, place_center_y=false, place_center_z=true}
+
+is equivalent to
+
+ {place_center_x = true, noplace_center_y=true, place_center_z=true}
+
+which is equivalent to
+
+ "place_center_x, noplace_center_y, place_center_z"
+
+or even
+
+ "place_center_x, place_center_z"
+
+since, by default, no schematic attributes are set.
+
+Items
+-----
+
+### Item types
+There are three kinds of items: nodes, tools and craftitems.
+
+* Node (`register_node`): A node from the world.
+* Tool (`register_tool`): A tool/weapon that can dig and damage
+ things according to `tool_capabilities`.
+* Craftitem (`register_craftitem`): A miscellaneous item.
+
+### Amount and wear
+All item stacks have an amount between 0 to 65535. It is 1 by
+default. Tool item stacks can not have an amount greater than 1.
+
+Tools use a wear (=damage) value ranging from 0 to 65535. The
+value 0 is the default and used is for unworn tools. The values
+1 to 65535 are used for worn tools, where a higher value stands for
+a higher wear. Non-tools always have a wear value of 0.
+
+### Item formats
+Items and item stacks can exist in three formats: Serializes, table format
+and `ItemStack`.
+
+#### Serialized
+This is called "stackstring" or "itemstring". It is a simple string with
+1-3 components: the full item identifier, an optional amount and an optional
+wear value. Syntax:
+
+ [[ ]]
+
+Examples:
+
+* `'default:apple'`: 1 apple
+* `'default:dirt 5'`: 5 dirt
+* `'default:pick_stone'`: a new stone pickaxe
+* `'default:pick_wood 1 21323'`: a wooden pickaxe, ca. 1/3 worn out
+
+#### Table format
+Examples:
+
+5 dirt nodes:
+
+ {name="default:dirt", count=5, wear=0, metadata=""}
+
+A wooden pick about 1/3 worn out:
+
+ {name="default:pick_wood", count=1, wear=21323, metadata=""}
+
+An apple:
+
+ {name="default:apple", count=1, wear=0, metadata=""}
+
+#### `ItemStack`
+A native C++ format with many helper methods. Useful for converting
+between formats. See the Class reference section for details.
+
+When an item must be passed to a function, it can usually be in any of
+these formats.
+
+
+## Groups
+------
+In a number of places, there is a group table. Groups define the
+properties of a thing (item, node, armor of entity, capabilities of
+tool) in such a way that the engine and other mods can can interact with
+the thing without actually knowing what the thing is.
+
+### Usage
+Groups are stored in a table, having the group names with keys and the
+group ratings as values. For example:
+
+ groups = {crumbly=3, soil=1}
+ -- ^ Default dirt
+
+ groups = {crumbly=2, soil=1, level=2, outerspace=1}
+ -- ^ A more special dirt-kind of thing
+
+Groups always have a rating associated with them. If there is no
+useful meaning for a rating for an enabled group, it shall be `1`.
+
+When not defined, the rating of a group defaults to `0`. Thus when you
+read groups, you must interpret `nil` and `0` as the same value, `0`.
+
+You can read the rating of a group for an item or a node by using
+
+ minetest.get_item_group(itemname, groupname)
+
+### Groups of items
+Groups of items can define what kind of an item it is (e.g. wool).
+
+### Groups of nodes
+In addition to the general item things, groups are used to define whether
+a node is destroyable and how long it takes to destroy by a tool.
+
+### Groups of entities
+For entities, groups are, as of now, used only for calculating damage.
+The rating is the percentage of damage caused by tools with this damage group.
+See "Entity damage mechanism".
+
+ object.get_armor_groups() --> a group-rating table (e.g. {fleshy=100})
+ object.set_armor_groups({fleshy=30, cracky=80})
+
+### Groups of tools
+Groups in tools define which groups of nodes and entities they are
+effective towards.
+
+### Groups in crafting recipes
+An example: Make meat soup from any meat, any water and any bowl:
+
+ {
+ output = 'food:meat_soup_raw',
+ recipe = {
+ {'group:meat'},
+ {'group:water'},
+ {'group:bowl'},
+ },
+ -- preserve = {'group:bowl'}, -- Not implemented yet (TODO)
+ }
+
+Another example: Make red wool from white wool and red dye:
+
+ {
+ type = 'shapeless',
+ output = 'wool:red',
+ recipe = {'wool:white', 'group:dye,basecolor_red'},
+ }
+
+### Special groups
+* `immortal`: Disables the group damage system for an entity
+* `punch_operable`: For entities; disables the regular damage mechanism for
+ players punching it by hand or a non-tool item, so that it can do something
+ else than take damage.
+* `level`: Can be used to give an additional sense of progression in the game.
+ * A larger level will cause e.g. a weapon of a lower level make much less
+ damage, and get worn out much faster, or not be able to get drops
+ from destroyed nodes.
+ * `0` is something that is directly accessible at the start of gameplay
+ * There is no upper limit
+* `dig_immediate`: (player can always pick up node without reducing tool wear)
+ * `2`: the node always gets the digging time 0.5 seconds (rail, sign)
+ * `3`: the node always gets the digging time 0 seconds (torch)
+* `disable_jump`: Player (and possibly other things) cannot jump from node
+* `fall_damage_add_percent`: damage speed = `speed * (1 + value/100)`
+* `bouncy`: value is bounce speed in percent
+* `falling_node`: if there is no walkable block under the node it will fall
+* `attached_node`: if the node under it is not a walkable block the node will be
+ dropped as an item. If the node is wallmounted the wallmounted direction is
+ checked.
+* `soil`: saplings will grow on nodes in this group
+* `connect_to_raillike`: makes nodes of raillike drawtype with same group value
+ connect to each other
+
+### Known damage and digging time defining groups
+* `crumbly`: dirt, sand
+* `cracky`: tough but crackable stuff like stone.
+* `snappy`: something that can be cut using fine tools; e.g. leaves, small
+ plants, wire, sheets of metal
+* `choppy`: something that can be cut using force; e.g. trees, wooden planks
+* `fleshy`: Living things like animals and the player. This could imply
+ some blood effects when hitting.
+* `explody`: Especially prone to explosions
+* `oddly_breakable_by_hand`:
+ Can be added to nodes that shouldn't logically be breakable by the
+ hand but are. Somewhat similar to `dig_immediate`, but times are more
+ like `{[1]=3.50,[2]=2.00,[3]=0.70}` and this does not override the
+ speed of a tool if the tool can dig at a faster speed than this
+ suggests for the hand.
+
+### Examples of custom groups
+Item groups are often used for defining, well, _groups of items_.
+
+* `meat`: any meat-kind of a thing (rating might define the size or healing
+ ability or be irrelevant -- it is not defined as of yet)
+* `eatable`: anything that can be eaten. Rating might define HP gain in half
+ hearts.
+* `flammable`: can be set on fire. Rating might define the intensity of the
+ fire, affecting e.g. the speed of the spreading of an open fire.
+* `wool`: any wool (any origin, any color)
+* `metal`: any metal
+* `weapon`: any weapon
+* `heavy`: anything considerably heavy
+
+### Digging time calculation specifics
+Groups such as `crumbly`, `cracky` and `snappy` are used for this
+purpose. Rating is `1`, `2` or `3`. A higher rating for such a group implies
+faster digging time.
+
+The `level` group is used to limit the toughness of nodes a tool can dig
+and to scale the digging times / damage to a greater extent.
+
+**Please do understand this**, otherwise you cannot use the system to it's
+full potential.
+
+Tools define their properties by a list of parameters for groups. They
+cannot dig other groups; thus it is important to use a standard bunch of
+groups to enable interaction with tools.
+
+#### Tools definition
+Tools define:
+
+* Full punch interval
+* Maximum drop level
+* For an arbitrary list of groups:
+ * Uses (until the tool breaks)
+ * Maximum level (usually `0`, `1`, `2` or `3`)
+ * Digging times
+ * Damage groups
+
+#### Full punch interval
+When used as a weapon, the tool will do full damage if this time is spent
+between punches. If e.g. half the time is spent, the tool will do half
+damage.
+
+#### Maximum drop level
+Suggests the maximum level of node, when dug with the tool, that will drop
+it's useful item. (e.g. iron ore to drop a lump of iron).
+
+This is not automated; it is the responsibility of the node definition
+to implement this.
+
+#### Uses
+Determines how many uses the tool has when it is used for digging a node,
+of this group, of the maximum level. For lower leveled nodes, the use count
+is multiplied by `3^leveldiff`.
+
+* `uses=10, leveldiff=0`: actual uses: 10
+* `uses=10, leveldiff=1`: actual uses: 30
+* `uses=10, leveldiff=2`: actual uses: 90
+
+#### Maximum level
+Tells what is the maximum level of a node of this group that the tool will
+be able to dig.
+
+#### Digging times
+List of digging times for different ratings of the group, for nodes of the
+maximum level.
+
+For example, as a Lua table, `times={2=2.00, 3=0.70}`. This would
+result in the tool to be able to dig nodes that have a rating of `2` or `3`
+for this group, and unable to dig the rating `1`, which is the toughest.
+Unless there is a matching group that enables digging otherwise.
+
+If the result digging time is 0, a delay of 0.15 seconds is added between
+digging nodes; If the player releases LMB after digging, this delay is set to 0,
+i.e. players can more quickly click the nodes away instead of holding LMB.
+
+#### Damage groups
+List of damage for groups of entities. See "Entity damage mechanism".
+
+#### Example definition of the capabilities of a tool
+
+ tool_capabilities = {
+ full_punch_interval=1.5,
+ max_drop_level=1,
+ groupcaps={
+ crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}}
+ }
+ damage_groups = {fleshy=2},
+ }
+
+This makes the tool be able to dig nodes that fulfil both of these:
+
+* Have the `crumbly` group
+* Have a `level` group less or equal to `2`
+
+Table of resulting digging times:
+
+ crumbly 0 1 2 3 4 <- level
+ -> 0 - - - - -
+ 1 0.80 1.60 1.60 - -
+ 2 0.60 1.20 1.20 - -
+ 3 0.40 0.80 0.80 - -
+
+ level diff: 2 1 0 -1 -2
+
+Table of resulting tool uses:
+
+ -> 0 - - - - -
+ 1 180 60 20 - -
+ 2 180 60 20 - -
+ 3 180 60 20 - -
+
+**Notes**:
+
+* At `crumbly==0`, the node is not diggable.
+* At `crumbly==3`, the level difference digging time divider kicks in and makes
+ easy nodes to be quickly breakable.
+* At `level > 2`, the node is not diggable, because it's `level > maxlevel`
+
+Entity damage mechanism
+-----------------------
+Damage calculation:
+
+ damage = 0
+ foreach group in cap.damage_groups:
+ damage += cap.damage_groups[group] * limit(actual_interval /
+ cap.full_punch_interval, 0.0, 1.0)
+ * (object.armor_groups[group] / 100.0)
+ -- Where object.armor_groups[group] is 0 for inexistent values
+ return damage
+
+Client predicts damage based on damage groups. Because of this, it is able to
+give an immediate response when an entity is damaged or dies; the response is
+pre-defined somehow (e.g. by defining a sprite animation) (not implemented;
+TODO).
+Currently a smoke puff will appear when an entity dies.
+
+The group `immortal` completely disables normal damage.
+
+Entities can define a special armor group, which is `punch_operable`. This
+group disables the regular damage mechanism for players punching it by hand or
+a non-tool item, so that it can do something else than take damage.
+
+On the Lua side, every punch calls:
+
+ entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction, damage)
+
+This should never be called directly, because damage is usually not handled by
+the entity itself.
+
+* `puncher` is the object performing the punch. Can be `nil`. Should never be
+ accessed unless absolutely required, to encourage interoperability.
+* `time_from_last_punch` is time from last punch (by `puncher`) or `nil`.
+* `tool_capabilities` can be `nil`.
+* `direction` is a unit vector, pointing from the source of the punch to
+ the punched object.
+* `damage` damage that will be done to entity
+Return value of this function will determin if damage is done by this function
+(retval true) or shall be done by engine (retval false)
+
+To punch an entity/object in Lua, call:
+
+ object:punch(puncher, time_from_last_punch, tool_capabilities, direction)
+
+* Return value is tool wear.
+* Parameters are equal to the above callback.
+* If `direction` equals `nil` and `puncher` does not equal `nil`,
+ `direction` will be automatically filled in based on the location of `puncher`.
+
+## Node Metadata
+-------------
+The instance of a node in the world normally only contains the three values
+mentioned in "Nodes". However, it is possible to insert extra data into a
+node. It is called "node metadata"; See `NodeMetaRef`.
+
+Node metadata contains two things:
+
+* A key-value store
+* An inventory
+
+Some of the values in the key-value store are handled specially:
+
+* `formspec`: Defines a right-click inventory menu. See "Formspec".
+* `infotext`: Text shown on the screen when the node is pointed at
+
+Example stuff:
+
+ local meta = minetest.get_meta(pos)
+ meta:set_string("formspec",
+ "size[8,9]"..
+ "list[context;main;0,0;8,4;]"..
+ "list[current_player;main;0,5;8,4;]")
+ meta:set_string("infotext", "Chest");
+ local inv = meta:get_inventory()
+ inv:set_size("main", 8*4)
+ print(dump(meta:to_table()))
+ meta:from_table({
+ inventory = {
+ main = {[1] = "default:dirt", [2] = "", [3] = "", [4] = "",
+ [5] = "", [6] = "", [7] = "", [8] = "", [9] = "",
+ [10] = "", [11] = "", [12] = "", [13] = "",
+ [14] = "default:cobble", [15] = "", [16] = "", [17] = "",
+ [18] = "", [19] = "", [20] = "default:cobble", [21] = "",
+ [22] = "", [23] = "", [24] = "", [25] = "", [26] = "",
+ [27] = "", [28] = "", [29] = "", [30] = "", [31] = "",
+ [32] = ""}
+ },
+ fields = {
+ formspec = "size[8,9]list[context;main;0,0;8,4;]list[current_player;main;0,5;8,4;]",
+ infotext = "Chest"
+ }
+ })
+
+Item Metadata
+-------------
+Item stacks can store metadata too. See `ItemStackMetaRef`.
+
+Item metadata only contains a key-value store.
+
+Some of the values in the key-value store are handled specially:
+
+* `description`: Set the item stack's description. Defaults to `idef.description`
+* `color`: A `ColorString`, which sets the stack's color.
+* `palette_index`: If the item has a palette, this is used to get the
+ current color from the palette.
+
+Example stuff:
+
+ local meta = stack:get_meta()
+ meta:set_string("key", "value")
+ print(dump(meta:to_table()))
+
+Formspec
+--------
+Formspec defines a menu. Currently not much else than inventories are
+supported. It is a string, with a somewhat strange format.
+
+Spaces and newlines can be inserted between the blocks, as is used in the
+examples.
+
+### Examples
+
+#### Chest
+
+ size[8,9]
+ list[context;main;0,0;8,4;]
+ list[current_player;main;0,5;8,4;]
+
+#### Furnace
+
+ size[8,9]
+ list[context;fuel;2,3;1,1;]
+ list[context;src;2,1;1,1;]
+ list[context;dst;5,1;2,2;]
+ list[current_player;main;0,5;8,4;]
+
+#### Minecraft-like player inventory
+
+ size[8,7.5]
+ image[1,0.6;1,2;player.png]
+ list[current_player;main;0,3.5;8,4;]
+ list[current_player;craft;3,0;3,3;]
+ list[current_player;craftpreview;7,1;1,1;]
+
+### Elements
+
+#### `size[,,]`
+* Define the size of the menu in inventory slots
+* `fixed_size`: `true`/`false` (optional)
+* deprecated: `invsize[,;]`
+
+#### `position[,]`
+* Define the position of the formspec
+* A value between 0.0 and 1.0 represents a position inside the screen
+* The default value is the center of the screen (0.5, 0.5)
+
+#### `anchor[,]`
+* Define the anchor of the formspec
+* A value between 0.0 and 1.0 represents an anchor inside the formspec
+* The default value is the center of the formspec (0.5, 0.5)
+
+#### `container[,]`
+* Start of a container block, moves all physical elements in the container by (X, Y)
+* Must have matching `container_end`
+* Containers can be nested, in which case the offsets are added
+ (child containers are relative to parent containers)
+
+#### `container_end[]`
+* End of a container, following elements are no longer relative to this container
+
+#### `list[;;,;,;]`
+* Show an inventory list
+
+#### `list[;;,;,;]`
+* Show an inventory list
+
+#### `listring[;]`
+* Allows to create a ring of inventory lists
+* Shift-clicking on items in one element of the ring
+ will send them to the next inventory list inside the ring
+* The first occurrence of an element inside the ring will
+ determine the inventory where items will be sent to
+
+#### `listring[]`
+* Shorthand for doing `listring[;]`
+ for the last two inventory lists added by list[...]
+
+#### `listcolors[;]`
+* Sets background color of slots as `ColorString`
+* Sets background color of slots on mouse hovering
+
+#### `listcolors[;;]`
+* Sets background color of slots as `ColorString`
+* Sets background color of slots on mouse hovering
+* Sets color of slots border
+
+#### `listcolors[;;;;]`
+* Sets background color of slots as `ColorString`
+* Sets background color of slots on mouse hovering
+* Sets color of slots border
+* Sets default background color of tooltips
+* Sets default font color of tooltips
+
+#### `tooltip[;;;]`
+* Adds tooltip for an element
+* `` tooltip background color as `ColorString` (optional)
+* `` tooltip font color as `ColorString` (optional)
+
+#### `image[,;,;]`
+* Show an image
+* Position and size units are inventory slots
+
+#### `item_image[,;,;]`
+* Show an inventory image of registered item/node
+* Position and size units are inventory slots
+
+#### `bgcolor[;]`
+* Sets background color of formspec as `ColorString`
+* If `true`, the background color is drawn fullscreen (does not effect the size of the formspec)
+
+#### `background[,;,;]`
+* Use a background. Inventory rectangles are not drawn then.
+* Position and size units are inventory slots
+* Example for formspec 8x4 in 16x resolution: image shall be sized
+ 8 times 16px times 4 times 16px.
+
+#### `background[,;,;;]`
+* Use a background. Inventory rectangles are not drawn then.
+* Position and size units are inventory slots
+* Example for formspec 8x4 in 16x resolution:
+ image shall be sized 8 times 16px times 4 times 16px
+* If `true` the background is clipped to formspec size
+ (`x` and `y` are used as offset values, `w` and `h` are ignored)
+
+#### `pwdfield[,;,;;