The huge item definition and item namespace unification patch (itemdef), see http...
authorKahrl <kahrl@gmx.net>
Thu, 12 Jan 2012 05:10:39 +0000 (06:10 +0100)
committerKahrl <kahrl@gmx.net>
Thu, 12 Jan 2012 05:10:39 +0000 (06:10 +0100)
65 files changed:
data/builtin.lua
data/clienttextures/unknown_item.png [new file with mode: 0644]
data/mods/bucket/init.lua
data/mods/default/init.lua
data/mods/experimental/init.lua
data/mods/legacy/init.lua
src/CMakeLists.txt
src/camera.cpp
src/camera.h
src/client.cpp
src/client.h
src/clientserver.h
src/connection.cpp
src/content_cao.cpp
src/content_mapblock.cpp
src/content_mapnode.cpp
src/content_mapnode.h
src/content_nodemeta.cpp
src/content_sao.cpp
src/content_sao.h
src/craftdef.cpp
src/craftdef.h
src/craftitemdef.cpp [deleted file]
src/craftitemdef.h [deleted file]
src/environment.h
src/game.cpp
src/gamedef.h
src/guiInventoryMenu.cpp
src/guiInventoryMenu.h
src/inventory.cpp
src/inventory.h
src/inventorymanager.cpp
src/inventorymanager.h
src/irrlichttypes.h
src/itemdef.cpp [new file with mode: 0644]
src/itemdef.h [new file with mode: 0644]
src/mapblock.cpp
src/mapblock.h
src/mapblock_mesh.cpp
src/mapblock_mesh.h
src/materials.cpp
src/materials.h
src/mesh.cpp
src/mesh.h
src/mineral.cpp
src/mineral.h
src/nodedef.cpp
src/nodedef.h
src/player.cpp
src/player.h
src/scriptapi.cpp
src/scriptapi.h
src/server.cpp
src/server.h
src/serverobject.cpp
src/serverobject.h
src/serverremoteplayer.cpp
src/serverremoteplayer.h
src/test.cpp
src/tile.cpp
src/tile.h
src/tooldef.cpp [deleted file]
src/tooldef.h [deleted file]
src/utility.cpp
src/utility.h

index 1046e934e5f566d32c2153fbe9be1a27c8187967..1926d88b4991d3a4cdc71cfad9742c35e0dfc45d 100644 (file)
@@ -80,15 +80,91 @@ function dump(o, dumped)
 end
 
 --
--- Built-in node definitions. Also defined in C.
+-- Item definition helpers
+--
+
+minetest.inventorycube = function(img1, img2, img3)
+       img2 = img2 or img1
+       img3 = img3 or img1
+       return "[inventorycube"
+                       .. "{" .. img1:gsub("%^", "&")
+                       .. "{" .. img2:gsub("%^", "&")
+                       .. "{" .. img3:gsub("%^", "&")
+end
+
+minetest.get_pointed_thing_position = function(pointed_thing, above)
+       if pointed_thing.type == "node" then
+               if above then
+                       -- The position where a node would be placed
+                       return pointed_thing.above
+               else
+                       -- The position where a node would be dug
+                       return pointed_thing.under
+               end
+       elseif pointed_thing.type == "object" then
+               obj = pointed.thing.ref
+               if obj ~= nil then
+                       return obj:getpos()
+               else
+                       return nil
+               end
+       else
+               return nil
+       end
+end
+
+function minetest.item_place(itemstack, placer, pointed_thing)
+       pos = minetest.get_pointed_thing_position(pointed_thing, true)
+       if pos ~= nil then
+               item = itemstack:take_item()
+               if item ~= nil then
+                       minetest.env:add_item(pos, item)
+               end
+       end
+       return itemstack
+end
+
+function minetest.item_drop(itemstack, dropper, pos)
+       minetest.env:add_item(pos, itemstack)
+       return ""
+end
+
+function minetest.item_eat(hp_change)
+       return function(itemstack, user, pointed_thing)  -- closure
+               if itemstack:take_item() ~= nil then
+                       user:set_hp(user:get_hp() + hp_change)
+               end
+               return itemstack
+       end
+end
+
+--
+-- Item definition defaults
 --
 
-minetest.register_nodedef_defaults({
+minetest.nodedef_default = {
+       -- Item properties
+       type="node",
        -- name intentionally not defined here
+       description = "",
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 99,
+       dropcount = -1,
+       usable = false,
+       liquids_pointable = false,
+       tool_digging_properties = nil,
+
+       -- Interaction callbacks
+       on_place = nil, -- let C handle node placement for now
+       on_drop = minetest.item_drop,
+       on_use = nil,
+
+       -- Node properties
        drawtype = "normal",
        visual_scale = 1.0,
-       tile_images = {"unknown_block.png"},
-       inventory_image = "unknown_block.png",
+       tile_images = {""},
        special_materials = {
                {image="", backface_culling=true},
                {image="", backface_culling=true},
@@ -104,8 +180,7 @@ minetest.register_nodedef_defaults({
        climbable = false,
        buildable_to = false,
        wall_mounted = false,
-       often_contains_mineral = false,
-       dug_item = "",
+       --dug_item intentionally not defined here
        extra_dug_item = "",
        extra_dug_item_rarity = 2,
        metadata_name = "",
@@ -124,219 +199,349 @@ minetest.register_nodedef_defaults({
                cuttability = 0,
                flammability = 0,
        },
-       cookresult_item = "", -- Cannot be cooked
-       furnace_cooktime = 3.0,
-       furnace_burntime = -1, -- Cannot be used as fuel
-})
+}
 
-minetest.register_node("air", {
-       drawtype = "airlike",
-       paramtype = "light",
-       sunlight_propagates = true,
-       walkable = false,
-       pointable = false,
-       diggable = false,
-       buildable_to = true,
-       air_equivalent = true,
-})
+minetest.craftitemdef_default = {
+       type="craft",
+       -- name intentionally not defined here
+       description = "",
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 99,
+       liquids_pointable = false,
+       tool_digging_properties = nil,
+
+       -- Interaction callbacks
+       on_place = minetest.item_place,
+       on_drop = minetest.item_drop,
+       on_use = nil,
+}
+
+minetest.tooldef_default = {
+       type="tool",
+       -- name intentionally not defined here
+       description = "",
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 1,
+       liquids_pointable = false,
+       tool_digging_properties = nil,
+
+       -- Interaction callbacks
+       on_place = minetest.item_place,
+       on_drop = minetest.item_drop,
+       on_use = nil,
+}
+
+minetest.noneitemdef_default = {  -- This is used for the hand and unknown items
+       type="none",
+       -- name intentionally not defined here
+       description = "",
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 99,
+       liquids_pointable = false,
+       tool_digging_properties = nil,
+
+       -- Interaction callbacks
+       on_place = nil,
+       on_drop = nil,
+       on_use = nil,
+}
 
-minetest.register_node("ignore", {
-       drawtype = "airlike",
-       paramtype = "none",
-       sunlight_propagates = false,
-       walkable = false,
-       pointable = false,
-       diggable = false,
-       buildable_to = true, -- A way to remove accidentally placed ignores
-       air_equivalent = true,
-})
+--
+-- Make raw registration functions inaccessible to anyone except builtin.lua
+--
+
+local register_item_raw = minetest.register_item_raw
+minetest.register_item_raw = nil
+
+local register_alias_raw = minetest.register_alias_raw
+minetest.register_item_raw = nil
 
 --
--- stackstring manipulation functions
--- example stackstring: 'craft "apple" 4'
--- example item: {type="craft", name="apple"}
--- example item: {type="tool", name="SteelPick", wear="23272"}
+-- Item / entity / ABM registration functions
 --
 
-function stackstring_take_item(stackstring)
-       if stackstring == nil then
-               return '', nil
-       end
-       local stacktype = nil
-       stacktype = string.match(stackstring,
-                       '([%a%d]+)')
-       if stacktype == "node" or stacktype == "craft" then
-               local itemtype = nil
-               local itemname = nil
-               local itemcount = nil
-               itemtype, itemname, itemcount = string.match(stackstring,
-                               '([%a%d]+) "([^"]*)" (%d+)')
-               itemcount = tonumber(itemcount)
-               if itemcount == 0 then
-                       return '', nil
-               elseif itemcount == 1 then
-                       return '', {type=itemtype, name=itemname}
-               else
-                       return itemtype.." \""..itemname.."\" "..(itemcount-1),
-                                       {type=itemtype, name=itemname}
+minetest.registered_abms = {}
+minetest.registered_entities = {}
+minetest.registered_items = {}
+minetest.registered_nodes = {}
+minetest.registered_craftitems = {}
+minetest.registered_tools = {}
+minetest.registered_aliases = {}
+
+-- For tables that are indexed by item name:
+-- If table[X] does not exist, default to table[minetest.registered_aliases[X]]
+local function set_alias_metatable(table)
+       setmetatable(table, {
+               __index = function(name)
+                       return rawget(table, minetest.registered_aliases[name])
+               end
+       })
+end
+set_alias_metatable(minetest.registered_items)
+set_alias_metatable(minetest.registered_nodes)
+set_alias_metatable(minetest.registered_craftitems)
+set_alias_metatable(minetest.registered_tools)
+
+-- These item names may not be used because they would interfere
+-- with legacy itemstrings
+local forbidden_item_names = {
+       MaterialItem = true,
+       MaterialItem2 = true,
+       MaterialItem3 = true,
+       NodeItem = true,
+       node = true,
+       CraftItem = true,
+       craft = true,
+       MBOItem = true,
+       ToolItem = true,
+       tool = true,
+}
+
+local function check_modname_prefix(name)
+       if name:sub(1,1) == ":" then
+               -- Escape the modname prefix enforcement mechanism
+               return name:sub(2)
+       else
+               -- Modname prefix enforcement
+               local expected_prefix = minetest.get_current_modname() .. ":"
+               if name:sub(1, #expected_prefix) ~= expected_prefix then
+                       error("Name " .. name .. " does not follow naming conventions: " ..
+                               "\"modname:\" or \":\" prefix required")
                end
-       elseif stacktype == "tool" then
-               local itemtype = nil
-               local itemname = nil
-               local itemwear = nil
-               itemtype, itemname, itemwear = string.match(stackstring,
-                               '([%a%d]+) "([^"]*)" (%d+)')
-               itemwear = tonumber(itemwear)
-               return '', {type=itemtype, name=itemname, wear=itemwear}
+               local subname = name:sub(#expected_prefix+1)
+               if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then
+                       error("Name " .. name .. " does not follow naming conventions: " ..
+                               "contains unallowed characters")
+               end
+               return name
        end
 end
 
-function stackstring_put_item(stackstring, item)
-       if item == nil then
-               return stackstring, false
+function minetest.register_abm(spec)
+       -- Add to minetest.registered_abms
+       minetest.registered_abms[#minetest.registered_abms+1] = spec
+end
+
+function minetest.register_entity(name, prototype)
+       -- Check name
+       if name == nil then
+               error("Unable to register entity: Name is nil")
        end
-       stackstring = stackstring or ''
-       local stacktype = nil
-       stacktype = string.match(stackstring,
-                       '([%a%d]+)')
-       stacktype = stacktype or ''
-       if stacktype ~= '' and stacktype ~= item.type then
-               return stackstring, false
+       name = check_modname_prefix(tostring(name))
+
+       prototype.name = name
+       prototype.__index = prototype  -- so that it can be used as a metatable
+
+       -- Add to minetest.registered_entities
+       minetest.registered_entities[name] = prototype
+end
+
+function minetest.register_item(name, itemdef)
+       -- Check name
+       if name == nil then
+               error("Unable to register item: Name is nil")
        end
-       if item.type == "node" or item.type == "craft" then
-               local itemtype = nil
-               local itemname = nil
-               local itemcount = nil
-               itemtype, itemname, itemcount = string.match(stackstring,
-                               '([%a%d]+) "([^"]*)" (%d+)')
-               itemtype = itemtype or item.type
-               itemname = itemname or item.name
-               if itemcount == nil then
-                       itemcount = 0
-               end
-               itemcount = itemcount + 1
-               return itemtype.." \""..itemname.."\" "..itemcount, true
-       elseif item.type == "tool" then
-               if stacktype ~= nil then
-                       return stackstring, false
-               end
-               local itemtype = nil
-               local itemname = nil
-               local itemwear = nil
-               itemtype, itemname, itemwear = string.match(stackstring,
-                               '([%a%d]+) "([^"]*)" (%d+)')
-               itemwear = tonumber(itemwear)
-               return itemtype.." \""..itemname.."\" "..itemwear, true
+       name = check_modname_prefix(tostring(name))
+       if forbidden_item_names[name] then
+               error("Unable to register item: Name is forbidden: " .. name)
+       end
+       itemdef.name = name
+
+       -- Apply defaults and add to registered_* table
+       if itemdef.type == "node" then
+               setmetatable(itemdef, {__index = minetest.nodedef_default})
+               minetest.registered_nodes[itemdef.name] = itemdef
+       elseif itemdef.type == "craft" then
+               setmetatable(itemdef, {__index = minetest.craftitemdef_default})
+               minetest.registered_craftitems[itemdef.name] = itemdef
+       elseif itemdef.type == "tool" then
+               setmetatable(itemdef, {__index = minetest.tooldef_default})
+               minetest.registered_tools[itemdef.name] = itemdef
+       elseif itemdef.type == "none" then
+               setmetatable(itemdef, {__index = minetest.noneitemdef_default})
+       else
+               error("Unable to register item: Type is invalid: " .. dump(itemdef))
        end
-       return stackstring, false
-end
 
-function stackstring_put_stackstring(stackstring, src)
-       while src ~= '' do
-               --print("src="..dump(src))
-               src, item = stackstring_take_item(src)
-               --print("src="..dump(src).." item="..dump(item))
-               local success
-               stackstring, success = stackstring_put_item(stackstring, item)
-               if not success then
-                       return stackstring, false
-               end
+       -- Default dug item
+       if itemdef.type == "node" and itemdef.dug_item == nil then
+               itemdef.dug_item = ItemStack({name=itemdef.name}):to_string()
        end
-       return stackstring, true
+
+       -- Legacy stuff
+       if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
+               minetest.register_craft({
+                       type="cooking",
+                       output=itemdef.cookresult_itemstring,
+                       recipe=itemdef.name,
+                       cooktime=itemdef.furnace_cooktime
+               })
+       end
+       if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
+               minetest.register_craft({
+                       type="fuel",
+                       recipe=itemdef.name,
+                       burntime=itemdef.furnace_burntime
+               })
+       end
+
+       -- Disable all further modifications
+       getmetatable(itemdef).__newindex = {}
+
+       --minetest.log("Registering item: " .. itemdef.name)
+       minetest.registered_items[itemdef.name] = itemdef
+       minetest.registered_aliases[itemdef.name] = nil
+       register_item_raw(itemdef)
 end
 
-function test_stackstring()
-       local stack
-       local item
-       local success
-
-       stack, item = stackstring_take_item('node "TNT" 3')
-       assert(stack == 'node "TNT" 2')
-       assert(item.type == 'node')
-       assert(item.name == 'TNT')
-
-       stack, item = stackstring_take_item('craft "with spaces" 2')
-       assert(stack == 'craft "with spaces" 1')
-       assert(item.type == 'craft')
-       assert(item.name == 'with spaces')
-
-       stack, item = stackstring_take_item('craft "with spaces" 1')
-       assert(stack == '')
-       assert(item.type == 'craft')
-       assert(item.name == 'with spaces')
-
-       stack, item = stackstring_take_item('craft "s8df2kj3" 0')
-       assert(stack == '')
-       assert(item == nil)
-
-       stack, item = stackstring_take_item('tool "With Spaces" 32487')
-       assert(stack == '')
-       assert(item.type == 'tool')
-       assert(item.name == 'With Spaces')
-       assert(item.wear == 32487)
-
-       stack, success = stackstring_put_item('node "With Spaces" 40',
-                       {type='node', name='With Spaces'})
-       assert(stack == 'node "With Spaces" 41')
-       assert(success == true)
-
-       stack, success = stackstring_put_item('craft "With Spaces" 40',
-                       {type='craft', name='With Spaces'})
-       assert(stack == 'craft "With Spaces" 41')
-       assert(success == true)
-
-       stack, success = stackstring_put_item('tool "With Spaces" 32487',
-                       {type='tool', name='With Spaces'})
-       assert(stack == 'tool "With Spaces" 32487')
-       assert(success == false)
-
-       stack, success = stackstring_put_item('node "With Spaces" 40',
-                       {type='tool', name='With Spaces'})
-       assert(stack == 'node "With Spaces" 40')
-       assert(success == false)
-       
-       assert(stackstring_put_stackstring('node "With Spaces" 2',
-                       'node "With Spaces" 1') == 'node "With Spaces" 3')
+function minetest.register_node(name, nodedef)
+       nodedef.type = "node"
+       minetest.register_item(name, nodedef)
 end
-test_stackstring()
 
---
--- NodeItem helpers
---
+function minetest.register_craftitem(name, craftitemdef)
+       craftitemdef.type = "craft"
 
-minetest.inventorycube = function(img1, img2, img3)
-       img2 = img2 or img1
-       img3 = img3 or img1
-       return "[inventorycube"
-                       .. "{" .. img1:gsub("%^", "&")
-                       .. "{" .. img2:gsub("%^", "&")
-                       .. "{" .. img3:gsub("%^", "&")
+       -- Legacy stuff
+       if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
+               craftitemdef.inventory_image = craftitemdef.image
+       end
+
+       minetest.register_item(name, craftitemdef)
 end
 
---
--- CraftItem helpers
---
+function minetest.register_tool(name, tooldef)
+       tooldef.type = "tool"
+       tooldef.stack_max = 1
+
+       -- Legacy stuff
+       if tooldef.inventory_image == nil and tooldef.image ~= nil then
+               tooldef.inventory_image = tooldef.image
+       end
+       if tooldef.tool_digging_properties == nil and
+          (tooldef.full_punch_interval ~= nil or
+           tooldef.basetime ~= nil or
+           tooldef.dt_weight ~= nil or
+           tooldef.dt_crackiness ~= nil or
+           tooldef.dt_crumbliness ~= nil or
+           tooldef.dt_cuttability ~= nil or
+           tooldef.basedurability ~= nil or
+           tooldef.dd_weight ~= nil or
+           tooldef.dd_crackiness ~= nil or
+           tooldef.dd_crumbliness ~= nil or
+           tooldef.dd_cuttability ~= nil) then
+               tooldef.tool_digging_properties = {
+                       full_punch_interval = tooldef.full_punch_interval,
+                       basetime = tooldef.basetime,
+                       dt_weight = tooldef.dt_weight,
+                       dt_crackiness = tooldef.dt_crackiness,
+                       dt_crumbliness = tooldef.dt_crumbliness,
+                       dt_cuttability = tooldef.dt_cuttability,
+                       basedurability = tooldef.basedurability,
+                       dd_weight = tooldef.dd_weight,
+                       dd_crackiness = tooldef.dd_crackiness,
+                       dd_crumbliness = tooldef.dd_crumbliness,
+                       dd_cuttability = tooldef.dd_cuttability,
+               }
+       end
 
-minetest.craftitem_place_item = function(item, placer, pos)
-       --print("craftitem_place_item")
-       --print("item: " .. dump(item))
-       --print("placer: " .. dump(placer))
-       --print("pos: " .. dump(pos))
-       minetest.env:add_item(pos, 'craft "' .. item .. '" 1')
-       return true
+       minetest.register_item(name, tooldef)
 end
 
-minetest.craftitem_eat = function(hp_change)
-       return function(item, user, pointed_thing)  -- closure
-               --print("craftitem_eat(" .. hp_change .. ")")
-               --print("item: " .. dump(item))
-               --print("user: " .. dump(user))
-               --print("pointed_thing: " .. dump(pointed_thing))
-               user:set_hp(user:get_hp() + hp_change)
-               return true
+function minetest.register_alias(name, convert_to)
+       if forbidden_item_names[name] then
+               error("Unable to register alias: Name is forbidden: " .. name)
        end
+       if minetest.registered_items[name] ~= nil then
+               minetest.log("WARNING: Not registering alias, item with same name" ..
+                       " is already defined: " .. name .. " -> " .. convert_to)
+       else
+               --minetest.log("Registering alias: " .. name .. " -> " .. convert_to)
+               minetest.registered_aliases[name] = convert_to
+               register_alias_raw(name, convert_to)
+       end
+end
+
+-- Alias the forbidden item names to "" so they can't be
+-- created via itemstrings (e.g. /give)
+local name
+for name in pairs(forbidden_item_names) do
+       minetest.registered_aliases[name] = ""
+       register_alias_raw(name, "")
 end
 
+
+-- Deprecated:
+-- Aliases for minetest.register_alias (how ironic...)
+--minetest.alias_node = minetest.register_alias
+--minetest.alias_tool = minetest.register_alias
+--minetest.alias_craftitem = minetest.register_alias
+
+--
+-- Built-in node definitions. Also defined in C.
+--
+
+minetest.register_item(":", {
+       type = "none",
+       wield_image = "wieldhand.png",
+       wield_scale = {x=1,y=1,z=2.5},
+       tool_digging_properties = {
+               full_punch_interval = 2.0,
+               basetime = 0.5,
+               dt_weight = 1,
+               dt_crackiness = 0,
+               dt_crumbliness = -1,
+               dt_cuttability = 0,
+               basedurability = 50,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       }
+})
+
+minetest.register_item(":unknown", {
+       type = "none",
+       description = "Unknown Item",
+       inventory_image = "unknown_item.png",
+       on_place = minetest.item_place,
+       on_drop = minetest.item_drop,
+})
+
+minetest.register_node(":air", {
+       description = "Air (you hacker you!)",
+       inventory_image = "unknown_block.png",
+       wield_image = "unknown_block.png",
+       drawtype = "airlike",
+       paramtype = "light",
+       sunlight_propagates = true,
+       walkable = false,
+       pointable = false,
+       diggable = false,
+       buildable_to = true,
+       air_equivalent = true,
+})
+
+minetest.register_node(":ignore", {
+       description = "Ignore (you hacker you!)",
+       inventory_image = "unknown_block.png",
+       wield_image = "unknown_block.png",
+       drawtype = "airlike",
+       paramtype = "none",
+       sunlight_propagates = false,
+       walkable = false,
+       pointable = false,
+       diggable = false,
+       buildable_to = true, -- A way to remove accidentally placed ignores
+       air_equivalent = true,
+})
+
 --
 -- Default material types
 --
@@ -422,7 +627,7 @@ end
 -- Callback registration
 --
 
-function make_registration()
+local function make_registration()
        local t = {}
        local registerfunc = function(func) table.insert(t, func) end
        return t, registerfunc
diff --git a/data/clienttextures/unknown_item.png b/data/clienttextures/unknown_item.png
new file mode 100644 (file)
index 0000000..35cabf0
Binary files /dev/null and b/data/clienttextures/unknown_item.png differ
index 639a614d4baa080c39391acb73a60388a5c4efdf..8ed9da5228de4387ab776db9bcda39db4a8d761f 100644 (file)
@@ -1,80 +1,95 @@
 -- bucket (Minetest 0.4 mod)
 -- A bucket, which can pick up water and lava
 
-minetest.alias_craftitem("bucket", "bucket:bucket_empty")
-minetest.alias_craftitem("bucket_water", "bucket:bucket_water")
-minetest.alias_craftitem("bucket_lava", "bucket:bucket_lava")
+minetest.register_alias("bucket", "bucket:bucket_empty")
+minetest.register_alias("bucket_water", "bucket:bucket_water")
+minetest.register_alias("bucket_lava", "bucket:bucket_lava")
 
 minetest.register_craft({
-       output = 'craft "bucket:bucket_empty" 1',
+       output = 'bucket:bucket_empty 1',
        recipe = {
-               {'craft "steel_ingot"', '', 'craft "steel_ingot"'},
-               {'', 'craft "steel_ingot"', ''},
+               {'default:steel_ingot', '', 'default:steel_ingot'},
+               {'', 'default:steel_ingot', ''},
        }
 })
 
-minetest.register_craftitem("bucket:bucket_empty", {
-       image = "bucket.png",
-       stack_max = 1,
-       liquids_pointable = true,
-       on_place_on_ground = minetest.craftitem_place_item,
-       on_use = function(item, player, pointed_thing)
-               if pointed_thing.type == "node" then
-                       n = minetest.env:get_node(pointed_thing.under)
-                       if n.name == "default:water_source" then
-                               minetest.env:add_node(pointed_thing.under, {name="air"})
-                               player:add_to_inventory_later('craft "bucket:bucket_water" 1')
-                               return true
-                       elseif n.name == "default:lava_source" then
-                               minetest.env:add_node(pointed_thing.under, {name="air"})
-                               player:add_to_inventory_later('craft "bucket:bucket_lava" 1')
-                               return true
+bucket = {}
+bucket.liquids = {}
+
+-- Register a new liquid
+--   source = name of the source node
+--   flowing = name of the flowing node
+--   itemname = name of the new bucket item (or nil if liquid is not takeable)
+--   inventory_image = texture of the new bucket item (ignored if itemname == nil)
+-- This function can be called from any mod (that depends on bucket).
+function bucket.register_liquid(source, flowing, itemname, inventory_image)
+       bucket.liquids[source] = {
+               source = source,
+               flowing = flowing,
+               itemname = itemname,
+       }
+       bucket.liquids[flowing] = bucket.liquids[source]
+
+       if itemname ~= nil then
+               minetest.register_craftitem(itemname, {
+                       inventory_image = inventory_image,
+                       stack_max = 1,
+                       liquids_pointable = true,
+                       on_use = function(itemstack, user, pointed_thing)
+                               -- Must be pointing to node
+                               if pointed_thing.type ~= "node" then
+                                       return
+                               end
+                               -- Check if pointing to a liquid
+                               n = minetest.env:get_node(pointed_thing.under)
+                               if bucket.liquids[n.name] == nil then
+                                       -- Not a liquid
+                                       minetest.env:add_node(pointed_thing.above, {name=source})
+                               elseif n.name ~= source then
+                                       -- It's a liquid
+                                       minetest.env:add_node(pointed_thing.under, {name=source})
+                               end
+                               return {name="bucket:bucket_empty"}
                        end
-               end
-               return false
-       end,
-})
+               })
+       end
+end
 
-minetest.register_craftitem("bucket:bucket_water", {
-       image = "bucket_water.png",
+minetest.register_craftitem("bucket:bucket_empty", {
+       inventory_image = "bucket.png",
        stack_max = 1,
        liquids_pointable = true,
-       on_place_on_ground = minetest.craftitem_place_item,
-       on_use = function(item, player, pointed_thing)
-               if pointed_thing.type == "node" then
-                       n = minetest.env:get_node(pointed_thing.under)
-                       if n.name == "default:water_source" then
-                               -- unchanged
-                       elseif n.name == "default:water_flowing" or n.name == "default:lava_source" or n.name == "default:lava_flowing" then
-                               minetest.env:add_node(pointed_thing.under, {name="default:water_source"})
-                       else
-                               minetest.env:add_node(pointed_thing.above, {name="default:water_source"})
-                       end
-                       player:add_to_inventory_later('craft "bucket:bucket_empty" 1')
-                       return true
+       on_use = function(itemstack, user, pointed_thing)
+               -- Must be pointing to node
+               if pointed_thing.type ~= "node" then
+                       return
+               end
+               -- Check if pointing to a liquid source
+               n = minetest.env:get_node(pointed_thing.under)
+               liquiddef = bucket.liquids[n.name]
+               if liquiddef ~= nil and liquiddef.source == n.name and liquiddef.itemname ~= nil then
+                       minetest.env:add_node(pointed_thing.under, {name="air"})
+                       return {name=liquiddef.itemname}
                end
-               return false
        end,
 })
 
-minetest.register_craftitem("bucket:bucket_lava", {
-       image = "bucket_lava.png",
-       stack_max = 1,
-       liquids_pointable = true,
-       on_place_on_ground = minetest.craftitem_place_item,
-       on_use = function(item, player, pointed_thing)
-               if pointed_thing.type == "node" then
-                       n = minetest.env:get_node(pointed_thing.under)
-                       if n.name == "default:lava_source" then
-                               -- unchanged
-                       elseif n.name == "default:water_source" or n.name == "default:water_flowing" or n.name == "default:lava_flowing" then
-                               minetest.env:add_node(pointed_thing.under, {name="default:lava_source"})
-                       else
-                               minetest.env:add_node(pointed_thing.above, {name="default:lava_source"})
-                       end
-                       player:add_to_inventory_later('craft "bucket:bucket_empty" 1')
-                       return true
-               end
-               return false
-       end,
+bucket.register_liquid(
+       "default:water_source",
+       "default:water_flowing",
+       "bucket:bucket_water",
+       "bucket_water.png"
+)
+
+bucket.register_liquid(
+       "default:lava_source",
+       "default:lava_flowing",
+       "bucket:bucket_lava",
+       "bucket_lava.png"
+)
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:bucket_lava",
+       burntime = 60,
 })
index aa03eabe8d342daf5cbbef52041ac1f3c10fb029..2bd950230a1d6f764857f28c5edb0da10b904334 100644 (file)
 -- eg. 'tool "default:pick_wood" 21323'
 -- eg. 'craft "default:apple" 2'
 --
--- item: A single item in Lua table format.
--- eg. {type="node", name="default:dirt"} 
+-- item: A stack of items in Lua table format.
+-- eg. {name="default:dirt", count=1, wear=0, metadata=""} 
 --     ^ a single dirt node
--- eg. {type="tool", name="default:pick_wood", wear=21323}
+-- eg. {name="default:pick_wood", count=1, wear=21323, metadata=""}
 --     ^ a wooden pick about 1/3 weared out
--- eg. {type="craft", name="default:apple"}
+-- eg. {name="default:apple", count=1, wear=0, metadata=""}
 --     ^ an apple.
 --
+-- Any time an item must be passed to a function, it can be an
+-- ItemStack (see below), an itemstring or a table in the above format.
+--
 -- Global functions:
 -- minetest.register_entity(name, prototype table)
--- minetest.register_tool(name, tool definition)
+-- minetest.register_abm(abm definition)
 -- minetest.register_node(name, node definition)
--- minetest.register_craftitem(name, craftitem definition)
+-- minetest.register_tool(name, item definition)
+-- minetest.register_craftitem(name, item definition)
+-- minetest.register_alias(name, convert_to)
 -- minetest.register_craft(recipe)
--- minetest.register_abm(abm definition)
--- minetest.alias_node(name, convert_to)
--- minetest.alias_tool(name, convert_to)
--- minetest.alias_craftitem(name, convert_to)
 -- minetest.register_globalstep(func(dtime))
 -- minetest.register_on_placenode(func(pos, newnode, placer))
 -- minetest.register_on_dignode(func(pos, oldnode, digger))
 -- minetest.chat_send_player(name, text)
 -- minetest.get_player_privs(name) -> set of privs
 -- minetest.get_inventory(location) -> InvRef
+-- minetest.get_current_modname() -> string
 -- minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname"
 -- ^ location = eg. {type="player", name="celeron55"}
 --                  {type="node", pos={x=, y=, z=}}
 --
--- stackstring_take_item(stackstring) -> stackstring, item
--- stackstring_put_item(stackstring, item) -> stackstring, success
--- stackstring_put_stackstring(stackstring, stackstring) -> stackstring, success
+-- minetest.debug(line)
+-- ^ Goes to dstream
+-- minetest.log(line)
+-- minetest.log(loglevel, line)
+-- ^ loglevel one of "error", "action", "info", "verbose"
 --
 -- minetest.digprop_constanttime(time)
 -- minetest.digprop_stonelike(toughness)
 -- minetest.env - environment reference
 --
 -- Global tables:
+-- minetest.registered_items
+-- ^ List of registered items, indexed by name
 -- minetest.registered_nodes
 -- ^ List of registered node definitions, indexed by name
 -- minetest.registered_craftitems
 -- ^ List of registered craft item definitions, indexed by name
+-- minetest.registered_tools
+-- ^ List of registered tool definitions, indexed by name
 -- minetest.registered_entities
 -- ^ List of registered entity prototypes, indexed by name
 -- minetest.object_refs
 -- Generic node metadata specific:
 -- - set_infotext(infotext)
 -- - get_inventory() -> InvRef
--- - inventory_set_list(name, {item1, item2, ...})
--- - inventory_get_list(name)
 -- - set_inventory_draw_spec(string)
 -- - set_allow_text_input(bool)
 -- - set_allow_removal(bool)
 --   ^ puncher = an another ObjectRef,
 --   ^ time_from_last_punch = time since last punch action of the puncher
 -- - right_click(clicker); clicker = an another ObjectRef
--- - get_wield_digging_properties() -> digging property table
--- - damage_wielded_item(num) (item damage/wear range is 0-65535)
--- - add_to_inventory(itemstring): add an item to object inventory (actually only works for the player as of now)
--- - add_to_inventory_later(itemstring): like above, but after callback returns (only allowed for craftitem callbacks)
 -- - get_hp(): returns number of hitpoints (2 * number of hearts)
 -- - set_hp(hp): set number of hitpoints (2 * number of hearts)
+-- - get_inventory() -> InvRef
+-- - get_wield_list(): returns the name of the inventory list the wielded item is in
+-- - get_wield_index(): returns the index of the wielded item
+-- - get_wielded_item() -> ItemStack
+-- - set_wielded_item(item): replaces the wielded item, returns true if successful
 -- LuaEntitySAO-only: (no-op for other objects)
 -- - setvelocity({x=num, y=num, z=num})
 -- - getvelocity() -> {x=num, y=num, z=num}
 -- - get_luaentity()
 -- Player-only: (no-op for other objects)
 -- - get_player_name(): will return nil if is not a player
--- - get_inventory() -> InvRef
--- - inventory_set_list(name, {item1, item2, ...})
--- - inventory_get_list(name) -> {item1, item2, ...}
 -- - get_look_dir(): get camera direction as a unit vector
 -- - get_look_pitch(): pitch in radians
 -- - get_look_yaw(): yaw in radians (wraps around pretty randomly as of now)
 -- - set_stack(listname, i, stack): copy stack to index i in list
 -- - get_list(listname): return full list
 -- - set_list(listname, list): set full list (size will not change)
--- - autoinsert_stack(listname, stack): insert stack somewhere in list
--- - autoinsert_stackstring(listname, stackstring)
+-- - add_item(listname, stack): add item somewhere in list, returns leftover ItemStack
+-- - room_for_item(listname, stack): returns true if the stack of items
+--     can be fully added to the list
+-- - contains_item(listname, stack): returns true if the stack of items
+--     can be fully taken from the list
+--   remove_item(listname, stack): take as many items as specified from the list,
+--     returns the items that were actually removed (as an ItemStack)
 --
 -- ItemStack methods:
--- - peek_item(): return item from stack without removing it
--- - take_item(): remove item from stack and return it
--- - put_item(item): put item in stack; return false if not possible
--- - put_stackstring(stackstring): return false if not possible
+-- - is_empty(): return true if stack is empty
+-- - get_name(): returns item name (e.g. "default:stone")
+-- - get_count(): returns number of items on the stack
+-- - get_wear(): returns tool wear (0-65535), 0 for non-tools
+-- - get_metadata(): returns metadata (a string attached to an item stack)
+-- - clear(): removes all items from the stack, making it empty
+-- - replace(item): replace the contents of this stack (item can also
+--     be an itemstring or table)
+-- - to_string(): returns the stack in itemstring form
+-- - to_table(): returns the stack in Lua table form
+-- - get_stack_max(): returns the maximum size of the stack (depends on the item)
+-- - get_free_space(): returns get_stack_max() - get_count()
+-- - is_known(): returns true if the item name refers to a defined item type
+-- - get_definition(): returns the item definition table
+-- - get_tool_digging_properties(): returns the digging properties of the item,
+--   ^ or those of the hand if none are defined for this item type
+-- - add_wear(amount): increases wear by amount if the item is a tool
+-- - add_item(item): put some item or stack onto this stack,
+--   ^ returns leftover ItemStack
+-- - item_fits(item): returns true if item or stack can be fully added to this one
+-- - take_item(n): take (and remove) up to n items from this stack
+--   ^ returns taken ItemStack
+--   ^ if n is omitted, n=1 is used
+-- - peek_item(n): copy (don't remove) up to n items from this stack
+--   ^ returns copied ItemStack
+--   ^ if n is omitted, n=1 is used
 --
 -- Registered entities:
 -- - Functions receive a "luaentity" as self:
 --     myvariable = whatever,
 -- }
 --
--- Tool definition:
+-- Item definition:
 -- {
---     image = "default_tool_steelaxe.png",
---     full_punch_interval = 1.0,
---     basetime = 1.0,
---     dt_weight = 0.5,
---     dt_crackiness = -0.2,
---     dt_crumbliness = 1,
---     dt_cuttability = -0.5,
---     basedurability = 330,
---     dd_weight = 0,
---     dd_crackiness = 0,
---     dd_crumbliness = 0,
---     dd_cuttability = 0,
+--     description = "Steel Axe",
+--     inventory_image = "default_tool_steelaxe.png",
+--     wield_image = "",
+--     wield_scale = {x=1,y=1,z=1},
+--     stack_max = 99,
+--     liquids_pointable = false,
+--     tool_digging_properties = {
+--         full_punch_interval = 1.0,
+--         basetime = 1.0,
+--         dt_weight = 0.5,
+--         dt_crackiness = -0.2,
+--         dt_crumbliness = 1,
+--         dt_cuttability = -0.5,
+--         basedurability = 330,
+--         dd_weight = 0,
+--         dd_crackiness = 0,
+--         dd_crumbliness = 0,
+--         dd_cuttability = 0,
+--     }
+--     on_drop = func(item, dropper, pos),
+--     on_place = func(item, placer, pointed_thing),
+--     on_use = func(item, user, pointed_thing),
 -- }
 --
 -- Node definition options:
 -- {
---     name = "modname:somenode",
+--     <all fields from item definitions>,
 --     drawtype = "normal",
 --     visual_scale = 1.0,
 --     tile_images = {"default_unknown_block.png"},
---     inventory_image = "default_unknown_block.png",
 --     special_materials = {
 --         {image="", backface_culling=true},
 --         {image="", backface_culling=true},
 --     climbable = false,
 --     buildable_to = false,
 --     wall_mounted = false,
---     often_contains_mineral = false,
 --     dug_item = "",
 --     extra_dug_item = "",
 --     extra_dug_item_rarity = 2,
 --         cuttability = 0,
 --         flammability = 0,
 --     },
---     cookresult_itemstring = "", -- Cannot be cooked
---     furnace_cooktime = 3.0,
---     furnace_burntime = -1, -- Cannot be used as fuel
+--     on_drop = func(item, dropper),
+--     on_place = func(item, placer, pointed_thing),
+--     on_use = func(item, user, pointed_thing),
 -- }
 --
 -- Craftitem definition options:
--- minetest.register_craftitem("modname_name", {
---     image = "default_image.png",
+-- {
+--     description = <tooltip text>,
+--     inventory_image = "default_unknown_block.png",
+--     wield_image = "",
 --     stack_max = <maximum number of items in stack>,
---     cookresult_itemstring = itemstring (result of cooking),
---     furnace_cooktime = <cooking time>,
---     furnace_burntime = <time to burn as fuel in furnace>,
---     usable = <uh... some boolean value>,
---     dropcount = <amount of items to drop using drop action>
 --     liquids_pointable = <whether can point liquids>,
---     on_drop = func(item, dropper, pos),
---     on_place_on_ground = func(item, placer, pos),
---     on_use = func(item, player, pointed_thing),
--- })
+--     on_drop = func(item, dropper),
+--     on_place = func(item, placer, pointed_thing),
+--     on_use = func(item, user, pointed_thing),
+-- }
 -- 
 -- Recipe:
 -- {
---     output = 'tool "default:pick_stone"',
+--     output = 'default:pick_stone',
 --     recipe = {
---         {'node "default:cobble"', 'node "default:cobble"', 'node "default:cobble"'},
---         {'', 'craft "default:stick"', ''},
---         {'', 'craft "default:stick"', ''},
---     }
+--         {'default:cobble', 'default:cobble', 'default:cobble'},
+--         {'', 'default:stick', ''},
+--         {'', 'default:stick', ''},
+--     },
+--     replacements = <optional list of item pairs,
+--                     replace one input item with another item on crafting>
+-- }
+--
+-- Recipe (shapeless):
+-- {
+--     type = "shapeless",
+--     output = 'mushrooms:mushroom_stew',
+--     recipe = {
+--         "mushrooms:bowl",
+--         "mushrooms:mushroom_brown",
+--         "mushrooms:mushroom_red",
+--     },
+--     replacements = <optional list of item pairs,
+--                     replace one input item with another item on crafting>
+-- }
+--
+-- Recipe (tool repair):
+-- {
+--     type = "toolrepair",
+--     additional_wear = -0.02,
+-- }
+--
+-- Recipe (cooking):
+-- {
+--     type = "cooking",
+--     output = "default:glass",
+--     recipe = "default:sand",
+--     cooktime = 3,
+-- }
+--
+-- Recipe (furnace fuel):
+-- {
+--     type = "fuel",
+--     recipe = "default:leaves",
+--     burntime = 1,
 -- }
 --
 -- ABM (ActiveBlockModifier) definition:
@@ -382,189 +454,213 @@ default = {}
 -- Tool definition
 --
 
--- The hand
-minetest.register_tool(":", {
-       image = "",
-       basetime = 0.5,
-       dt_weight = 1,
-       dt_crackiness = 0,
-       dt_crumbliness = -1,
-       dt_cuttability = 0,
-       basedurability = 50,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
-})
-
 minetest.register_tool("default:pick_wood", {
-       image = "default_tool_woodpick.png",
-       basetime = 2.0,
-       dt_weight = 0,
-       dt_crackiness = -0.5,
-       dt_crumbliness = 2,
-       dt_cuttability = 0,
-       basedurability = 30,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Wooden Pickaxe",
+       inventory_image = "default_tool_woodpick.png",
+       tool_digging_properties = {
+               basetime = 2.0,
+               dt_weight = 0,
+               dt_crackiness = -0.5,
+               dt_crumbliness = 2,
+               dt_cuttability = 0,
+               basedurability = 30,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:pick_stone", {
-       image = "default_tool_stonepick.png",
-       basetime = 1.5,
-       dt_weight = 0,
-       dt_crackiness = -0.5,
-       dt_crumbliness = 2,
-       dt_cuttability = 0,
-       basedurability = 100,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Stone Pickaxe",
+       inventory_image = "default_tool_stonepick.png",
+       tool_digging_properties = {
+               basetime = 1.5,
+               dt_weight = 0,
+               dt_crackiness = -0.5,
+               dt_crumbliness = 2,
+               dt_cuttability = 0,
+               basedurability = 100,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:pick_steel", {
-       image = "default_tool_steelpick.png",
-       basetime = 1.0,
-       dt_weight = 0,
-       dt_crackiness = -0.5,
-       dt_crumbliness = 2,
-       dt_cuttability = 0,
-       basedurability = 333,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Steel Pickaxe",
+       inventory_image = "default_tool_steelpick.png",
+       tool_digging_properties = {
+               basetime = 1.0,
+               dt_weight = 0,
+               dt_crackiness = -0.5,
+               dt_crumbliness = 2,
+               dt_cuttability = 0,
+               basedurability = 333,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:pick_mese", {
-       image = "default_tool_mesepick.png",
-       basetime = 0,
-       dt_weight = 0,
-       dt_crackiness = 0,
-       dt_crumbliness = 0,
-       dt_cuttability = 0,
-       basedurability = 1337,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Mese Pickaxe",
+       inventory_image = "default_tool_mesepick.png",
+       tool_digging_properties = {
+               basetime = 0,
+               dt_weight = 0,
+               dt_crackiness = 0,
+               dt_crumbliness = 0,
+               dt_cuttability = 0,
+               basedurability = 1337,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:shovel_wood", {
-       image = "default_tool_woodshovel.png",
-       basetime = 2.0,
-       dt_weight = 0.5,
-       dt_crackiness = 2,
-       dt_crumbliness = -1.5,
-       dt_cuttability = 0.3,
-       basedurability = 30,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Wooden Shovel",
+       inventory_image = "default_tool_woodshovel.png",
+       tool_digging_properties = {
+               basetime = 2.0,
+               dt_weight = 0.5,
+               dt_crackiness = 2,
+               dt_crumbliness = -1.5,
+               dt_cuttability = 0.3,
+               basedurability = 30,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:shovel_stone", {
-       image = "default_tool_stoneshovel.png",
-       basetime = 1.5,
-       dt_weight = 0.5,
-       dt_crackiness = 2,
-       dt_crumbliness = -1.5,
-       dt_cuttability = 0.1,
-       basedurability = 100,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Stone Shovel",
+       inventory_image = "default_tool_stoneshovel.png",
+       tool_digging_properties = {
+               basetime = 1.5,
+               dt_weight = 0.5,
+               dt_crackiness = 2,
+               dt_crumbliness = -1.5,
+               dt_cuttability = 0.1,
+               basedurability = 100,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:shovel_steel", {
-       image = "default_tool_steelshovel.png",
-       basetime = 1.0,
-       dt_weight = 0.5,
-       dt_crackiness = 2,
-       dt_crumbliness = -1.5,
-       dt_cuttability = 0.0,
-       basedurability = 330,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Steel Shovel",
+       inventory_image = "default_tool_steelshovel.png",
+       tool_digging_properties = {
+               basetime = 1.0,
+               dt_weight = 0.5,
+               dt_crackiness = 2,
+               dt_crumbliness = -1.5,
+               dt_cuttability = 0.0,
+               basedurability = 330,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:axe_wood", {
-       image = "default_tool_woodaxe.png",
-       basetime = 2.0,
-       dt_weight = 0.5,
-       dt_crackiness = -0.2,
-       dt_crumbliness = 1,
-       dt_cuttability = -0.5,
-       basedurability = 30,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Wooden Axe",
+       inventory_image = "default_tool_woodaxe.png",
+       tool_digging_properties = {
+               basetime = 2.0,
+               dt_weight = 0.5,
+               dt_crackiness = -0.2,
+               dt_crumbliness = 1,
+               dt_cuttability = -0.5,
+               basedurability = 30,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:axe_stone", {
-       image = "default_tool_stoneaxe.png",
-       basetime = 1.5,
-       dt_weight = 0.5,
-       dt_crackiness = -0.2,
-       dt_crumbliness = 1,
-       dt_cuttability = -0.5,
-       basedurability = 100,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Stone Axe",
+       inventory_image = "default_tool_stoneaxe.png",
+       tool_digging_properties = {
+               basetime = 1.5,
+               dt_weight = 0.5,
+               dt_crackiness = -0.2,
+               dt_crumbliness = 1,
+               dt_cuttability = -0.5,
+               basedurability = 100,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:axe_steel", {
-       image = "default_tool_steelaxe.png",
-       basetime = 1.0,
-       dt_weight = 0.5,
-       dt_crackiness = -0.2,
-       dt_crumbliness = 1,
-       dt_cuttability = -0.5,
-       basedurability = 330,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Steel Axe",
+       inventory_image = "default_tool_steelaxe.png",
+       tool_digging_properties = {
+               basetime = 1.0,
+               dt_weight = 0.5,
+               dt_crackiness = -0.2,
+               dt_crumbliness = 1,
+               dt_cuttability = -0.5,
+               basedurability = 330,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       },
 })
 minetest.register_tool("default:sword_wood", {
-       image = "default_tool_woodsword.png",
-       basetime = 3.0,
-       dt_weight = 3,
-       dt_crackiness = 0,
-       dt_crumbliness = 1,
-       dt_cuttability = -1,
-       basedurability = 30,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Wooden Sword",
+       inventory_image = "default_tool_woodsword.png",
+       tool_digging_properties = {
+               basetime = 3.0,
+               dt_weight = 3,
+               dt_crackiness = 0,
+               dt_crumbliness = 1,
+               dt_cuttability = -1,
+               basedurability = 30,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       }
 })
 minetest.register_tool("default:sword_stone", {
-       image = "default_tool_stonesword.png",
-       basetime = 2.5,
-       dt_weight = 3,
-       dt_crackiness = 0,
-       dt_crumbliness = 1,
-       dt_cuttability = -1,
-       basedurability = 100,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Stone Sword",
+       inventory_image = "default_tool_stonesword.png",
+       tool_digging_properties = {
+               basetime = 2.5,
+               dt_weight = 3,
+               dt_crackiness = 0,
+               dt_crumbliness = 1,
+               dt_cuttability = -1,
+               basedurability = 100,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       }
 })
 minetest.register_tool("default:sword_steel", {
-       image = "default_tool_steelsword.png",
-       basetime = 2.0,
-       dt_weight = 3,
-       dt_crackiness = 0,
-       dt_crumbliness = 1,
-       dt_cuttability = -1,
-       basedurability = 330,
-       dd_weight = 0,
-       dd_crackiness = 0,
-       dd_crumbliness = 0,
-       dd_cuttability = 0,
+       description = "Steel Sword",
+       inventory_image = "default_tool_steelsword.png",
+       tool_digging_properties = {
+               basetime = 2.0,
+               dt_weight = 3,
+               dt_crackiness = 0,
+               dt_crumbliness = 1,
+               dt_cuttability = -1,
+               basedurability = 330,
+               dd_weight = 0,
+               dd_crackiness = 0,
+               dd_crumbliness = 0,
+               dd_cuttability = 0,
+       }
 })
 
 --
@@ -572,409 +668,581 @@ minetest.register_tool("default:sword_steel", {
 --
 
 minetest.register_craft({
-       output = 'node "default:wood" 4',
+       output = 'default:wood 4',
        recipe = {
-               {'node "default:tree"'},
+               {'default:tree'},
        }
 })
 
 minetest.register_craft({
-       output = 'craft "default:stick" 4',
+       output = 'default:stick 4',
        recipe = {
-               {'node "default:wood"'},
+               {'default:wood'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:fence_wood" 2',
+       output = 'default:fence_wood 2',
        recipe = {
-               {'craft "default:stick"', 'craft "default:stick"', 'craft "default:stick"'},
-               {'craft "default:stick"', 'craft "default:stick"', 'craft "default:stick"'},
+               {'default:stick', 'default:stick', 'default:stick'},
+               {'default:stick', 'default:stick', 'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:sign_wall" 1',
+       output = 'default:sign_wall',
        recipe = {
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
-               {'', 'craft "default:stick"', ''},
+               {'default:wood', 'default:wood', 'default:wood'},
+               {'default:wood', 'default:wood', 'default:wood'},
+               {'', 'default:stick', ''},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:torch" 4',
+       output = 'default:torch 4',
        recipe = {
-               {'craft "default:coal_lump"'},
-               {'craft "default:stick"'},
+               {'default:coal_lump'},
+               {'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:pick_wood"',
+       output = 'default:pick_wood',
        recipe = {
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
-               {'', 'craft "default:stick"', ''},
-               {'', 'craft "default:stick"', ''},
+               {'default:wood', 'default:wood', 'default:wood'},
+               {'', 'default:stick', ''},
+               {'', 'default:stick', ''},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:pick_stone"',
+       output = 'default:pick_stone',
        recipe = {
-               {'node "default:cobble"', 'node "default:cobble"', 'node "default:cobble"'},
-               {'', 'craft "default:stick"', ''},
-               {'', 'craft "default:stick"', ''},
+               {'default:cobble', 'default:cobble', 'default:cobble'},
+               {'', 'default:stick', ''},
+               {'', 'default:stick', ''},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:pick_steel"',
+       output = 'default:pick_steel',
        recipe = {
-               {'craft "default:steel_ingot"', 'craft "default:steel_ingot"', 'craft "default:steel_ingot"'},
-               {'', 'craft "default:stick"', ''},
-               {'', 'craft "default:stick"', ''},
+               {'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
+               {'', 'default:stick', ''},
+               {'', 'default:stick', ''},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:pick_mese"',
+       output = 'default:pick_mese',
        recipe = {
-               {'node "default:mese"', 'node "default:mese"', 'node "default:mese"'},
-               {'', 'craft "default:stick"', ''},
-               {'', 'craft "default:stick"', ''},
+               {'default:mese', 'default:mese', 'default:mese'},
+               {'', 'default:stick', ''},
+               {'', 'default:stick', ''},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:shovel_wood"',
+       output = 'default:shovel_wood',
        recipe = {
-               {'node "default:wood"'},
-               {'craft "default:stick"'},
-               {'craft "default:stick"'},
+               {'default:wood'},
+               {'default:stick'},
+               {'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:shovel_stone"',
+       output = 'default:shovel_stone',
        recipe = {
-               {'node "default:cobble"'},
-               {'craft "default:stick"'},
-               {'craft "default:stick"'},
+               {'default:cobble'},
+               {'default:stick'},
+               {'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:shovel_steel"',
+       output = 'default:shovel_steel',
        recipe = {
-               {'craft "default:steel_ingot"'},
-               {'craft "default:stick"'},
-               {'craft "default:stick"'},
+               {'default:steel_ingot'},
+               {'default:stick'},
+               {'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:axe_wood"',
+       output = 'default:axe_wood',
        recipe = {
-               {'node "default:wood"', 'node "default:wood"'},
-               {'node "default:wood"', 'craft "default:stick"'},
-               {'', 'craft "default:stick"'},
+               {'default:wood', 'default:wood'},
+               {'default:wood', 'default:stick'},
+               {'', 'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:axe_stone"',
+       output = 'default:axe_stone',
        recipe = {
-               {'node "default:cobble"', 'node "default:cobble"'},
-               {'node "default:cobble"', 'craft "default:stick"'},
-               {'', 'craft "default:stick"'},
+               {'default:cobble', 'default:cobble'},
+               {'default:cobble', 'default:stick'},
+               {'', 'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:axe_steel"',
+       output = 'default:axe_steel',
        recipe = {
-               {'craft "default:steel_ingot"', 'craft "default:steel_ingot"'},
-               {'craft "default:steel_ingot"', 'craft "default:stick"'},
-               {'', 'craft "default:stick"'},
+               {'default:steel_ingot', 'default:steel_ingot'},
+               {'default:steel_ingot', 'default:stick'},
+               {'', 'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:sword_wood"',
+       output = 'default:sword_wood',
        recipe = {
-               {'node "default:wood"'},
-               {'node "default:wood"'},
-               {'craft "default:stick"'},
+               {'default:wood'},
+               {'default:wood'},
+               {'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:sword_stone"',
+       output = 'default:sword_stone',
        recipe = {
-               {'node "default:cobble"'},
-               {'node "default:cobble"'},
-               {'craft "default:stick"'},
+               {'default:cobble'},
+               {'default:cobble'},
+               {'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'tool "default:sword_steel"',
+       output = 'default:sword_steel',
        recipe = {
-               {'craft "default:steel_ingot"'},
-               {'craft "default:steel_ingot"'},
-               {'craft "default:stick"'},
+               {'default:steel_ingot'},
+               {'default:steel_ingot'},
+               {'default:stick'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:rail" 15',
+       output = 'default:rail 15',
        recipe = {
-               {'craft "default:steel_ingot"', '', 'craft "default:steel_ingot"'},
-               {'craft "default:steel_ingot"', 'craft "default:stick"', 'craft "default:steel_ingot"'},
-               {'craft "default:steel_ingot"', '', 'craft "default:steel_ingot"'},
+               {'default:steel_ingot', '', 'default:steel_ingot'},
+               {'default:steel_ingot', 'default:stick', 'default:steel_ingot'},
+               {'default:steel_ingot', '', 'default:steel_ingot'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:chest" 1',
+       output = 'default:chest',
        recipe = {
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
-               {'node "default:wood"', '', 'node "default:wood"'},
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
+               {'default:wood', 'default:wood', 'default:wood'},
+               {'default:wood', '', 'default:wood'},
+               {'default:wood', 'default:wood', 'default:wood'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:chest_locked" 1',
+       output = 'default:chest_locked',
        recipe = {
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
-               {'node "default:wood"', 'craft "default:steel_ingot"', 'node "default:wood"'},
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
+               {'default:wood', 'default:wood', 'default:wood'},
+               {'default:wood', 'default:steel_ingot', 'default:wood'},
+               {'default:wood', 'default:wood', 'default:wood'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:furnace" 1',
+       output = 'default:furnace',
        recipe = {
-               {'node "default:cobble"', 'node "default:cobble"', 'node "default:cobble"'},
-               {'node "default:cobble"', '', 'node "default:cobble"'},
-               {'node "default:cobble"', 'node "default:cobble"', 'node "default:cobble"'},
+               {'default:cobble', 'default:cobble', 'default:cobble'},
+               {'default:cobble', '', 'default:cobble'},
+               {'default:cobble', 'default:cobble', 'default:cobble'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:steelblock" 1',
+       output = 'default:steelblock',
        recipe = {
-               {'craft "default:steel_ingot"', 'craft "default:steel_ingot"', 'craft "default:steel_ingot"'},
-               {'craft "default:steel_ingot"', 'craft "default:steel_ingot"', 'craft "default:steel_ingot"'},
-               {'craft "default:steel_ingot"', 'craft "default:steel_ingot"', 'craft "default:steel_ingot"'},
+               {'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
+               {'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
+               {'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:sandstone" 1',
+       output = 'default:sandstone',
        recipe = {
-               {'node "default:sand"', 'node "default:sand"'},
-               {'node "default:sand"', 'node "default:sand"'},
+               {'default:sand', 'default:sand'},
+               {'default:sand', 'default:sand'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:clay" 1',
+       output = 'default:clay',
        recipe = {
-               {'craft "default:clay_lump"', 'craft "default:clay_lump"'},
-               {'craft "default:clay_lump"', 'craft "default:clay_lump"'},
+               {'default:clay_lump', 'default:clay_lump'},
+               {'default:clay_lump', 'default:clay_lump'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:brick" 1',
+       output = 'default:brick',
        recipe = {
-               {'craft "default:clay_brick"', 'craft "default:clay_brick"'},
-               {'craft "default:clay_brick"', 'craft "default:clay_brick"'},
+               {'default:clay_brick', 'default:clay_brick'},
+               {'default:clay_brick', 'default:clay_brick'},
        }
 })
 
 minetest.register_craft({
-       output = 'craft "default:paper" 1',
+       output = 'default:paper',
        recipe = {
-               {'node "default:papyrus"', 'node "default:papyrus"', 'node "default:papyrus"'},
+               {'default:papyrus', 'default:papyrus', 'default:papyrus'},
        }
 })
 
 minetest.register_craft({
-       output = 'craft "default:book" 1',
+       output = 'default:book',
        recipe = {
-               {'craft "default:paper"'},
-               {'craft "default:paper"'},
-               {'craft "default:paper"'},
+               {'default:paper'},
+               {'default:paper'},
+               {'default:paper'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:bookshelf" 1',
+       output = 'default:bookshelf',
        recipe = {
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
-               {'craft "default:book"', 'craft "default:book"', 'craft "default:book"'},
-               {'node "default:wood"', 'node "default:wood"', 'node "default:wood"'},
+               {'default:wood', 'default:wood', 'default:wood'},
+               {'default:book', 'default:book', 'default:book'},
+               {'default:wood', 'default:wood', 'default:wood'},
        }
 })
 
 minetest.register_craft({
-       output = 'node "default:ladder" 1',
+       output = 'default:ladder',
        recipe = {
-               {'craft "default:stick"', '', 'craft "default:stick"'},
-               {'craft "default:stick"', 'craft "default:stick"', 'craft "default:stick"'},
-               {'craft "default:stick"', '', 'craft "default:stick"'},
+               {'default:stick', '', 'default:stick'},
+               {'default:stick', 'default:stick', 'default:stick'},
+               {'default:stick', '', 'default:stick'},
        }
 })
 
+--
+-- Crafting (tool repair)
+--
+minetest.register_craft({
+       type = "toolrepair",
+       additional_wear = -0.02,
+})
+
+--
+-- Cooking recipes
+--
+
+minetest.register_craft({
+       type = "cooking",
+       output = "default:glass",
+       recipe = "default:sand",
+})
+
+minetest.register_craft({
+       type = "cooking",
+       output = "default:coal_lump",
+       recipe = "default:tree",
+})
+
+minetest.register_craft({
+       type = "cooking",
+       output = "default:coal_lump",
+       recipe = "default:jungletree",
+})
+
+minetest.register_craft({
+       type = "cooking",
+       output = "default:stone",
+       recipe = "default:cobble",
+})
+
+minetest.register_craft({
+       type = "cooking",
+       output = "default:steel_ingot",
+       recipe = "default:iron_lump",
+})
+
+minetest.register_craft({
+       type = "cooking",
+       output = "default:clay_brick",
+       recipe = "default:clay_lump",
+})
+
+--
+-- Fuels
+--
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:tree",
+       burntime = 30,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:jungletree",
+       burntime = 30,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:junglegrass",
+       burntime = 2,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:leaves",
+       burntime = 1,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:cactus",
+       burntime = 15,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:papyrus",
+       burntime = 1,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:bookshelf",
+       burntime = 30,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:fence_wood",
+       burntime = 15,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:ladder",
+       burntime = 5,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:wood",
+       burntime = 7,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:mese",
+       burntime = 30,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:lava_source",
+       burntime = 60,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:torch",
+       burntime = 4,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:sign_wall",
+       burntime = 10,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:chest",
+       burntime = 30,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:chest_locked",
+       burntime = 30,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:nyancat",
+       burntime = 1,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:nyancat_rainbow",
+       burntime = 1,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:sapling",
+       burntime = 10,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:apple",
+       burntime = 3,
+})
+
+minetest.register_craft({
+       type = "fuel",
+       recipe = "default:coal_lump",
+       burntime = 40,
+})
+
 --
 -- Node definitions
 --
 
 minetest.register_node("default:stone", {
+       description = "Stone",
        tile_images = {"default_stone.png"},
-       inventory_image = minetest.inventorycube("default_stone.png"),
        paramtype = "mineral",
        is_ground_content = true,
-       often_contains_mineral = true, -- Texture atlas hint
        material = minetest.digprop_stonelike(1.0),
        dug_item = 'node "default:cobble" 1',
 })
 
 minetest.register_node("default:dirt_with_grass", {
+       description = "Dirt with grass",
        tile_images = {"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
-       inventory_image = minetest.inventorycube("default_dirt.png^default_grass_side.png"),
        is_ground_content = true,
        material = minetest.digprop_dirtlike(1.0),
        dug_item = 'node "default:dirt" 1',
 })
 
 minetest.register_node("default:dirt_with_grass_footsteps", {
+       description = "Dirt with grass and footsteps",
        tile_images = {"default_grass_footsteps.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
-       inventory_image = "default_grass_footsteps.png",
        is_ground_content = true,
        material = minetest.digprop_dirtlike(1.0),
        dug_item = 'node "default:dirt" 1',
 })
 
 minetest.register_node("default:dirt", {
+       description = "Dirt",
        tile_images = {"default_dirt.png"},
-       inventory_image = minetest.inventorycube("default_dirt.png"),
        is_ground_content = true,
        material = minetest.digprop_dirtlike(1.0),
 })
 
 minetest.register_node("default:sand", {
+       description = "Sand",
        tile_images = {"default_sand.png"},
-       inventory_image = minetest.inventorycube("default_sand.png"),
        is_ground_content = true,
        material = minetest.digprop_dirtlike(1.0),
-       cookresult_itemstring = 'node "default:glass" 1',
 })
 
 minetest.register_node("default:gravel", {
+       description = "Gravel",
        tile_images = {"default_gravel.png"},
-       inventory_image = minetest.inventorycube("default_gravel.png"),
        is_ground_content = true,
        material = minetest.digprop_gravellike(1.0),
 })
 
 minetest.register_node("default:sandstone", {
+       description = "Sandstone",
        tile_images = {"default_sandstone.png"},
-       inventory_image = minetest.inventorycube("default_sandstone.png"),
        is_ground_content = true,
        material = minetest.digprop_dirtlike(1.0),  -- FIXME should this be stonelike?
        dug_item = 'node "default:sand" 1',  -- FIXME is this intentional?
 })
 
 minetest.register_node("default:clay", {
+       description = "Clay",
        tile_images = {"default_clay.png"},
-       inventory_image = minetest.inventorycube("default_clay.png"),
        is_ground_content = true,
        material = minetest.digprop_dirtlike(1.0),
        dug_item = 'craft "default:clay_lump" 4',
 })
 
 minetest.register_node("default:brick", {
+       description = "Brick",
        tile_images = {"default_brick.png"},
-       inventory_image = minetest.inventorycube("default_brick.png"),
        is_ground_content = true,
        material = minetest.digprop_stonelike(1.0),
        dug_item = 'craft "default:clay_brick" 4',
 })
 
 minetest.register_node("default:tree", {
+       description = "Tree",
        tile_images = {"default_tree_top.png", "default_tree_top.png", "default_tree.png"},
-       inventory_image = minetest.inventorycube("default_tree_top.png", "default_tree.png", "default_tree.png"),
        is_ground_content = true,
        material = minetest.digprop_woodlike(1.0),
-       cookresult_itemstring = 'craft "default:coal_lump" 1',
-       furnace_burntime = 30,
 })
 
 minetest.register_node("default:jungletree", {
+       description = "Jungle Tree",
        tile_images = {"default_jungletree_top.png", "default_jungletree_top.png", "default_jungletree.png"},
-       inventory_image = minetest.inventorycube("default_jungletree_top.png", "default_jungletree.png", "default_jungletree.png"),
        is_ground_content = true,
        material = minetest.digprop_woodlike(1.0),
-       cookresult_itemstring = 'craft "default:coal_lump" 1',
-       furnace_burntime = 30,
 })
 
 minetest.register_node("default:junglegrass", {
+       description = "Jungle Grass",
        drawtype = "plantlike",
        visual_scale = 1.3,
        tile_images = {"default_junglegrass.png"},
        inventory_image = "default_junglegrass.png",
+       wield_image = "default_junglegrass.png",
        paramtype = "light",
        walkable = false,
        material = minetest.digprop_leaveslike(1.0),
-       furnace_burntime = 2,
 })
 
 minetest.register_node("default:leaves", {
+       description = "Leaves",
        drawtype = "allfaces_optional",
        visual_scale = 1.3,
        tile_images = {"default_leaves.png"},
-       inventory_image = minetest.inventorycube("default_leaves.png"),
        paramtype = "light",
        material = minetest.digprop_leaveslike(1.0),
        extra_dug_item = 'node "default:sapling" 1',
        extra_dug_item_rarity = 20,
-       furnace_burntime = 1,
 })
 
 minetest.register_node("default:cactus", {
+       description = "Cactus",
        tile_images = {"default_cactus_top.png", "default_cactus_top.png", "default_cactus_side.png"},
-       inventory_image = minetest.inventorycube("default_cactus_top.png", "default_cactus_side.png", "default_cactus_side.png"),
        is_ground_content = true,
        material = minetest.digprop_woodlike(0.75),
-       furnace_burntime = 15,
 })
 
 minetest.register_node("default:papyrus", {
+       description = "Papyrus",
        drawtype = "plantlike",
        tile_images = {"default_papyrus.png"},
        inventory_image = "default_papyrus.png",
+       wield_image = "default_papyrus.png",
        paramtype = "light",
        is_ground_content = true,
        walkable = false,
        material = minetest.digprop_leaveslike(0.5),
-       furnace_burntime = 1,
 })
 
 minetest.register_node("default:bookshelf", {
+       description = "Bookshelf",
        tile_images = {"default_wood.png", "default_wood.png", "default_bookshelf.png"},
-       inventory_image = minetest.inventorycube("default_wood.png", "default_bookshelf.png", "default_bookshelf.png"),
        is_ground_content = true,
        material = minetest.digprop_woodlike(0.75),
-       furnace_burntime = 30,
 })
 
 minetest.register_node("default:glass", {
+       description = "Glass",
        drawtype = "glasslike",
        tile_images = {"default_glass.png"},
        inventory_image = minetest.inventorycube("default_glass.png"),
@@ -985,23 +1253,26 @@ minetest.register_node("default:glass", {
 })
 
 minetest.register_node("default:fence_wood", {
+       description = "Wooden Fence",
        drawtype = "fencelike",
        tile_images = {"default_wood.png"},
        inventory_image = "default_fence.png",
+       wield_image = "default_fence.png",
        paramtype = "light",
        is_ground_content = true,
        selection_box = {
                type = "fixed",
                fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
        },
-       furnace_burntime = 15,
        material = minetest.digprop_woodlike(0.75),
 })
 
 minetest.register_node("default:rail", {
+       description = "Rail",
        drawtype = "raillike",
        tile_images = {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"},
        inventory_image = "default_rail.png",
+       wield_image = "default_rail.png",
        paramtype = "light",
        is_ground_content = true,
        walkable = false,
@@ -1013,9 +1284,11 @@ minetest.register_node("default:rail", {
 })
 
 minetest.register_node("default:ladder", {
+       dsecription = "Ladder",
        drawtype = "signlike",
        tile_images = {"default_ladder.png"},
        inventory_image = "default_ladder.png",
+       wield_image = "default_ladder.png",
        paramtype = "light",
        is_ground_content = true,
        wall_mounted = true,
@@ -1027,37 +1300,35 @@ minetest.register_node("default:ladder", {
                --wall_bottom = = <default>
                --wall_side = = <default>
        },
-       furnace_burntime = 5,
        material = minetest.digprop_woodlike(0.5),
 })
 
 minetest.register_node("default:wood", {
+       description = "Wood",
        tile_images = {"default_wood.png"},
-       inventory_image = minetest.inventorycube("default_wood.png"),
        is_ground_content = true,
-       furnace_burntime = 7,
        material = minetest.digprop_woodlike(0.75),
 })
 
 minetest.register_node("default:mese", {
+       description = "Mese",
        tile_images = {"default_mese.png"},
-       inventory_image = minetest.inventorycube("default_mese.png"),
        is_ground_content = true,
-       furnace_burntime = 30,
        material = minetest.digprop_stonelike(0.5),
 })
 
 minetest.register_node("default:cloud", {
+       description = "Cloud",
        tile_images = {"default_cloud.png"},
-       inventory_image = minetest.inventorycube("default_cloud.png"),
        is_ground_content = true,
 })
 
 minetest.register_node("default:water_flowing", {
+       description = "Water (flowing)",
+       inventory_image = minetest.inventorycube("default_water.png"),
        drawtype = "flowingliquid",
        tile_images = {"default_water.png"},
        alpha = WATER_ALPHA,
-       inventory_image = minetest.inventorycube("default_water.png"),
        paramtype = "light",
        walkable = false,
        pointable = false,
@@ -1075,10 +1346,11 @@ minetest.register_node("default:water_flowing", {
 })
 
 minetest.register_node("default:water_source", {
+       description = "Water",
+       inventory_image = minetest.inventorycube("default_water.png"),
        drawtype = "liquid",
        tile_images = {"default_water.png"},
        alpha = WATER_ALPHA,
-       inventory_image = minetest.inventorycube("default_water.png"),
        paramtype = "light",
        walkable = false,
        pointable = false,
@@ -1096,9 +1368,10 @@ minetest.register_node("default:water_source", {
 })
 
 minetest.register_node("default:lava_flowing", {
+       description = "Lava (flowing)",
+       inventory_image = minetest.inventorycube("default_lava.png"),
        drawtype = "flowingliquid",
        tile_images = {"default_lava.png"},
-       inventory_image = minetest.inventorycube("default_lava.png"),
        paramtype = "light",
        light_source = LIGHT_MAX - 1,
        walkable = false,
@@ -1118,9 +1391,10 @@ minetest.register_node("default:lava_flowing", {
 })
 
 minetest.register_node("default:lava_source", {
+       description = "Lava",
+       inventory_image = minetest.inventorycube("default_lava.png"),
        drawtype = "liquid",
        tile_images = {"default_lava.png"},
-       inventory_image = minetest.inventorycube("default_lava.png"),
        paramtype = "light",
        light_source = LIGHT_MAX - 1,
        walkable = false,
@@ -1137,13 +1411,14 @@ minetest.register_node("default:lava_source", {
                -- New-style lava source material (mostly unused)
                {image="default_lava.png", backface_culling=false},
        },
-       furnace_burntime = 60,
 })
 
 minetest.register_node("default:torch", {
+       description = "Torch",
        drawtype = "torchlike",
        tile_images = {"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
        inventory_image = "default_torch_on_floor.png",
+       wield_image = "default_torch_on_floor.png",
        paramtype = "light",
        sunlight_propagates = true,
        walkable = false,
@@ -1156,13 +1431,14 @@ minetest.register_node("default:torch", {
                wall_side = {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
        },
        material = minetest.digprop_constanttime(0.0),
-       furnace_burntime = 4,
 })
 
 minetest.register_node("default:sign_wall", {
+       description = "Sign",
        drawtype = "signlike",
        tile_images = {"default_sign_wall.png"},
        inventory_image = "default_sign_wall.png",
+       wield_image = "default_sign_wall.png",
        paramtype = "light",
        sunlight_propagates = true,
        walkable = false,
@@ -1175,88 +1451,86 @@ minetest.register_node("default:sign_wall", {
                --wall_side = <default>
        },
        material = minetest.digprop_constanttime(0.5),
-       furnace_burntime = 10,
 })
 
 minetest.register_node("default:chest", {
+       description = "Chest",
        tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
                "default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
-       inventory_image = minetest.inventorycube("default_chest_top.png", "default_chest_front.png", "default_chest_side.png"),
        paramtype = "facedir_simple",
        metadata_name = "chest",
        material = minetest.digprop_woodlike(1.0),
-       furnace_burntime = 30,
 })
 
 minetest.register_node("default:chest_locked", {
+       description = "Locked Chest",
        tile_images = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
                "default_chest_side.png", "default_chest_side.png", "default_chest_lock.png"},
-       inventory_image = minetest.inventorycube("default_chest_top.png", "default_chest_lock.png", "default_chest_side.png"),
        paramtype = "facedir_simple",
        metadata_name = "locked_chest",
        material = minetest.digprop_woodlike(1.0),
-       furnace_burntime = 30,
 })
 
 minetest.register_node("default:furnace", {
+       description = "Furnace",
        tile_images = {"default_furnace_side.png", "default_furnace_side.png", "default_furnace_side.png",
                "default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"},
-       inventory_image = minetest.inventorycube("default_furnace_side.png", "default_furnace_front.png", "default_furnace_side.png"),
        paramtype = "facedir_simple",
        metadata_name = "furnace",
        material = minetest.digprop_stonelike(3.0),
 })
 
 minetest.register_node("default:cobble", {
+       description = "Cobble",
        tile_images = {"default_cobble.png"},
-       inventory_image = minetest.inventorycube("default_cobble.png"),
        is_ground_content = true,
-       cookresult_itemstring = 'node "default:stone" 1',
        material = minetest.digprop_stonelike(0.9),
 })
 
 minetest.register_node("default:mossycobble", {
+       description = "Mossy Cobble",
        tile_images = {"default_mossycobble.png"},
-       inventory_image = minetest.inventorycube("default_mossycobble.png"),
        is_ground_content = true,
        material = minetest.digprop_stonelike(0.8),
 })
 
 minetest.register_node("default:steelblock", {
+       description = "Steel Block",
        tile_images = {"default_steel_block.png"},
-       inventory_image = minetest.inventorycube("default_steel_block.png"),
        is_ground_content = true,
        material = minetest.digprop_stonelike(5.0),
 })
 
 minetest.register_node("default:nyancat", {
+       description = "Nyancat",
        tile_images = {"default_nc_side.png", "default_nc_side.png", "default_nc_side.png",
                "default_nc_side.png", "default_nc_back.png", "default_nc_front.png"},
        inventory_image = "default_nc_front.png",
        paramtype = "facedir_simple",
        material = minetest.digprop_stonelike(3.0),
-       furnace_burntime = 1,
 })
 
 minetest.register_node("default:nyancat_rainbow", {
+       description = "Nyancat Rainbow",
        tile_images = {"default_nc_rb.png"},
        inventory_image = "default_nc_rb.png",
        material = minetest.digprop_stonelike(3.0),
-       furnace_burntime = 1,
 })
 
 minetest.register_node("default:sapling", {
+       description = "Sapling",
        drawtype = "plantlike",
        visual_scale = 1.0,
        tile_images = {"default_sapling.png"},
        inventory_image = "default_sapling.png",
+       wield_image = "default_sapling.png",
        paramtype = "light",
        walkable = false,
        material = minetest.digprop_constanttime(0.0),
-       furnace_burntime = 10,
 })
 
 minetest.register_node("default:apple", {
+       description = "Apple",
        drawtype = "plantlike",
        visual_scale = 1.0,
        tile_images = {"default_apple.png"},
@@ -1264,9 +1538,8 @@ minetest.register_node("default:apple", {
        paramtype = "light",
        sunlight_propagates = true,
        walkable = false,
-       dug_item = 'craft "default:apple" 1',
        material = minetest.digprop_constanttime(0.0),
-       furnace_burntime = 3,
+       on_use = minetest.item_eat(4),
 })
 
 --
@@ -1274,93 +1547,84 @@ minetest.register_node("default:apple", {
 --
 
 minetest.register_craftitem("default:stick", {
-       image = "default_stick.png",
-       --furnace_burntime = ...,
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Stick",
+       inventory_image = "default_stick.png",
 })
 
 minetest.register_craftitem("default:paper", {
-       image = "default_paper.png",
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Paper",
+       inventory_image = "default_paper.png",
 })
 
 minetest.register_craftitem("default:book", {
-       image = "default_book.png",
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Book",
+       inventory_image = "default_book.png",
 })
 
 minetest.register_craftitem("default:coal_lump", {
-       image = "default_coal_lump.png",
-       furnace_burntime = 40;
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Lump of coal",
+       inventory_image = "default_coal_lump.png",
 })
 
 minetest.register_craftitem("default:iron_lump", {
-       image = "default_iron_lump.png",
-       cookresult_itemstring = 'craft "default:steel_ingot" 1',
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Lump of iron",
+       inventory_image = "default_iron_lump.png",
 })
 
 minetest.register_craftitem("default:clay_lump", {
-       image = "default_clay_lump.png",
-       cookresult_itemstring = 'craft "default:clay_brick" 1',
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Lump of clay",
+       inventory_image = "default_clay_lump.png",
 })
 
 minetest.register_craftitem("default:steel_ingot", {
-       image = "default_steel_ingot.png",
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Steel ingot",
+       inventory_image = "default_steel_ingot.png",
 })
 
 minetest.register_craftitem("default:clay_brick", {
-       image = "default_clay_brick.png",
-       on_place_on_ground = minetest.craftitem_place_item,
+       description = "Clay brick",
+       inventory_image = "default_steel_ingot.png",
+       inventory_image = "default_clay_brick.png",
 })
 
 minetest.register_craftitem("default:scorched_stuff", {
-       image = "default_scorched_stuff.png",
-       on_place_on_ground = minetest.craftitem_place_item,
-})
-
-minetest.register_craftitem("default:apple", {
-       image = "default_apple.png",
-       on_place_on_ground = minetest.craftitem_place_item,
-       on_use = minetest.craftitem_eat(4),
+       description = "Scorched stuff",
+       inventory_image = "default_scorched_stuff.png",
 })
 
 --
 -- Creative inventory
 --
 
-minetest.add_to_creative_inventory('tool "default:pick_mese" 0')
-minetest.add_to_creative_inventory('tool "default:pick_steel" 0')
-minetest.add_to_creative_inventory('tool "default:axe_steel" 0')
-minetest.add_to_creative_inventory('tool "default:shovel_steel" 0')
-
-minetest.add_to_creative_inventory('node "default:torch" 0')
-minetest.add_to_creative_inventory('node "default:cobble" 0')
-minetest.add_to_creative_inventory('node "default:dirt" 0')
-minetest.add_to_creative_inventory('node "default:stone" 0')
-minetest.add_to_creative_inventory('node "default:sand" 0')
-minetest.add_to_creative_inventory('node "default:sandstone" 0')
-minetest.add_to_creative_inventory('node "default:clay" 0')
-minetest.add_to_creative_inventory('node "default:brick" 0')
-minetest.add_to_creative_inventory('node "default:tree" 0')
-minetest.add_to_creative_inventory('node "default:wood" 0')
-minetest.add_to_creative_inventory('node "default:leaves" 0')
-minetest.add_to_creative_inventory('node "default:cactus" 0')
-minetest.add_to_creative_inventory('node "default:papyrus" 0')
-minetest.add_to_creative_inventory('node "default:bookshelf" 0')
-minetest.add_to_creative_inventory('node "default:glass" 0')
-minetest.add_to_creative_inventory('node "default:fence" 0')
-minetest.add_to_creative_inventory('node "default:rail" 0')
-minetest.add_to_creative_inventory('node "default:mese" 0')
-minetest.add_to_creative_inventory('node "default:chest" 0')
-minetest.add_to_creative_inventory('node "default:furnace" 0')
-minetest.add_to_creative_inventory('node "default:sign_wall" 0')
-minetest.add_to_creative_inventory('node "default:water_source" 0')
-minetest.add_to_creative_inventory('node "default:lava_source" 0')
-minetest.add_to_creative_inventory('node "default:ladder" 0')
+minetest.add_to_creative_inventory('default:pick_mese')
+minetest.add_to_creative_inventory('default:pick_steel')
+minetest.add_to_creative_inventory('default:axe_steel')
+minetest.add_to_creative_inventory('default:shovel_steel')
+
+minetest.add_to_creative_inventory('default:torch')
+minetest.add_to_creative_inventory('default:cobble')
+minetest.add_to_creative_inventory('default:dirt')
+minetest.add_to_creative_inventory('default:stone')
+minetest.add_to_creative_inventory('default:sand')
+minetest.add_to_creative_inventory('default:sandstone')
+minetest.add_to_creative_inventory('default:clay')
+minetest.add_to_creative_inventory('default:brick')
+minetest.add_to_creative_inventory('default:tree')
+minetest.add_to_creative_inventory('default:wood')
+minetest.add_to_creative_inventory('default:leaves')
+minetest.add_to_creative_inventory('default:cactus')
+minetest.add_to_creative_inventory('default:papyrus')
+minetest.add_to_creative_inventory('default:bookshelf')
+minetest.add_to_creative_inventory('default:glass')
+minetest.add_to_creative_inventory('default:fence')
+minetest.add_to_creative_inventory('default:rail')
+minetest.add_to_creative_inventory('default:mese')
+minetest.add_to_creative_inventory('default:chest')
+minetest.add_to_creative_inventory('default:furnace')
+minetest.add_to_creative_inventory('default:sign_wall')
+minetest.add_to_creative_inventory('default:water_source')
+minetest.add_to_creative_inventory('default:lava_source')
+minetest.add_to_creative_inventory('default:ladder')
 
 --
 -- Some common functions
@@ -1455,64 +1719,66 @@ function on_punchnode(p, node)
 end
 minetest.register_on_punchnode(on_punchnode)
 
+local function handle_give_command(cmd, giver, receiver, stackstring)
+       if not minetest.get_player_privs(giver)["give"] then
+               minetest.chat_send_player(giver, "error: you don't have permission to give")
+               return
+       end
+       minetest.debug("DEBUG: "..cmd..' invoked, stackstring="'..stackstring..'"')
+       minetest.log(cmd..' invoked, stackstring="'..stackstring..'"')
+       local itemstack = ItemStack(stackstring)
+       if itemstack:is_empty() then
+               minetest.chat_send_player(giver, 'error: cannot give an empty item')
+               return
+       elseif not itemstack:is_known() then
+               minetest.chat_send_player(giver, 'error: cannot give an unknown item')
+               return
+       end
+       local receiverref = minetest.env:get_player_by_name(receiver)
+       if receiverref == nil then
+               minetest.chat_send_player(giver, receiver..' is not a known player')
+               return
+       end
+       local leftover = receiverref:get_inventory():add_item("main", itemstack)
+       if leftover:is_empty() then
+               partiality = ""
+       elseif leftover:get_count() == itemstack:get_count() then
+               partiality = "could not be "
+       else
+               partiality = "partially "
+       end
+       if giver == receiver then
+               minetest.chat_send_player(giver, '"'..stackstring
+                       ..'" '..partiality..'added to inventory.');
+       else
+               minetest.chat_send_player(giver, '"'..stackstring
+                       ..'" '..partiality..'added to '..receiver..'\'s inventory.');
+               minetest.chat_send_player(receiver, '"'..stackstring
+                       ..'" '..partiality..'added to inventory.');
+       end
+end
+
 minetest.register_on_chat_message(function(name, message)
        --print("default on_chat_message: name="..dump(name).." message="..dump(message))
        local cmd = "/giveme"
        if message:sub(0, #cmd) == cmd then
-               if not minetest.get_player_privs(name)["give"] then
-                       minetest.chat_send_player(name, "you don't have permission to give")
-                       return true -- Handled chat message
-               end
                local stackstring = string.match(message, cmd.." (.*)")
                if stackstring == nil then
                        minetest.chat_send_player(name, 'usage: '..cmd..' stackstring')
                        return true -- Handled chat message
                end
-               print(cmd..' invoked, stackstring="'..stackstring..'"')
-               local player = minetest.env:get_player_by_name(name)
-               if player == nil then
-                       minetest.chat_send_player(name, name2..' is not a known player')
-                       return true -- Handled chat message
-               end
-               local added, error_msg = player:add_to_inventory(stackstring)
-               if added then
-                       minetest.chat_send_player(name, '"'..stackstring
-                                       ..'" added to inventory.');
-               else
-                       minetest.chat_send_player(name, 'Could not give "'..stackstring
-                                       ..'": '..error_msg);
-               end
-               return true -- Handled chat message
+               handle_give_command(cmd, name, name, stackstring)
+               return true
        end
        local cmd = "/give"
        if message:sub(0, #cmd) == cmd then
-               if not minetest.get_player_privs(name)["give"] then
-                       minetest.chat_send_player(name, "you don't have permission to give")
-                       return true -- Handled chat message
-               end
-               local name2, stackstring = string.match(message, cmd.." ([%a%d_-]+) (.*)")
-               if name == nil or stackstring == nil then
+               local receiver, stackstring = string.match(message, cmd.." ([%a%d_-]+) (.*)")
+               if receiver == nil or stackstring == nil then
                        minetest.chat_send_player(name, 'usage: '..cmd..' name stackstring')
                        return true -- Handled chat message
                end
-               print(cmd..' invoked, name2="'..name2
-                               ..'" stackstring="'..stackstring..'"')
-               local player = minetest.env:get_player_by_name(name2)
-               if player == nil then
-                       minetest.chat_send_player(name, name2..' is not a known player')
-                       return true -- Handled chat message
-               end
-               local added, error_msg = player:add_to_inventory(stackstring)
-               if added then
-                       minetest.chat_send_player(name, '"'..stackstring
-                                       ..'" added to '..name2..'\'s inventory.');
-                       minetest.chat_send_player(name2, '"'..stackstring
-                                       ..'" added to inventory.');
-               else
-                       minetest.chat_send_player(name, 'Could not give "'..stackstring
-                                       ..'": '..error_msg);
-               end
-               return true -- Handled chat message
+               handle_give_command(cmd, name, receiver, stackstring)
+               return true
        end
        local cmd = "/spawnentity"
        if message:sub(0, #cmd) == cmd then
@@ -1520,6 +1786,10 @@ minetest.register_on_chat_message(function(name, message)
                        minetest.chat_send_player(name, "you don't have permission to spawn (give)")
                        return true -- Handled chat message
                end
+               if not minetest.get_player_privs(name)["interact"] then
+                       minetest.chat_send_player(name, "you don't have permission to interact")
+                       return true -- Handled chat message
+               end
                local entityname = string.match(message, cmd.." (.*)")
                if entityname == nil then
                        minetest.chat_send_player(name, 'usage: '..cmd..' entityname')
@@ -1538,6 +1808,21 @@ minetest.register_on_chat_message(function(name, message)
                                ..'" spawned.');
                return true -- Handled chat message
        end
+       local cmd = "/pulverize"
+       if message:sub(0, #cmd) == cmd then
+               local player = minetest.env:get_player_by_name(name)
+               if player == nil then
+                       print("Unable to pulverize, player is nil")
+                       return true -- Handled chat message
+               end
+               if player:get_wielded_item():is_empty() then
+                       minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.')
+               else
+                       player:set_wielded_item(nil)
+                       minetest.chat_send_player(name, 'An item was pulverized.')
+               end
+               return true
+       end
 end)
 
 --
index 2aae9b1998b591475ab19af2b3c77c780ecc45fa..a58d8f08d9f8ce803092223284ee785f5b78fcb1 100644 (file)
@@ -25,6 +25,7 @@ minetest.register_globalstep(on_step)
 
 -- An example furnace-thing implemented in Lua
 
+--[[
 minetest.register_node("experimental:luafurnace", {
        tile_images = {"default_lava.png", "default_furnace_side.png",
                "default_furnace_side.png", "default_furnace_side.png",
@@ -56,15 +57,6 @@ minetest.register_on_placenode(function(pos, newnode, placer)
        end
 end)
 
-local get_item_definition = function(item)
-       if not item then return nil end
-       if item.type == "node" then
-               return minetest.registered_nodes[item.name]
-       elseif item.type == "craft" then
-               return minetest.registered_craftitems[item.name]
-       end
-end
-
 minetest.register_abm({
        nodenames = {"experimental:luafurnace"},
        interval = 1.0,
@@ -176,7 +168,6 @@ minetest.register_abm({
                inv:set_stack("fuel", 1, stack)
        end,
 })
---[[
 minetest.register_abm({
        nodenames = {"experimental:luafurnace"},
        interval = 1.0,
@@ -231,7 +222,6 @@ minetest.register_abm({
                meta:set_infotext("Lua Furnace: total cooked: "..total_cooked)
        end,
 })
---]]
 minetest.register_craft({
        output = 'node "experimental:luafurnace" 1',
        recipe = {
@@ -240,6 +230,7 @@ minetest.register_craft({
                {'node "default:cobble"', 'node "default:cobble"', 'node "default:cobble"'},
        }
 })
+--]]
 
 --
 -- Random stuff
@@ -261,38 +252,16 @@ minetest.register_tool("experimental:horribletool", {
 })
 --]]
 
---[[minetest.register_craft({
-       output = 'node "somenode" 4',
-       recipe = {
-               {'craft "default_tick" 1'},
-       }
-})
-
-minetest.register_node("experimental:somenode", {
-       tile_images = {"lava.png", "mese.png", "stone.png", "grass.png", "cobble.png", "tree_top.png"},
-       inventory_image = minetest.inventorycube("lava.png", "mese.png", "stone.png"),
-       --inventory_image = "treeprop.png",
-       material = {
-               diggability = "normal",
-               weight = 0,
-               crackiness = 0,
-               crumbliness = 0,
-               cuttability = 0,
-               flammability = 0
-       },
-       metadata_name = "chest",
-})]]
-
 --
 -- TNT (not functional)
 --
 
 minetest.register_craft({
-       output = 'node "experimental:tnt" 4',
+       output = 'experimental:tnt',
        recipe = {
-               {'node "default:wood" 1'},
-               {'craft "default:coal_lump" 1'},
-               {'node "default:wood" 1'}
+               {'default:wood'},
+               {'default:coal_lump'},
+               {'default:wood'}
        }
 })
 
@@ -363,7 +332,7 @@ function TNT:on_punch(hitter)
        self.health = self.health - 1
        if self.health <= 0 then
                self.object:remove()
-               hitter:add_to_inventory("node TNT 1")
+               hitter:get_inventory():add_item("main", "experimental:tnt")
                hitter:set_hp(hitter:get_hp() - 1)
        end
 end
@@ -380,7 +349,7 @@ end
 minetest.register_entity("experimental:tnt", TNT)
 
 -- Add TNT's old name also
-minetest.alias_node("TNT", "experimental:tnt")
+minetest.register_alias("TNT", "experimental:tnt")
 
 --
 -- A test entity for testing animated and yaw-modulated sprites
@@ -547,6 +516,7 @@ minetest.register_abm({
     end,
 })--]]
 
+print("experimental modname="..dump(minetest.get_current_modname()))
 print("experimental modpath="..dump(minetest.get_modpath("experimental")))
 
 -- END
index 3bd68f0bcbc8ecc6939d3d02485201425c7c3efe..127b0e17fb825478805dfcf8c7e1fa5ccfd549cf 100644 (file)
 -- Aliases to support loading 0.3 and old 0.4 worlds and inventories
 --
 
-minetest.alias_node("stone", "default:stone")
-minetest.alias_node("dirt_with_grass", "default:dirt_with_grass")
-minetest.alias_node("dirt_with_grass_footsteps", "default:dirt_with_grass_footsteps")
-minetest.alias_node("dirt", "default:dirt")
-minetest.alias_node("sand", "default:sand")
-minetest.alias_node("gravel", "default:gravel")
-minetest.alias_node("sandstone", "default:sandstone")
-minetest.alias_node("clay", "default:clay")
-minetest.alias_node("brick", "default:brick")
-minetest.alias_node("tree", "default:tree")
-minetest.alias_node("jungletree", "default:jungletree")
-minetest.alias_node("junglegrass", "default:junglegrass")
-minetest.alias_node("leaves", "default:leaves")
-minetest.alias_node("cactus", "default:cactus")
-minetest.alias_node("papyrus", "default:papyrus")
-minetest.alias_node("bookshelf", "default:bookshelf")
-minetest.alias_node("glass", "default:glass")
-minetest.alias_node("wooden_fence", "default:fence_wood")
-minetest.alias_node("rail", "default:rail")
-minetest.alias_node("ladder", "default:ladder")
-minetest.alias_node("wood", "default:wood")
-minetest.alias_node("mese", "default:mese")
-minetest.alias_node("cloud", "default:cloud")
-minetest.alias_node("water_flowing", "default:water_flowing")
-minetest.alias_node("water_source", "default:water_source")
-minetest.alias_node("lava_flowing", "default:lava_flowing")
-minetest.alias_node("lava_source", "default:lava_source")
-minetest.alias_node("torch", "default:torch")
-minetest.alias_node("sign_wall", "default:sign_wall")
-minetest.alias_node("furnace", "default:furnace")
-minetest.alias_node("chest", "default:chest")
-minetest.alias_node("locked_chest", "default:chest_locked")
-minetest.alias_node("cobble", "default:cobble")
-minetest.alias_node("mossycobble", "default:mossycobble")
-minetest.alias_node("steelblock", "default:steelblock")
-minetest.alias_node("nyancat", "default:nyancat")
-minetest.alias_node("nyancat_rainbow", "default:nyancat_rainbow")
-minetest.alias_node("sapling", "default:sapling")
-minetest.alias_node("apple", "default:apple")
+minetest.register_alias("stone", "default:stone")
+minetest.register_alias("dirt_with_grass", "default:dirt_with_grass")
+minetest.register_alias("dirt_with_grass_footsteps", "default:dirt_with_grass_footsteps")
+minetest.register_alias("dirt", "default:dirt")
+minetest.register_alias("sand", "default:sand")
+minetest.register_alias("gravel", "default:gravel")
+minetest.register_alias("sandstone", "default:sandstone")
+minetest.register_alias("clay", "default:clay")
+minetest.register_alias("brick", "default:brick")
+minetest.register_alias("tree", "default:tree")
+minetest.register_alias("jungletree", "default:jungletree")
+minetest.register_alias("junglegrass", "default:junglegrass")
+minetest.register_alias("leaves", "default:leaves")
+minetest.register_alias("cactus", "default:cactus")
+minetest.register_alias("papyrus", "default:papyrus")
+minetest.register_alias("bookshelf", "default:bookshelf")
+minetest.register_alias("glass", "default:glass")
+minetest.register_alias("wooden_fence", "default:fence_wood")
+minetest.register_alias("rail", "default:rail")
+minetest.register_alias("ladder", "default:ladder")
+minetest.register_alias("wood", "default:wood")
+minetest.register_alias("mese", "default:mese")
+minetest.register_alias("cloud", "default:cloud")
+minetest.register_alias("water_flowing", "default:water_flowing")
+minetest.register_alias("water_source", "default:water_source")
+minetest.register_alias("lava_flowing", "default:lava_flowing")
+minetest.register_alias("lava_source", "default:lava_source")
+minetest.register_alias("torch", "default:torch")
+minetest.register_alias("sign_wall", "default:sign_wall")
+minetest.register_alias("furnace", "default:furnace")
+minetest.register_alias("chest", "default:chest")
+minetest.register_alias("locked_chest", "default:chest_locked")
+minetest.register_alias("cobble", "default:cobble")
+minetest.register_alias("mossycobble", "default:mossycobble")
+minetest.register_alias("steelblock", "default:steelblock")
+minetest.register_alias("nyancat", "default:nyancat")
+minetest.register_alias("nyancat_rainbow", "default:nyancat_rainbow")
+minetest.register_alias("sapling", "default:sapling")
+minetest.register_alias("apple", "default:apple")
 
-minetest.alias_tool("WPick", "default:pick_wood")
-minetest.alias_tool("STPick", "default:pick_stone")
-minetest.alias_tool("SteelPick", "default:pick_steel")
-minetest.alias_tool("MesePick", "default:pick_mese")
-minetest.alias_tool("WShovel", "default:shovel_wood")
-minetest.alias_tool("STShovel", "default:shovel_stone")
-minetest.alias_tool("SteelShovel", "default:shovel_steel")
-minetest.alias_tool("WAxe", "default:axe_wood")
-minetest.alias_tool("STAxe", "default:axe_stone")
-minetest.alias_tool("SteelAxe", "default:axe_steel")
-minetest.alias_tool("WSword", "default:sword_wood")
-minetest.alias_tool("STSword", "default:sword_stone")
-minetest.alias_tool("SteelSword", "default:sword_steel")
+minetest.register_alias("WPick", "default:pick_wood")
+minetest.register_alias("STPick", "default:pick_stone")
+minetest.register_alias("SteelPick", "default:pick_steel")
+minetest.register_alias("MesePick", "default:pick_mese")
+minetest.register_alias("WShovel", "default:shovel_wood")
+minetest.register_alias("STShovel", "default:shovel_stone")
+minetest.register_alias("SteelShovel", "default:shovel_steel")
+minetest.register_alias("WAxe", "default:axe_wood")
+minetest.register_alias("STAxe", "default:axe_stone")
+minetest.register_alias("SteelAxe", "default:axe_steel")
+minetest.register_alias("WSword", "default:sword_wood")
+minetest.register_alias("STSword", "default:sword_stone")
+minetest.register_alias("SteelSword", "default:sword_steel")
 
-minetest.alias_craftitem("Stick", "default:stick")
-minetest.alias_craftitem("paper", "default:paper")
-minetest.alias_craftitem("book", "default:book")
-minetest.alias_craftitem("lump_of_coal", "default:coal_lump")
-minetest.alias_craftitem("lump_of_iron", "default:iron_lump")
-minetest.alias_craftitem("lump_of_clay", "default:clay_lump")
-minetest.alias_craftitem("steel_ingot", "default:steel_ingot")
-minetest.alias_craftitem("clay_brick", "default:clay_brick")
-minetest.alias_craftitem("scorched_stuff", "default:scorched_stuff")
-minetest.alias_craftitem("apple", "default:apple")
+minetest.register_alias("Stick", "default:stick")
+minetest.register_alias("paper", "default:paper")
+minetest.register_alias("book", "default:book")
+minetest.register_alias("lump_of_coal", "default:coal_lump")
+minetest.register_alias("lump_of_iron", "default:iron_lump")
+minetest.register_alias("lump_of_clay", "default:clay_lump")
+minetest.register_alias("steel_ingot", "default:steel_ingot")
+minetest.register_alias("clay_brick", "default:clay_brick")
+minetest.register_alias("scorched_stuff", "default:scorched_stuff")
 
 --
 -- Old items
 --
 
 minetest.register_craftitem(":rat", {
-       image = "rat.png",
-       cookresult_itemstring = 'craft "cooked_rat" 1',
+       description = "Rat",
+       inventory_image = "rat.png",
        on_drop = function(item, dropper, pos)
                minetest.env:add_rat(pos)
-               return true
+               item:take_item()
+               return item
        end,
+       on_place = function(item, dropped, pointed)
+               pos = minetest.get_pointed_thing_position(pointed, true)
+               if pos ~= nil then
+                       minetest.env:add_rat(pos)
+                       item:take_item()
+                       return item
+               end
+       end
 })
 
 minetest.register_craftitem(":cooked_rat", {
-       image = "cooked_rat.png",
-       cookresult_itemstring = 'craft "scorched_stuff" 1',
-       on_place_on_ground = minetest.craftitem_place_item,
-       on_use = minetest.craftitem_eat(6),
+       description = "Cooked rat",
+       inventory_image = "cooked_rat.png",
+       on_use = minetest.item_eat(6),
 })
 
 minetest.register_craftitem(":firefly", {
-       image = "firefly.png",
+       description = "Firefly",
+       inventory_image = "firefly.png",
        on_drop = function(item, dropper, pos)
                minetest.env:add_firefly(pos)
-               return true
+               item:take_item()
+               return item
        end,
+       on_place = function(item, dropped, pointed)
+               pos = minetest.get_pointed_thing_position(pointed, true)
+               if pos ~= nil then
+                       minetest.env:add_firefly(pos)
+                       item:take_item()
+                       return item
+               end
+       end
+})
+
+minetest.register_craft({
+       type = "cooking",
+       output = "cooked_rat",
+       recipe = "rat",
+})
+
+minetest.register_craft({
+       type = "cooking",
+       output = "scorched_stuff",
+       recipe = "cooked_rat",
 })
 
 -- END
index 3b3b326c454f9967ffef4a823507b737212616ea..e82e26dc7a2fa09e2979c72dd815b2ddf63fca8d 100644 (file)
@@ -100,9 +100,8 @@ set(common_SRCS
        content_abm.cpp
        craftdef.cpp
        nameidmapping.cpp
-       tooldef.cpp
+       itemdef.cpp
        nodedef.cpp
-       craftitemdef.cpp
        luaentity_common.cpp
        scriptapi.cpp
        script.cpp
index c0e1714681122dd01ff31bc48dd6f03734298f23..066208569b1702cedaed7efbff1bc5f12714e6c1 100644 (file)
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "tile.h"
 #include <cmath>
 #include "settings.h"
-#include "nodedef.h" // For wield visualization
+#include "itemdef.h" // For wield visualization
 
 Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control):
        m_smgr(smgr),
@@ -37,6 +37,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control):
 
        m_wieldmgr(NULL),
        m_wieldnode(NULL),
+       m_wieldlight(0),
 
        m_draw_control(draw_control),
        m_viewing_range_min(5.0),
@@ -77,15 +78,15 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control):
        // all other 3D scene nodes and before the GUI.
        m_wieldmgr = smgr->createNewSceneManager();
        m_wieldmgr->addCameraSceneNode();
-       m_wieldnode = new ExtrudedSpriteSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr);
+       m_wieldnode = m_wieldmgr->addMeshSceneNode(createCubeMesh(v3f(1,1,1)), NULL);  // need a dummy mesh
 
        updateSettings();
 }
 
 Camera::~Camera()
 {
+       m_wieldnode->setMesh(NULL);
        m_wieldmgr->drop();
-       m_wieldnode->drop();
 }
 
 bool Camera::successfullyCreated(std::wstring& error_message)
@@ -292,7 +293,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize)
        }
        m_wieldnode->setPosition(wield_position);
        m_wieldnode->setRotation(wield_rotation);
-       m_wieldnode->updateLight(player->light);
+       m_wieldlight = player->light;
 
        // Render distance feedback loop
        updateViewingRange(frametime);
@@ -449,62 +450,38 @@ void Camera::updateSettings()
        m_wanted_frametime = 1.0 / wanted_fps;
 }
 
-void Camera::wield(const InventoryItem* item, IGameDef *gamedef)
+void Camera::setDigging(s32 button)
 {
-       //ITextureSource *tsrc = gamedef->tsrc();
-       INodeDefManager *ndef = gamedef->ndef();
+       if (m_digging_button == -1)
+               m_digging_button = button;
+}
 
-       if (item != NULL)
+void Camera::wield(const ItemStack &item, IGameDef *gamedef)
+{
+       IItemDefManager *idef = gamedef->idef();
+       scene::IMesh *wield_mesh = item.getDefinition(idef).wield_mesh;
+       if(wield_mesh)
        {
-               bool isCube = false;
-
-               // Try to make a MaterialItem cube.
-               if (std::string(item->getName()) == "MaterialItem")
-               {
-                       // A block-type material
-                       MaterialItem* mat_item = (MaterialItem*) item;
-                       content_t content = mat_item->getMaterial();
-                       switch(ndef->get(content).drawtype){
-                       case NDT_NORMAL:
-                       case NDT_LIQUID:
-                       case NDT_FLOWINGLIQUID:
-                       case NDT_GLASSLIKE:
-                       case NDT_ALLFACES:
-                       case NDT_ALLFACES_OPTIONAL:
-                               m_wieldnode->setCube(ndef->get(content).tiles);
-                               isCube = true;
-                               break;
-                       default:
-                               break;
-                       }
-               }
-
-               // If that failed, make an extruded sprite.
-               if (!isCube)
-               {
-                       m_wieldnode->setSprite(item->getImageRaw());
-               }
-
+               m_wieldnode->setMesh(wield_mesh);
                m_wieldnode->setVisible(true);
        }
        else
        {
-               // Bare hands
-               m_wieldnode->setSprite(gamedef->tsrc()->getTextureRaw("wieldhand.png"));
-               m_wieldnode->setVisible(true);
+               m_wieldnode->setVisible(false);
        }
 }
 
-void Camera::setDigging(s32 button)
-{
-       if (m_digging_button == -1)
-               m_digging_button = button;
-}
-
 void Camera::drawWieldedTool()
 {
+       // Set vertex colors of wield mesh according to light level
+       u8 li = decode_light(m_wieldlight);
+       video::SColor color(255,li,li,li);
+       setMeshColor(m_wieldnode->getMesh(), color);
+
+       // Clear Z buffer
        m_wieldmgr->getVideoDriver()->clearZBuffer();
 
+       // Draw the wielded node (in a separate scene manager)
        scene::ICameraSceneNode* cam = m_wieldmgr->getActiveCamera();
        cam->setAspectRatio(m_cameranode->getAspectRatio());
        cam->setFOV(m_cameranode->getFOV());
@@ -512,145 +489,3 @@ void Camera::drawWieldedTool()
        cam->setFarValue(100);
        m_wieldmgr->drawAll();
 }
-
-
-ExtrudedSpriteSceneNode::ExtrudedSpriteSceneNode(
-       scene::ISceneNode* parent,
-       scene::ISceneManager* mgr,
-       s32 id,
-       const v3f& position,
-       const v3f& rotation,
-       const v3f& scale
-):
-       ISceneNode(parent, mgr, id, position, rotation, scale)
-{
-       m_meshnode = mgr->addMeshSceneNode(NULL, this, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
-       m_cubemesh = NULL;
-       m_is_cube = false;
-       m_light = LIGHT_MAX;
-}
-
-ExtrudedSpriteSceneNode::~ExtrudedSpriteSceneNode()
-{
-       removeChild(m_meshnode);
-       if (m_cubemesh)
-               m_cubemesh->drop();
-}
-
-void ExtrudedSpriteSceneNode::setSprite(video::ITexture* texture)
-{
-       const v3f sprite_scale(40.0, 40.0, 4.0); // width, height, thickness
-
-       if (texture == NULL)
-       {
-               m_meshnode->setVisible(false);
-               return;
-       }
-
-       io::path name = getExtrudedName(texture);
-       scene::IMeshCache* cache = SceneManager->getMeshCache();
-       scene::IAnimatedMesh* mesh = cache->getMeshByName(name);
-       if (mesh != NULL)
-       {
-               // Extruded texture has been found in cache.
-               m_meshnode->setMesh(mesh);
-       }
-       else
-       {
-               // Texture was not yet extruded, do it now and save in cache
-               mesh = createExtrudedMesh(texture,
-                               SceneManager->getVideoDriver(),
-                               sprite_scale);
-               if (mesh == NULL)
-               {
-                       dstream << "Warning: failed to extrude sprite" << std::endl;
-                       m_meshnode->setVisible(false);
-                       return;
-               }
-               cache->addMesh(name, mesh);
-               m_meshnode->setMesh(mesh);
-               mesh->drop();
-       }
-
-       m_meshnode->getMaterial(0).setTexture(0, texture);
-       m_meshnode->getMaterial(0).setFlag(video::EMF_LIGHTING, false);
-       m_meshnode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, false);
-       m_meshnode->getMaterial(0).MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-       m_meshnode->setVisible(true);
-       m_is_cube = false;
-       updateLight(m_light);
-}
-
-void ExtrudedSpriteSceneNode::setCube(const TileSpec tiles[6])
-{
-       const v3f cube_scale(30.0, 30.0, 30.0);
-
-       if (m_cubemesh == NULL)
-       {
-               m_cubemesh = createCubeMesh(cube_scale);
-       }
-
-       m_meshnode->setMesh(m_cubemesh);
-       for (int i = 0; i < 6; ++i)
-       {
-               // Get the tile texture and atlas transformation
-               video::ITexture* atlas = tiles[i].texture.atlas;
-               v2f pos = tiles[i].texture.pos;
-               v2f size = tiles[i].texture.size;
-
-               // Set material flags and texture
-               video::SMaterial& material = m_meshnode->getMaterial(i);
-               material.setFlag(video::EMF_LIGHTING, false);
-               material.setFlag(video::EMF_BILINEAR_FILTER, false);
-               tiles[i].applyMaterialOptions(material);
-               material.setTexture(0, atlas);
-               material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y);
-               material.getTextureMatrix(0).setTextureScale(size.X, size.Y);
-       }
-       m_meshnode->setVisible(true);
-       m_is_cube = true;
-       updateLight(m_light);
-}
-
-void ExtrudedSpriteSceneNode::updateLight(u8 light)
-{
-       m_light = light;
-
-       u8 li = decode_light(light);
-       // Set brightness one lower than incoming light
-       diminish_light(li);
-       video::SColor color(255,li,li,li);
-       setMeshColor(m_meshnode->getMesh(), color);
-}
-
-void ExtrudedSpriteSceneNode::removeSpriteFromCache(video::ITexture* texture)
-{
-       scene::IMeshCache* cache = SceneManager->getMeshCache();
-       scene::IAnimatedMesh* mesh = cache->getMeshByName(getExtrudedName(texture));
-       if (mesh != NULL)
-               cache->removeMesh(mesh);
-}
-
-const core::aabbox3d<f32>& ExtrudedSpriteSceneNode::getBoundingBox() const
-{
-       return m_meshnode->getBoundingBox();
-}
-
-void ExtrudedSpriteSceneNode::OnRegisterSceneNode()
-{
-       if (IsVisible)
-               SceneManager->registerNodeForRendering(this);
-       ISceneNode::OnRegisterSceneNode();
-}
-
-void ExtrudedSpriteSceneNode::render()
-{
-       // do nothing
-}
-
-io::path ExtrudedSpriteSceneNode::getExtrudedName(video::ITexture* texture)
-{
-       io::path path = texture->getName();
-       path.append("/[extruded]");
-       return path;
-}
index d5789d807191527f533b69f9f3e4d6b72492751d..56c99d1014881a8ed1498d74ac7320ab196b039a 100644 (file)
@@ -26,12 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "tile.h"
 #include "utility.h"
 #include <ICameraSceneNode.h>
-#include <IMeshCache.h>
-#include <IAnimatedMesh.h>
 
 class LocalPlayer;
 struct MapDrawControl;
-class ExtrudedSpriteSceneNode;
 class IGameDef;
 
 /*
@@ -116,13 +113,13 @@ public:
        // Update settings from g_settings
        void updateSettings();
 
-       // Replace the wielded item mesh
-       void wield(const InventoryItem* item, IGameDef *gamedef);
-
        // Start digging animation
        // Pass 0 for left click, 1 for right click
        void setDigging(s32 button);
 
+       // Replace the wielded item mesh
+       void wield(const ItemStack &item, IGameDef *gamedef);
+
        // Draw the wielded tool.
        // This has to happen *after* the main scene is drawn.
        // Warning: This clears the Z buffer.
@@ -136,7 +133,8 @@ private:
        scene::ICameraSceneNode* m_cameranode;
 
        scene::ISceneManager* m_wieldmgr;
-       ExtrudedSpriteSceneNode* m_wieldnode;
+       scene::IMeshSceneNode* m_wieldnode;
+       u8 m_wieldlight;
 
        // draw control
        MapDrawControl& m_draw_control;
@@ -182,46 +180,4 @@ private:
        s32 m_digging_button;
 };
 
-
-/*
-       A scene node that displays a 2D mesh extruded into the third dimension,
-       to add an illusion of depth.
-
-       Since this class was created to display the wielded tool of the local
-       player, and only tools and items are rendered like this (but not solid
-       content like stone and mud, which are shown as cubes), the option to
-       draw a textured cube instead is provided.
- */
-class ExtrudedSpriteSceneNode: public scene::ISceneNode
-{
-public:
-       ExtrudedSpriteSceneNode(
-               scene::ISceneNode* parent,
-               scene::ISceneManager* mgr,
-               s32 id = -1,
-               const v3f& position = v3f(0,0,0),
-               const v3f& rotation = v3f(0,0,0),
-               const v3f& scale = v3f(1,1,1));
-       ~ExtrudedSpriteSceneNode();
-
-       void setSprite(video::ITexture* texture);
-       void setCube(const TileSpec tiles[6]);
-
-       void updateLight(u8 light);
-
-       void removeSpriteFromCache(video::ITexture* texture);
-
-       virtual const core::aabbox3d<f32>& getBoundingBox() const;
-       virtual void OnRegisterSceneNode();
-       virtual void render();
-
-private:
-       scene::IMeshSceneNode* m_meshnode;
-       scene::IMesh* m_cubemesh;
-       bool m_is_cube;
-       u8 m_light;
-
-       io::path getExtrudedName(video::ITexture* texture);
-};
-
 #endif
index 38ed1497876bfe34c105fa327b602627a47fcce1..feb8a3a1ed30d7abe85363dce1f361035d2aef15 100644 (file)
@@ -33,8 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "nodemetadata.h"
 #include "nodedef.h"
-#include "tooldef.h"
-#include "craftitemdef.h"
+#include "itemdef.h"
 #include <IFileSystem.h>
 #include "sha1.h"
 #include "base64.h"
@@ -207,14 +206,12 @@ Client::Client(
                std::string password,
                MapDrawControl &control,
                IWritableTextureSource *tsrc,
-               IWritableToolDefManager *tooldef,
-               IWritableNodeDefManager *nodedef,
-               IWritableCraftItemDefManager *craftitemdef
+               IWritableItemDefManager *itemdef,
+               IWritableNodeDefManager *nodedef
 ):
        m_tsrc(tsrc),
-       m_tooldef(tooldef),
+       m_itemdef(itemdef),
        m_nodedef(nodedef),
-       m_craftitemdef(craftitemdef),
        m_mesh_update_thread(this),
        m_env(
                new ClientMap(this, this, control,
@@ -234,9 +231,8 @@ Client::Client(
        m_access_denied(false),
        m_texture_receive_progress(0),
        m_textures_received(false),
-       m_tooldef_received(false),
-       m_nodedef_received(false),
-       m_craftitemdef_received(false)
+       m_itemdef_received(false),
+       m_nodedef_received(false)
 {
        m_packetcounter_timer = 0.0;
        //m_delete_unused_sectors_timer = 0.0;
@@ -251,12 +247,6 @@ Client::Client(
        else
                infostream<<"Not building texture atlas."<<std::endl;
        
-       // Update node textures
-       m_nodedef->updateTextures(m_tsrc);
-
-       // Start threads after setting up content definitions
-       m_mesh_update_thread.Start();
-
        /*
                Add local player
        */
@@ -266,9 +256,6 @@ Client::Client(
                player->updateName(playername);
 
                m_env.addPlayer(player);
-               
-               // Initialize player in the inventory context
-               m_inventory_context.current_player = player;
        }
 }
 
@@ -983,7 +970,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        //t4.stop();
 
                        //TimeTaker t1("inventory.deSerialize()", m_device);
-                       player->inventory.deSerialize(is, this);
+                       player->inventory.deSerialize(is);
                        //t1.stop();
 
                        m_inventory_updated = true;
@@ -1216,18 +1203,18 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        } else {
                                InventoryList *inv = player->inventory.getList("main");
                                std::string itemstring(deSerializeString(is));
-                               if (itemstring.empty()) {
-                                       inv->deleteItem(0);
-                                       infostream
-                                               <<"Client: empty player item for peer "
-                                               << peer_id << std::endl;
-                               } else {
-                                       std::istringstream iss(itemstring);
-                                       delete inv->changeItem(0,
-                                                       InventoryItem::deSerialize(iss, this));
-                                       infostream<<"Client: player item for peer " << peer_id << ": ";
-                                       player->getWieldItem()->serialize(infostream);
-                                       infostream<<std::endl;
+                               ItemStack item;
+                               item.deSerialize(itemstring, m_itemdef);
+                               inv->changeItem(0, item);
+                               if(itemstring.empty())
+                               {
+                                       infostream<<"Client: empty player item for peer "
+                                               <<peer_id<<std::endl;
+                               }
+                               else
+                               {
+                                       infostream<<"Client: player item for peer "
+                                               <<peer_id<<": "<<itemstring<<std::endl;
                                }
                        }
                }
@@ -1256,14 +1243,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
 
-
-               // Stop threads while updating content definitions
-               m_mesh_update_thread.setRun(false);
-               // Process the remaining TextureSource queue to let MeshUpdateThread
-               // get it's remaining textures and thus let it stop
-               while(m_mesh_update_thread.IsRunning()){
-                       m_tsrc->processQueue();
-               }
+               // Mesh update thread must be stopped while
+               // updating content definitions
+               assert(!m_mesh_update_thread.IsRunning());
 
                int num_textures = readU16(is);
 
@@ -1362,9 +1344,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        }
 
                }
-               // Resume threads
-               m_mesh_update_thread.setRun(true);
-               m_mesh_update_thread.Start();
 
                ClientEvent event;
                event.type = CE_TEXTURES_UPDATED;
@@ -1412,14 +1391,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
 
-               // Stop threads while updating content definitions
-               m_mesh_update_thread.setRun(false);
-               // Process the remaining TextureSource queue to let MeshUpdateThread
-               // get it's remaining textures and thus let it stop
-               while(m_mesh_update_thread.IsRunning()){
-                       m_tsrc->processQueue();
-               }
-               
+               // Mesh update thread must be stopped while
+               // updating content definitions
+               assert(!m_mesh_update_thread.IsRunning());
+
                /*
                        u16 command
                        u16 total number of texture bunches
@@ -1484,22 +1459,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                        img->drop();
                        rfile->drop();
                }
-               
-               if(m_nodedef_received && m_textures_received){
-                       // Rebuild inherited images and recreate textures
-                       m_tsrc->rebuildImagesAndTextures();
-
-                       // Update texture atlas
-                       if(g_settings->getBool("enable_texture_atlas"))
-                               m_tsrc->buildMainAtlas(this);
-                       
-                       // Update node textures
-                       m_nodedef->updateTextures(m_tsrc);
-               }
-
-               // Resume threads
-               m_mesh_update_thread.setRun(true);
-               m_mesh_update_thread.Start();
 
                ClientEvent event;
                event.type = CE_TEXTURES_UPDATED;
@@ -1507,82 +1466,53 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
        }
        else if(command == TOCLIENT_TOOLDEF)
        {
-               infostream<<"Client: Received tool definitions: packet size: "
-                               <<datasize<<std::endl;
-
-               std::string datastring((char*)&data[2], datasize-2);
-               std::istringstream is(datastring, std::ios_base::binary);
-
-               m_tooldef_received = true;
-
-               // Stop threads while updating content definitions
-               m_mesh_update_thread.setRun(false);
-               // Process the remaining TextureSource queue to let MeshUpdateThread
-               // get it's remaining textures and thus let it stop
-               while(m_mesh_update_thread.IsRunning()){
-                       m_tsrc->processQueue();
-               }
-               
-               std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
-               m_tooldef->deSerialize(tmp_is);
-               
-               // Resume threads
-               m_mesh_update_thread.setRun(true);
-               m_mesh_update_thread.Start();
+               infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
        }
        else if(command == TOCLIENT_NODEDEF)
        {
                infostream<<"Client: Received node definitions: packet size: "
                                <<datasize<<std::endl;
 
+               // Mesh update thread must be stopped while
+               // updating content definitions
+               assert(!m_mesh_update_thread.IsRunning());
+
+               // Decompress node definitions
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
-
-               m_nodedef_received = true;
-
-               // Stop threads while updating content definitions
-               m_mesh_update_thread.stop();
-
                std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
-               m_nodedef->deSerialize(tmp_is, this);
-               
-               if(m_textures_received){
-                       // Update texture atlas
-                       if(g_settings->getBool("enable_texture_atlas"))
-                               m_tsrc->buildMainAtlas(this);
-                       
-                       // Update node textures
-                       m_nodedef->updateTextures(m_tsrc);
-               }
+               std::ostringstream tmp_os;
+               decompressZlib(tmp_is, tmp_os);
 
-               // Resume threads
-               m_mesh_update_thread.setRun(true);
-               m_mesh_update_thread.Start();
+               // Deserialize node definitions
+               std::istringstream tmp_is2(tmp_os.str());
+               m_nodedef->deSerialize(tmp_is2);
+               m_nodedef_received = true;
        }
        else if(command == TOCLIENT_CRAFTITEMDEF)
        {
-               infostream<<"Client: Received CraftItem definitions: packet size: "
+               infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
+       }
+       else if(command == TOCLIENT_ITEMDEF)
+       {
+               infostream<<"Client: Received item definitions: packet size: "
                                <<datasize<<std::endl;
 
+               // Mesh update thread must be stopped while
+               // updating content definitions
+               assert(!m_mesh_update_thread.IsRunning());
+
+               // Decompress item definitions
                std::string datastring((char*)&data[2], datasize-2);
                std::istringstream is(datastring, std::ios_base::binary);
-
-               m_craftitemdef_received = true;
-
-               // Stop threads while updating content definitions
-               m_mesh_update_thread.setRun(false);
-               // Process the remaining TextureSource queue to let MeshUpdateThread
-               // get it's remaining textures and thus let it stop
-               while(m_mesh_update_thread.IsRunning()){
-                       m_tsrc->processQueue();
-               }
-               
                std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
-               m_craftitemdef->deSerialize(tmp_is);
-               
-               // Resume threads
-               m_mesh_update_thread.setRun(true);
-               m_mesh_update_thread.Start();
+               std::ostringstream tmp_os;
+               decompressZlib(tmp_is, tmp_os);
+
+               // Deserialize node definitions
+               std::istringstream tmp_is2(tmp_os.str());
+               m_itemdef->deSerialize(tmp_is2);
+               m_itemdef_received = true;
        }
        else
        {
@@ -1943,11 +1873,6 @@ void Client::selectPlayerItem(u16 item)
        //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
        m_playeritem = item;
        m_inventory_updated = true;
-
-       LocalPlayer *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-       player->wieldItem(item);
-
        sendPlayerItem(item);
 }
 
@@ -1971,17 +1896,19 @@ void Client::getLocalInventory(Inventory &dst)
        dst = player->inventory;
 }
 
-InventoryContext *Client::getInventoryContext()
-{
-       return &m_inventory_context;
-}
-
 Inventory* Client::getInventory(const InventoryLocation &loc)
 {
        switch(loc.type){
        case InventoryLocation::UNDEFINED:
        {}
        break;
+       case InventoryLocation::CURRENT_PLAYER:
+       {
+               Player *player = m_env.getLocalPlayer();
+               assert(player != NULL);
+               return &player->inventory;
+       }
+       break;
        case InventoryLocation::PLAYER:
        {
                Player *player = m_env.getPlayer(loc.name.c_str());
@@ -2003,36 +1930,6 @@ Inventory* Client::getInventory(const InventoryLocation &loc)
        }
        return NULL;
 }
-#if 0
-Inventory* Client::getInventory(InventoryContext *c, std::string id)
-{
-       if(id == "current_player")
-       {
-               assert(c->current_player);
-               return &(c->current_player->inventory);
-       }
-       
-       Strfnd fn(id);
-       std::string id0 = fn.next(":");
-
-       if(id0 == "nodemeta")
-       {
-               v3s16 p;
-               p.X = stoi(fn.next(","));
-               p.Y = stoi(fn.next(","));
-               p.Z = stoi(fn.next(","));
-               NodeMetadata* meta = getNodeMetadata(p);
-               if(meta)
-                       return meta->getInventory();
-               infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
-                               <<"no metadata found"<<std::endl;
-               return NULL;
-       }
-
-       infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
-       return NULL;
-}
-#endif
 void Client::inventoryAction(InventoryAction *a)
 {
        sendInventoryAction(a);
@@ -2234,6 +2131,32 @@ ClientEvent Client::getClientEvent()
        return m_client_event_queue.pop_front();
 }
 
+void Client::afterContentReceived()
+{
+       assert(m_itemdef_received);
+       assert(m_nodedef_received);
+       assert(m_textures_received);
+
+       // Rebuild inherited images and recreate textures
+       m_tsrc->rebuildImagesAndTextures();
+
+       // Update texture atlas
+       if(g_settings->getBool("enable_texture_atlas"))
+               m_tsrc->buildMainAtlas(this);
+
+       // Update node aliases
+       m_nodedef->updateAliases(m_itemdef);
+
+       // Update node textures
+       m_nodedef->updateTextures(m_tsrc);
+
+       // Update item textures and meshes
+       m_itemdef->updateTexturesAndMeshes(this);
+
+       // Start mesh update thread after setting up content definitions
+       m_mesh_update_thread.Start();
+}
+
 float Client::getRTT(void)
 {
        try{
@@ -2245,9 +2168,9 @@ float Client::getRTT(void)
 
 // IGameDef interface
 // Under envlock
-IToolDefManager* Client::getToolDefManager()
+IItemDefManager* Client::getItemDefManager()
 {
-       return m_tooldef;
+       return m_itemdef;
 }
 INodeDefManager* Client::getNodeDefManager()
 {
@@ -2258,10 +2181,6 @@ ICraftDefManager* Client::getCraftDefManager()
        return NULL;
        //return m_craftdef;
 }
-ICraftItemDefManager* Client::getCraftItemDefManager()
-{
-       return m_craftitemdef;
-}
 ITextureSource* Client::getTextureSource()
 {
        return m_tsrc;
index 49794acf585b16d4050024172ebec96c3809bcdc..6d5e5c52503eaca3a0b418c76be79eb3e1f7905c 100644 (file)
@@ -36,10 +36,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 struct MeshMakeData;
 class IGameDef;
 class IWritableTextureSource;
-class IWritableToolDefManager;
+class IWritableItemDefManager;
 class IWritableNodeDefManager;
 //class IWritableCraftDefManager;
-class IWritableCraftItemDefManager;
 
 class ClientNotReadyException : public BaseException
 {
@@ -167,9 +166,8 @@ public:
                        std::string password,
                        MapDrawControl &control,
                        IWritableTextureSource *tsrc,
-                       IWritableToolDefManager *tooldef,
-                       IWritableNodeDefManager *nodedef,
-                       IWritableCraftItemDefManager *craftitemdef
+                       IWritableItemDefManager *itemdef,
+                       IWritableNodeDefManager *nodedef
        );
        
        ~Client();
@@ -245,11 +243,8 @@ public:
        // Copies the inventory of the local player to parameter
        void getLocalInventory(Inventory &dst);
        
-       InventoryContext *getInventoryContext();
-
        /* InventoryManager interface */
        Inventory* getInventory(const InventoryLocation &loc);
-       //Inventory* getInventory(InventoryContext *c, std::string id);
        void inventoryAction(InventoryAction *a);
 
        // Gets closest object pointed by the shootline
@@ -323,20 +318,19 @@ public:
 
        bool texturesReceived()
        { return m_textures_received; }
-       bool tooldefReceived()
-       { return m_tooldef_received; }
+       bool itemdefReceived()
+       { return m_itemdef_received; }
        bool nodedefReceived()
        { return m_nodedef_received; }
-       bool craftitemdefReceived()
-       { return m_craftitemdef_received; }
        
+       void afterContentReceived();
+
        float getRTT(void);
 
        // IGameDef interface
-       virtual IToolDefManager* getToolDefManager();
+       virtual IItemDefManager* getItemDefManager();
        virtual INodeDefManager* getNodeDefManager();
        virtual ICraftDefManager* getCraftDefManager();
-       virtual ICraftItemDefManager* getCraftItemDefManager();
        virtual ITextureSource* getTextureSource();
        virtual u16 allocateUnknownNodeId(const std::string &name);
 
@@ -363,9 +357,8 @@ private:
        IntervalLimiter m_map_timer_and_unload_interval;
 
        IWritableTextureSource *m_tsrc;
-       IWritableToolDefManager *m_tooldef;
+       IWritableItemDefManager *m_itemdef;
        IWritableNodeDefManager *m_nodedef;
-       IWritableCraftItemDefManager *m_craftitemdef;
        MeshUpdateThread m_mesh_update_thread;
        ClientEnvironment m_env;
        con::Connection m_con;
@@ -387,13 +380,11 @@ private:
        std::string m_password;
        bool m_access_denied;
        std::wstring m_access_denied_reason;
-       InventoryContext m_inventory_context;
        Queue<ClientEvent> m_client_event_queue;
        float m_texture_receive_progress;
        bool m_textures_received;
-       bool m_tooldef_received;
+       bool m_itemdef_received;
        bool m_nodedef_received;
-       bool m_craftitemdef_received;
        friend class FarMesh;
 };
 
index 43de689e4af4f5f962ed2e0dd483feb401918af5..acb4f85308f9ef6240933618694d963f07870cb6 100644 (file)
@@ -39,9 +39,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                Make players to be handled mostly as ActiveObjects
        PROTOCOL_VERSION 6:
                Only non-cached textures are sent
+       PROTOCOL_VERSION 7:
+               Add TOCLIENT_ITEMDEF
+               Obsolete TOCLIENT_TOOLDEF
+               Obsolete TOCLIENT_CRAFTITEMDEF
+               Compress the contents of TOCLIENT_ITEMDEF and TOCLIENT_NODEDEF
 */
 
-#define PROTOCOL_VERSION 6
+#define PROTOCOL_VERSION 7
 
 #define PROTOCOL_ID 0x4f457403
 
@@ -252,6 +257,14 @@ enum ToClientCommand
                        string sha1_digest
                }
        */
+
+       TOCLIENT_ITEMDEF = 0x3d,
+       /*
+               u16 command
+               u32 length of next item
+               serialized ItemDefManager
+       */
+
 };
 
 enum ToServerCommand
index b9c5d2ac8bac413c1264ac6746e873fca0c471e7..8f308c99fe1321860f431e68109b58b07e9f0594 100644 (file)
@@ -666,7 +666,7 @@ void Connection::send(float dtime)
 // Receive packets from the network and buffers and create ConnectionEvents
 void Connection::receive()
 {
-       u32 datasize = 100000;
+       u32 datasize = m_max_packet_size * 2;  // Double it just to be safe
        // TODO: We can not know how many layers of header there are.
        // For now, just assume there are no other than the base headers.
        u32 packet_maxsize = datasize + BASE_HEADER_SIZE;
@@ -854,10 +854,6 @@ void Connection::receive()
                        dout_con<<"ProcessPacket returned data of size "
                                        <<resultdata.getSize()<<std::endl;
                        
-                       if(datasize < resultdata.getSize())
-                               throw InvalidIncomingDataException
-                                               ("Buffer too small for received data");
-                       
                        ConnectionEvent e;
                        e.dataReceived(peer_id, resultdata);
                        putEvent(e);
index d581b35dc24fd78b6b349f2fdca4d871622d41a8..a2708674b7295d86ba775a640a725b0a4bfca997 100644 (file)
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "content_cao.h"
 #include "tile.h"
 #include "environment.h"
+#include "collision.h"
 #include "settings.h"
 #include <ICameraSceneNode.h>
 #include <ITextSceneNode.h>
@@ -172,6 +173,8 @@ public:
        void updateLight(u8 light_at_pos);
        v3s16 getLightPosition();
        void updateNodePos();
+       void updateInfoText();
+       void updateTexture();
 
        void step(float dtime, ClientEnvironment *env);
 
@@ -191,7 +194,7 @@ private:
        core::aabbox3d<f32> m_selection_box;
        scene::IMeshSceneNode *m_node;
        v3f m_position;
-       std::string m_inventorystring;
+       std::string m_itemstring;
        std::string m_infotext;
 };
 
@@ -595,39 +598,13 @@ void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
        buf->drop();
        m_node = smgr->addMeshSceneNode(mesh, NULL);
        mesh->drop();
-       // Set it to use the materials of the meshbuffers directly.
-       // This is needed for changing the texture in the future
-       m_node->setReadOnlyMaterials(true);
        updateNodePos();
 
        /*
                Update image of node
        */
 
-       // Create an inventory item to see what is its image
-       std::istringstream is(m_inventorystring, std::ios_base::binary);
-       video::ITexture *texture = NULL;
-       try{
-               InventoryItem *item = NULL;
-               item = InventoryItem::deSerialize(is, m_gamedef);
-               infostream<<__FUNCTION_NAME<<": m_inventorystring=\""
-                               <<m_inventorystring<<"\" -> item="<<item
-                               <<std::endl;
-               if(item)
-               {
-                       texture = item->getImage();
-                       delete item;
-               }
-       }
-       catch(SerializationError &e)
-       {
-               infostream<<"WARNING: "<<__FUNCTION_NAME
-                               <<": error deSerializing inventorystring \""
-                               <<m_inventorystring<<"\""<<std::endl;
-       }
-       
-       // Set meshbuffer texture
-       buf->getMaterial().setTexture(0, texture);
+       updateTexture();
 }
 
 void ItemCAO::removeFromScene()
@@ -662,6 +639,51 @@ void ItemCAO::updateNodePos()
        m_node->setPosition(m_position);
 }
 
+void ItemCAO::updateInfoText()
+{
+       try{
+               IItemDefManager *idef = m_gamedef->idef();
+               ItemStack item;
+               item.deSerialize(m_itemstring, idef);
+               if(item.isKnown(idef))
+                       m_infotext = item.getDefinition(idef).description;
+               else
+                       m_infotext = "Unknown item: '" + m_itemstring + "'";
+               if(item.count >= 2)
+                       m_infotext += " (" + itos(item.count) + ")";
+       }
+       catch(SerializationError &e)
+       {
+               m_infotext = "Unknown item: '" + m_itemstring + "'";
+       }
+}
+
+void ItemCAO::updateTexture()
+{
+       if(m_node == NULL)
+               return;
+
+       // Create an inventory item to see what is its image
+       std::istringstream is(m_itemstring, std::ios_base::binary);
+       video::ITexture *texture = NULL;
+       try{
+               IItemDefManager *idef = m_gamedef->idef();
+               ItemStack item;
+               item.deSerialize(is, idef);
+               texture = item.getDefinition(idef).inventory_texture;
+       }
+       catch(SerializationError &e)
+       {
+               infostream<<"WARNING: "<<__FUNCTION_NAME
+                               <<": error deSerializing itemstring \""
+                               <<m_itemstring<<std::endl;
+       }
+       
+       // Set meshbuffer texture
+       m_node->getMaterial(0).setTexture(0, texture);
+}
+
+
 void ItemCAO::step(float dtime, ClientEnvironment *env)
 {
        if(m_node)
@@ -689,6 +711,13 @@ void ItemCAO::processMessage(const std::string &data)
                m_position = readV3F1000(is);
                updateNodePos();
        }
+       if(cmd == 1)
+       {
+               // itemstring
+               m_itemstring = deSerializeString(is);
+               updateInfoText();
+               updateTexture();
+       }
 }
 
 void ItemCAO::initialize(const std::string &data)
@@ -704,28 +733,12 @@ void ItemCAO::initialize(const std::string &data)
                        return;
                // pos
                m_position = readV3F1000(is);
-               // inventorystring
-               m_inventorystring = deSerializeString(is);
+               // itemstring
+               m_itemstring = deSerializeString(is);
        }
        
        updateNodePos();
-       
-       /*
-               Set infotext to item name if item cannot be deserialized
-       */
-       try{
-               InventoryItem *item = NULL;
-               item = InventoryItem::deSerialize(m_inventorystring, m_gamedef);
-               if(item){
-                       if(!item->isKnown())
-                               m_infotext = "Unknown item: '" + m_inventorystring + "'";
-               }
-               delete item;
-       }
-       catch(SerializationError &e)
-       {
-               m_infotext = "Unknown item: '" + m_inventorystring + "'";
-       }
+       updateInfoText();
 }
 
 /*
index e8e4fd231680a486136449e07646538dcb8a4144..79f49cbf814f588f7129e1e8f115400d6028f9a8 100644 (file)
@@ -20,112 +20,105 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "content_mapblock.h"
 
 #include "main.h" // For g_settings
-#include "mineral.h"
-#include "mapblock_mesh.h" // For MapBlock_LightColor()
+#include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
 #include "settings.h"
 #include "nodedef.h"
+#include "tile.h"
 #include "gamedef.h"
 
-#ifndef SERVER
 // Create a cuboid.
-//  material  - the material to use (for all 6 faces)
 //  collector - the MeshCollector for the resulting polygons
-//  pa        - texture atlas pointer for the material
+//  box       - the position and size of the box
+//  materials - the materials to use (for all 6 faces)
+//  pa        - texture atlas pointers for the materials
+//  matcount  - number of entries in "materials" and "pa", 1<=matcount<=6
 //  c         - vertex colour - used for all
-//  pos       - the position of the centre of the cuboid
-//  rz,ry,rz  - the radius of the cuboid in each dimension
 //  txc       - texture coordinates - this is a list of texture coordinates
 //              for the opposite corners of each face - therefore, there
 //              should be (2+2)*6=24 values in the list. Alternatively, pass
 //              NULL to use the entire texture for each face. The order of
-//              the faces in the list is top-backi-right-front-left-bottom
-//              If you specified 0,0,1,1 for each face, that would be the
-//              same as passing NULL.
-void makeCuboid(video::SMaterial &material, MeshCollector *collector,
-       AtlasPointer* pa, video::SColor &c,
-       v3f &pos, f32 rx, f32 ry, f32 rz, f32* txc)
+//              the faces in the list is up-down-right-left-back-front
+//              (compatible with ContentFeatures). If you specified 0,0,1,1
+//              for each face, that would be the same as passing NULL.
+void makeCuboid(MeshCollector *collector, const aabb3f &box,
+       const video::SMaterial *materials, const AtlasPointer *pa, int matcount,        
+       video::SColor &c, const f32* txc)
 {
-       f32 tu0=pa->x0();
-       f32 tu1=pa->x1();
-       f32 tv0=pa->y0();
-       f32 tv1=pa->y1();
-       f32 txus=tu1-tu0;
-       f32 txvs=tv1-tv0;
-
-       video::S3DVertex v[4] =
+       assert(matcount >= 1);
+
+       v3f min = box.MinEdge;
+       v3f max = box.MaxEdge;
+
+       if(txc == NULL)
        {
-               video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv1),
-               video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv1),
-               video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv0),
-               video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv0)
-       };
+               static const f32 txc_default[24] = {
+                       0,0,1,1,
+                       0,0,1,1,
+                       0,0,1,1,
+                       0,0,1,1,
+                       0,0,1,1,
+                       0,0,1,1
+               };
+               txc = txc_default;
+       }
 
-       for(int i=0;i<6;i++)
+       video::S3DVertex vertices[24] =
        {
-               switch(i)
-               {
-                       case 0: // top
-                               v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
-                               v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
-                               v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz;
-                               v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz;
-                               break;
-                       case 1: // back
-                               v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
-                               v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
-                               v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
-                               v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
-                               break;
-                       case 2: //right
-                               v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
-                               v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
-                               v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
-                               v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
-                               break;
-                       case 3: // front
-                               v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
-                               v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
-                               v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
-                               v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
-                               break;
-                       case 4: // left
-                               v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
-                               v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
-                               v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
-                               v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
-                               break;
-                       case 5: // bottom
-                               v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz;
-                               v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz;
-                               v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
-                               v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
-                               break;
-               }
+               // up
+               video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
+               video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
+               video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
+               video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
+               // down
+               video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
+               video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
+               video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
+               video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
+               // right
+               video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
+               video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
+               video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
+               video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
+               // left
+               video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
+               video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
+               video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
+               video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
+               // back
+               video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[16],txc[17]),
+               video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[18],txc[17]),
+               video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[18],txc[19]),
+               video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[16],txc[19]),
+               // front
+               video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[20],txc[21]),
+               video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[22],txc[21]),
+               video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[22],txc[23]),
+               video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[20],txc[23]),
+       };
 
-               if(txc!=NULL)
-               {
-                       v[0].TCoords.X=tu0+txus*txc[0]; v[0].TCoords.Y=tv0+txvs*txc[3];
-                       v[1].TCoords.X=tu0+txus*txc[2]; v[1].TCoords.Y=tv0+txvs*txc[3];
-                       v[2].TCoords.X=tu0+txus*txc[2]; v[2].TCoords.Y=tv0+txvs*txc[1];
-                       v[3].TCoords.X=tu0+txus*txc[0]; v[3].TCoords.Y=tv0+txvs*txc[1];
-                       txc+=4;
-               }
+       for(s32 j=0; j<24; j++)
+       {
+               int matindex = MYMIN(j/4, matcount-1);
+               vertices[j].TCoords *= pa[matindex].size;
+               vertices[j].TCoords += pa[matindex].pos;
+       }
 
-               for(u16 i=0; i<4; i++)
-                       v[i].Pos += pos;
-               u16 indices[] = {0,1,2,2,3,0};
-               collector->append(material, v, 4, indices, 6);
+       u16 indices[] = {0,1,2,2,3,0};
 
+       // Add to mesh collector
+       for(s32 j=0; j<24; j+=4)
+       {
+               int matindex = MYMIN(j/4, matcount-1);
+               collector->append(materials[matindex],
+                               vertices+j, 4, indices, 6);
        }
-
 }
-#endif
 
-#ifndef SERVER
 void mapblock_mesh_generate_special(MeshMakeData *data,
                MeshCollector &collector, IGameDef *gamedef)
 {
        INodeDefManager *nodedef = gamedef->ndef();
+       ITextureSource *tsrc = gamedef->getTextureSource();
 
        // 0ms
        //TimeTaker timer("mapblock_mesh_generate_special()");
@@ -521,7 +514,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
                        material_glass.setFlag(video::EMF_FOG_ENABLE, true);
                        material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       AtlasPointer pa_glass = f.tiles[0].texture;
+                       TileSpec tile_glass = getNodeTile(n, p, v3s16(0,0,0),
+                                       &data->m_temp_mods, tsrc, nodedef);
+                       AtlasPointer pa_glass = tile_glass.texture;
                        material_glass.setTexture(0, pa_glass.atlas);
 
                        u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
@@ -585,54 +580,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
                        material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
                        material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       AtlasPointer pa_leaves1 = f.tiles[0].texture;
+                       TileSpec tile_leaves1 = getNodeTile(n, p, v3s16(0,0,0),
+                                       &data->m_temp_mods, tsrc, nodedef);
+                       AtlasPointer pa_leaves1 = tile_leaves1.texture;
                        material_leaves1.setTexture(0, pa_leaves1.atlas);
 
                        u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
                        video::SColor c = MapBlock_LightColor(255, l);
 
-                       for(u32 j=0; j<6; j++)
-                       {
-                               video::S3DVertex vertices[4] =
-                               {
-                                       video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
-                                               pa_leaves1.x0(), pa_leaves1.y1()),
-                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
-                                               pa_leaves1.x1(), pa_leaves1.y1()),
-                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
-                                               pa_leaves1.x1(), pa_leaves1.y0()),
-                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
-                                               pa_leaves1.x0(), pa_leaves1.y0()),
-                               };
-
-                               // Rotations in the g_6dirs format
-                               if(j == 0) // Z+
-                                       for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateXZBy(0);
-                               else if(j == 1) // Y+
-                                       for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateYZBy(-90);
-                               else if(j == 2) // X+
-                                       for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateXZBy(-90);
-                               else if(j == 3) // Z-
-                                       for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateXZBy(180);
-                               else if(j == 4) // Y-
-                                       for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateYZBy(90);
-                               else if(j == 5) // X-
-                                       for(u16 i=0; i<4; i++)
-                                               vertices[i].Pos.rotateXZBy(90);
-
-                               for(u16 i=0; i<4; i++){
-                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
-                               }
-
-                               u16 indices[] = {0,1,2,2,3,0};
-                               // Add to mesh collector
-                               collector.append(material_leaves1, vertices, 4, indices, 6);
-                       }
+                       v3f pos = intToFloat(p+blockpos_nodes, BS);
+                       aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
+                       box.MinEdge += pos;
+                       box.MaxEdge += pos;
+                       makeCuboid(&collector, box,
+                                       &material_leaves1, &pa_leaves1, 1,
+                                       c, NULL);
                break;}
                case NDT_ALLFACES_OPTIONAL:
                        // This is always pre-converted to something else
@@ -824,9 +786,22 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
                        material_wood.setFlag(video::EMF_FOG_ENABLE, true);
                        material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       AtlasPointer pa_wood = f.tiles[0].texture;
+                       TileSpec tile_wood = getNodeTile(n, p, v3s16(0,0,0),
+                                       &data->m_temp_mods, tsrc, nodedef);
+                       AtlasPointer pa_wood = tile_wood.texture;
                        material_wood.setTexture(0, pa_wood.atlas);
 
+                       video::SMaterial material_wood_nomod;
+                       material_wood_nomod.setFlag(video::EMF_LIGHTING, false);
+                       material_wood_nomod.setFlag(video::EMF_BILINEAR_FILTER, false);
+                       material_wood_nomod.setFlag(video::EMF_FOG_ENABLE, true);
+                       material_wood_nomod.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+                       TileSpec tile_wood_nomod = getNodeTile(n, p, v3s16(0,0,0),
+                                       NULL, tsrc, nodedef);
+                       AtlasPointer pa_wood_nomod = tile_wood_nomod.texture;
+                       material_wood_nomod.setTexture(0, pa_wood_nomod.atlas);
+
                        u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
                        video::SColor c = MapBlock_LightColor(255, l);
 
@@ -834,18 +809,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        const f32 bar_rad=(f32)BS/20;
                        const f32 bar_len=(f32)(BS/2)-post_rad;
 
-                       // The post - always present
                        v3f pos = intToFloat(p+blockpos_nodes, BS);
+
+                       // The post - always present
+                       aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
+                       post.MinEdge += pos;
+                       post.MaxEdge += pos;
                        f32 postuv[24]={
+                                       0.4,0.4,0.6,0.6,
                                        0.4,0.4,0.6,0.6,
                                        0.35,0,0.65,1,
                                        0.35,0,0.65,1,
                                        0.35,0,0.65,1,
-                                       0.35,0,0.65,1,
-                                       0.4,0.4,0.6,0.6};
-                       makeCuboid(material_wood, &collector,
-                               &pa_wood, c, pos,
-                               post_rad,BS/2,post_rad, postuv);
+                                       0.35,0,0.65,1};
+                       makeCuboid(&collector, post, &material_wood,
+                                       &pa_wood, 1, c, postuv);
 
                        // Now a section of fence, +X, if there's a post there
                        v3s16 p2 = p;
@@ -854,9 +832,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        const ContentFeatures *f2 = &nodedef->get(n2);
                        if(f2->drawtype == NDT_FENCELIKE)
                        {
-                               pos = intToFloat(p+blockpos_nodes, BS);
-                               pos.X += BS/2;
-                               pos.Y += BS/4;
+                               aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
+                                               bar_len+BS/2,bar_rad+BS/4,bar_rad);
+                               bar.MinEdge += pos;
+                               bar.MaxEdge += pos;
                                f32 xrailuv[24]={
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6,
@@ -864,14 +843,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6};
-                               makeCuboid(material_wood, &collector,
-                                       &pa_wood, c, pos,
-                                       bar_len,bar_rad,bar_rad, xrailuv);
-
-                               pos.Y -= BS/2;
-                               makeCuboid(material_wood, &collector,
-                                       &pa_wood, c, pos,
-                                       bar_len,bar_rad,bar_rad, xrailuv);
+                               makeCuboid(&collector, bar, &material_wood_nomod,
+                                               &pa_wood_nomod, 1, c, xrailuv);
+                               bar.MinEdge.Y -= BS/2;
+                               bar.MaxEdge.Y -= BS/2;
+                               makeCuboid(&collector, bar, &material_wood_nomod,
+                                               &pa_wood_nomod, 1, c, xrailuv);
                        }
 
                        // Now a section of fence, +Z, if there's a post there
@@ -881,9 +858,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        f2 = &nodedef->get(n2);
                        if(f2->drawtype == NDT_FENCELIKE)
                        {
-                               pos = intToFloat(p+blockpos_nodes, BS);
-                               pos.Z += BS/2;
-                               pos.Y += BS/4;
+                               aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
+                                               bar_rad,bar_rad+BS/4,bar_len+BS/2);
+                               bar.MinEdge += pos;
+                               bar.MaxEdge += pos;
                                f32 zrailuv[24]={
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6,
@@ -891,14 +869,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6};
-                               makeCuboid(material_wood, &collector,
-                                       &pa_wood, c, pos,
-                                       bar_rad,bar_rad,bar_len, zrailuv);
-                               pos.Y -= BS/2;
-                               makeCuboid(material_wood, &collector,
-                                       &pa_wood, c, pos,
-                                       bar_rad,bar_rad,bar_len, zrailuv);
 
+                               makeCuboid(&collector, bar, &material_wood_nomod,
+                                               &pa_wood_nomod, 1, c, zrailuv);
+                               bar.MinEdge.Y -= BS/2;
+                               bar.MaxEdge.Y -= BS/2;
+                               makeCuboid(&collector, bar, &material_wood_nomod,
+                                               &pa_wood_nomod, 1, c, zrailuv);
                        }
                break;}
                case NDT_RAILLIKE:
@@ -1011,5 +988,4 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                }
        }
 }
-#endif
 
index a74760ba3fd707540ad73ffbc60a525dde36f73b..4701736cfd79849f8ada6eca4b039547967241b8 100644 (file)
@@ -26,75 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nameidmapping.h"
 #include <map>
 
-/*
-       Legacy node definitions
-*/
-
-#define WATER_ALPHA 160
-
-#define WATER_VISC 1
-#define LAVA_VISC 7
-
-void setConstantMaterialProperties(MaterialProperties &mprop, float time)
-{
-       mprop.diggability = DIGGABLE_CONSTANT;
-       mprop.constant_time = time;
-}
-
-void setStoneLikeMaterialProperties(MaterialProperties &mprop, float toughness)
-{
-       mprop.diggability = DIGGABLE_NORMAL;
-       mprop.weight = 5.0 * toughness;
-       mprop.crackiness = 1.0;
-       mprop.crumbliness = -0.1;
-       mprop.cuttability = -0.2;
-}
-
-void setDirtLikeMaterialProperties(MaterialProperties &mprop, float toughness)
-{
-       mprop.diggability = DIGGABLE_NORMAL;
-       mprop.weight = toughness * 1.2;
-       mprop.crackiness = 0;
-       mprop.crumbliness = 1.2;
-       mprop.cuttability = -0.4;
-}
-
-void setGravelLikeMaterialProperties(MaterialProperties &mprop, float toughness)
-{
-       mprop.diggability = DIGGABLE_NORMAL;
-       mprop.weight = toughness * 2.0;
-       mprop.crackiness = 0.2;
-       mprop.crumbliness = 1.5;
-       mprop.cuttability = -1.0;
-}
-
-void setWoodLikeMaterialProperties(MaterialProperties &mprop, float toughness)
-{
-       mprop.diggability = DIGGABLE_NORMAL;
-       mprop.weight = toughness * 1.0;
-       mprop.crackiness = 0.75;
-       mprop.crumbliness = -1.0;
-       mprop.cuttability = 1.5;
-}
-
-void setLeavesLikeMaterialProperties(MaterialProperties &mprop, float toughness)
-{
-       mprop.diggability = DIGGABLE_NORMAL;
-       mprop.weight = -0.5 * toughness;
-       mprop.crackiness = 0;
-       mprop.crumbliness = 0;
-       mprop.cuttability = 2.0;
-}
-
-void setGlassLikeMaterialProperties(MaterialProperties &mprop, float toughness)
-{
-       mprop.diggability = DIGGABLE_NORMAL;
-       mprop.weight = 0.1 * toughness;
-       mprop.crackiness = 2.0;
-       mprop.crumbliness = -1.0;
-       mprop.cuttability = -1.0;
-}
-
 /*
        Legacy node content type IDs
        Ranges:
@@ -209,46 +140,46 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version)
 
 void content_mapnode_get_name_id_mapping(NameIdMapping *nimap)
 {
-       nimap->set(0, "stone");
-       nimap->set(2, "water_flowing");
-       nimap->set(3, "torch");
-       nimap->set(9, "water_source");
-       nimap->set(14, "sign_wall");
-       nimap->set(15, "chest");
-       nimap->set(16, "furnace");
-       nimap->set(17, "locked_chest");
-       nimap->set(21, "wooden_fence");
-       nimap->set(30, "rail");
-       nimap->set(31, "ladder");
-       nimap->set(32, "lava_flowing");
-       nimap->set(33, "lava_source");
-       nimap->set(0x800, "dirt_with_grass");
-       nimap->set(0x801, "tree");
-       nimap->set(0x802, "leaves");
-       nimap->set(0x803, "dirt_with_grass_footsteps");
-       nimap->set(0x804, "mese");
-       nimap->set(0x805, "dirt");
-       nimap->set(0x806, "cloud");
-       nimap->set(0x807, "coalstone");
-       nimap->set(0x808, "wood");
-       nimap->set(0x809, "sand");
-       nimap->set(0x80a, "cobble");
-       nimap->set(0x80b, "steel");
-       nimap->set(0x80c, "glass");
-       nimap->set(0x80d, "mossycobble");
-       nimap->set(0x80e, "gravel");
-       nimap->set(0x80f, "sandstone");
-       nimap->set(0x810, "cactus");
-       nimap->set(0x811, "brick");
-       nimap->set(0x812, "clay");
-       nimap->set(0x813, "papyrus");
-       nimap->set(0x814, "bookshelf");
-       nimap->set(0x815, "jungletree");
-       nimap->set(0x816, "junglegrass");
-       nimap->set(0x817, "nyancat");
-       nimap->set(0x818, "nyancat_rainbow");
-       nimap->set(0x819, "apple");
-       nimap->set(0x820, "sapling");
+       nimap->set(0, "default:stone");
+       nimap->set(2, "default:water_flowing");
+       nimap->set(3, "default:torch");
+       nimap->set(9, "default:water_source");
+       nimap->set(14, "default:sign_wall");
+       nimap->set(15, "default:chest");
+       nimap->set(16, "default:furnace");
+       nimap->set(17, "default:chest_locked");
+       nimap->set(21, "default:fence_wood");
+       nimap->set(30, "default:rail");
+       nimap->set(31, "default:ladder");
+       nimap->set(32, "default:lava_flowing");
+       nimap->set(33, "default:lava_source");
+       nimap->set(0x800, "default:dirt_with_grass");
+       nimap->set(0x801, "default:tree");
+       nimap->set(0x802, "default:leaves");
+       nimap->set(0x803, "default:dirt_with_grass_footsteps");
+       nimap->set(0x804, "default:mese");
+       nimap->set(0x805, "default:dirt");
+       nimap->set(0x806, "default:cloud");
+       nimap->set(0x807, "default:coalstone");
+       nimap->set(0x808, "default:wood");
+       nimap->set(0x809, "default:sand");
+       nimap->set(0x80a, "default:cobble");
+       nimap->set(0x80b, "default:steelblock");
+       nimap->set(0x80c, "default:glass");
+       nimap->set(0x80d, "default:mossycobble");
+       nimap->set(0x80e, "default:gravel");
+       nimap->set(0x80f, "default:sandstone");
+       nimap->set(0x810, "default:cactus");
+       nimap->set(0x811, "default:brick");
+       nimap->set(0x812, "default:clay");
+       nimap->set(0x813, "default:papyrus");
+       nimap->set(0x814, "default:bookshelf");
+       nimap->set(0x815, "default:jungletree");
+       nimap->set(0x816, "default:junglegrass");
+       nimap->set(0x817, "default:nyancat");
+       nimap->set(0x818, "default:nyancat_rainbow");
+       nimap->set(0x819, "default:apple");
+       nimap->set(0x820, "default:sapling");
        // Static types
        nimap->set(CONTENT_IGNORE, "ignore");
        nimap->set(CONTENT_AIR, "air");
@@ -259,46 +190,46 @@ class NewNameGetter
 public:
        NewNameGetter()
        {
-               old_to_new["CONTENT_STONE"] = "stone";
-               old_to_new["CONTENT_WATER"] = "water_flowing";
-               old_to_new["CONTENT_TORCH"] = "torch";
-               old_to_new["CONTENT_WATERSOURCE"] = "water_source";
-               old_to_new["CONTENT_SIGN_WALL"] = "sign_wall";
-               old_to_new["CONTENT_CHEST"] = "chest";
-               old_to_new["CONTENT_FURNACE"] = "furnace";
-               old_to_new["CONTENT_LOCKABLE_CHEST"] = "locked_chest";
-               old_to_new["CONTENT_FENCE"] = "wooden_fence";
-               old_to_new["CONTENT_RAIL"] = "rail";
-               old_to_new["CONTENT_LADDER"] = "ladder";
-               old_to_new["CONTENT_LAVA"] = "lava_flowing";
-               old_to_new["CONTENT_LAVASOURCE"] = "lava_source";
-               old_to_new["CONTENT_GRASS"] = "dirt_with_grass";
-               old_to_new["CONTENT_TREE"] = "tree";
-               old_to_new["CONTENT_LEAVES"] = "leaves";
-               old_to_new["CONTENT_GRASS_FOOTSTEPS"] = "dirt_with_grass_footsteps";
-               old_to_new["CONTENT_MESE"] = "mese";
-               old_to_new["CONTENT_MUD"] = "dirt";
-               old_to_new["CONTENT_CLOUD"] = "cloud";
-               old_to_new["CONTENT_COALSTONE"] = "coalstone";
-               old_to_new["CONTENT_WOOD"] = "wood";
-               old_to_new["CONTENT_SAND"] = "sand";
-               old_to_new["CONTENT_COBBLE"] = "cobble";
-               old_to_new["CONTENT_STEEL"] = "steel";
-               old_to_new["CONTENT_GLASS"] = "glass";
-               old_to_new["CONTENT_MOSSYCOBBLE"] = "mossycobble";
-               old_to_new["CONTENT_GRAVEL"] = "gravel";
-               old_to_new["CONTENT_SANDSTONE"] = "sandstone";
-               old_to_new["CONTENT_CACTUS"] = "cactus";
-               old_to_new["CONTENT_BRICK"] = "brick";
-               old_to_new["CONTENT_CLAY"] = "clay";
-               old_to_new["CONTENT_PAPYRUS"] = "papyrus";
-               old_to_new["CONTENT_BOOKSHELF"] = "bookshelf";
-               old_to_new["CONTENT_JUNGLETREE"] = "jungletree";
-               old_to_new["CONTENT_JUNGLEGRASS"] = "junglegrass";
-               old_to_new["CONTENT_NC"] = "nyancat";
-               old_to_new["CONTENT_NC_RB"] = "nyancat_rainbow";
-               old_to_new["CONTENT_APPLE"] = "apple";
-               old_to_new["CONTENT_SAPLING"] = "sapling";
+               old_to_new["CONTENT_STONE"] = "default:stone";
+               old_to_new["CONTENT_WATER"] = "default:water_flowing";
+               old_to_new["CONTENT_TORCH"] = "default:torch";
+               old_to_new["CONTENT_WATERSOURCE"] = "default:water_source";
+               old_to_new["CONTENT_SIGN_WALL"] = "default:sign_wall";
+               old_to_new["CONTENT_CHEST"] = "default:chest";
+               old_to_new["CONTENT_FURNACE"] = "default:furnace";
+               old_to_new["CONTENT_LOCKABLE_CHEST"] = "default:locked_chest";
+               old_to_new["CONTENT_FENCE"] = "default:wooden_fence";
+               old_to_new["CONTENT_RAIL"] = "default:rail";
+               old_to_new["CONTENT_LADDER"] = "default:ladder";
+               old_to_new["CONTENT_LAVA"] = "default:lava_flowing";
+               old_to_new["CONTENT_LAVASOURCE"] = "default:lava_source";
+               old_to_new["CONTENT_GRASS"] = "default:dirt_with_grass";
+               old_to_new["CONTENT_TREE"] = "default:tree";
+               old_to_new["CONTENT_LEAVES"] = "default:leaves";
+               old_to_new["CONTENT_GRASS_FOOTSTEPS"] = "default:dirt_with_grass_footsteps";
+               old_to_new["CONTENT_MESE"] = "default:mese";
+               old_to_new["CONTENT_MUD"] = "default:dirt";
+               old_to_new["CONTENT_CLOUD"] = "default:cloud";
+               old_to_new["CONTENT_COALSTONE"] = "default:coalstone";
+               old_to_new["CONTENT_WOOD"] = "default:wood";
+               old_to_new["CONTENT_SAND"] = "default:sand";
+               old_to_new["CONTENT_COBBLE"] = "default:cobble";
+               old_to_new["CONTENT_STEEL"] = "default:steel";
+               old_to_new["CONTENT_GLASS"] = "default:glass";
+               old_to_new["CONTENT_MOSSYCOBBLE"] = "default:mossycobble";
+               old_to_new["CONTENT_GRAVEL"] = "default:gravel";
+               old_to_new["CONTENT_SANDSTONE"] = "default:sandstone";
+               old_to_new["CONTENT_CACTUS"] = "default:cactus";
+               old_to_new["CONTENT_BRICK"] = "default:brick";
+               old_to_new["CONTENT_CLAY"] = "default:clay";
+               old_to_new["CONTENT_PAPYRUS"] = "default:papyrus";
+               old_to_new["CONTENT_BOOKSHELF"] = "default:bookshelf";
+               old_to_new["CONTENT_JUNGLETREE"] = "default:jungletree";
+               old_to_new["CONTENT_JUNGLEGRASS"] = "default:junglegrass";
+               old_to_new["CONTENT_NC"] = "default:nyancat";
+               old_to_new["CONTENT_NC_RB"] = "default:nyancat_rainbow";
+               old_to_new["CONTENT_APPLE"] = "default:apple";
+               old_to_new["CONTENT_SAPLING"] = "default:sapling";
                // Just in case
                old_to_new["CONTENT_IGNORE"] = "ignore";
                old_to_new["CONTENT_AIR"] = "air";
@@ -334,605 +265,3 @@ content_t legacy_get_id(const std::string &oldname, INodeDefManager *ndef)
        return id;
 }
 
-// Initialize default (legacy) node definitions
-void content_mapnode_init(IWritableNodeDefManager *nodemgr)
-{
-       content_t i;
-       ContentFeatures f;
-
-       i = CONTENT_STONE;
-       f = ContentFeatures();
-       f.name = "stone";
-       f.setAllTextures("stone.png");
-       f.setInventoryTextureCube("stone.png", "stone.png", "stone.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.often_contains_mineral = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 1";
-       setStoneLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_GRASS;
-       f = ContentFeatures();
-       f.name = "dirt_with_grass";
-       f.setAllTextures("mud.png^grass_side.png");
-       f.setTexture(0, "grass.png");
-       f.setTexture(1, "mud.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_MUD)+" 1";
-       setDirtLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_GRASS_FOOTSTEPS;
-       f = ContentFeatures();
-       f.name = "dirt_with_grass_footsteps";
-       f.setAllTextures("mud.png^grass_side.png");
-       f.setTexture(0, "grass_footsteps.png");
-       f.setTexture(1, "mud.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_MUD)+" 1";
-       setDirtLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_MUD;
-       f = ContentFeatures();
-       f.name = "dirt";
-       f.setAllTextures("mud.png");
-       f.setInventoryTextureCube("mud.png", "mud.png", "mud.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setDirtLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_SAND;
-       f = ContentFeatures();
-       f.name = "sand";
-       f.setAllTextures("sand.png");
-       f.setInventoryTextureCube("sand.png", "sand.png", "sand.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.cookresult_item = std::string("MaterialItem2 ")+itos(CONTENT_GLASS)+" 1";
-       setDirtLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_GRAVEL;
-       f = ContentFeatures();
-       f.name = "gravel";
-       f.setAllTextures("gravel.png");
-       f.setInventoryTextureCube("gravel.png", "gravel.png", "gravel.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setGravelLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_SANDSTONE;
-       f = ContentFeatures();
-       f.name = "sandstone";
-       f.setAllTextures("sandstone.png");
-       f.setInventoryTextureCube("sandstone.png", "sandstone.png", "sandstone.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_SAND)+" 1";
-       setDirtLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_CLAY;
-       f = ContentFeatures();
-       f.name = "clay";
-       f.setAllTextures("clay.png");
-       f.setInventoryTextureCube("clay.png", "clay.png", "clay.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("CraftItem lump_of_clay 4");
-       setDirtLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_BRICK;
-       f = ContentFeatures();
-       f.name = "brick";
-       f.setAllTextures("brick.png");
-       f.setInventoryTextureCube("brick.png", "brick.png", "brick.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("CraftItem clay_brick 4");
-       setStoneLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_TREE;
-       f = ContentFeatures();
-       f.name = "tree";
-       f.setAllTextures("tree.png");
-       f.setTexture(0, "tree_top.png");
-       f.setTexture(1, "tree_top.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.cookresult_item = "CraftItem lump_of_coal 1";
-       f.furnace_burntime = 30;
-       setWoodLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_JUNGLETREE;
-       f = ContentFeatures();
-       f.name = "jungletree";
-       f.setAllTextures("jungletree.png");
-       f.setTexture(0, "jungletree_top.png");
-       f.setTexture(1, "jungletree_top.png");
-       f.param_type = CPT_MINERAL;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.furnace_burntime = 30;
-       setWoodLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_JUNGLEGRASS;
-       f = ContentFeatures();
-       f.name = "junglegrass";
-       f.drawtype = NDT_PLANTLIKE;
-       f.visual_scale = 1.3;
-       f.setAllTextures("junglegrass.png");
-       f.setInventoryTexture("junglegrass.png");
-       f.light_propagates = true;
-       f.param_type = CPT_LIGHT;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.walkable = false;
-       setLeavesLikeMaterialProperties(f.material, 1.0);
-       f.furnace_burntime = 2;
-       nodemgr->set(i, f);
-
-       i = CONTENT_LEAVES;
-       f = ContentFeatures();
-       f.name = "leaves";
-       f.drawtype = NDT_ALLFACES_OPTIONAL;
-       f.setAllTextures("leaves.png");
-       //f.setAllTextures("[noalpha:leaves.png");
-       f.light_propagates = true;
-       f.param_type = CPT_LIGHT;
-       f.extra_dug_item = std::string("MaterialItem2 ")+itos(CONTENT_SAPLING)+" 1";
-       f.extra_dug_item_rarity = 20;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setLeavesLikeMaterialProperties(f.material, 1.0);
-       f.furnace_burntime = 1.0;
-       nodemgr->set(i, f);
-
-       i = CONTENT_CACTUS;
-       f = ContentFeatures();
-       f.name = "cactus";
-       f.setAllTextures("cactus_side.png");
-       f.setTexture(0, "cactus_top.png");
-       f.setTexture(1, "cactus_top.png");
-       f.setInventoryTextureCube("cactus_top.png", "cactus_side.png", "cactus_side.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setWoodLikeMaterialProperties(f.material, 0.75);
-       f.furnace_burntime = 15;
-       nodemgr->set(i, f);
-
-       i = CONTENT_PAPYRUS;
-       f = ContentFeatures();
-       f.name = "papyrus";
-       f.drawtype = NDT_PLANTLIKE;
-       f.setAllTextures("papyrus.png");
-       f.setInventoryTexture("papyrus.png");
-       f.light_propagates = true;
-       f.param_type = CPT_LIGHT;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.walkable = false;
-       setLeavesLikeMaterialProperties(f.material, 0.5);
-       f.furnace_burntime = 1;
-       nodemgr->set(i, f);
-
-       i = CONTENT_BOOKSHELF;
-       f = ContentFeatures();
-       f.name = "bookshelf";
-       f.setAllTextures("bookshelf.png");
-       f.setTexture(0, "wood.png");
-       f.setTexture(1, "wood.png");
-       // FIXME: setInventoryTextureCube() only cares for the first texture
-       f.setInventoryTextureCube("bookshelf.png", "bookshelf.png", "bookshelf.png");
-       //f.setInventoryTextureCube("wood.png", "bookshelf.png", "bookshelf.png");
-       f.param_type = CPT_MINERAL;
-       f.is_ground_content = true;
-       setWoodLikeMaterialProperties(f.material, 0.75);
-       f.furnace_burntime = 30;
-       nodemgr->set(i, f);
-
-       i = CONTENT_GLASS;
-       f = ContentFeatures();
-       f.name = "glass";
-       f.drawtype = NDT_GLASSLIKE;
-       f.setAllTextures("glass.png");
-       f.light_propagates = true;
-       f.sunlight_propagates = true;
-       f.param_type = CPT_LIGHT;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.setInventoryTextureCube("glass.png", "glass.png", "glass.png");
-       setGlassLikeMaterialProperties(f.material, 1.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_FENCE;
-       f = ContentFeatures();
-       f.name = "wooden_fence";
-       f.drawtype = NDT_FENCELIKE;
-       f.setInventoryTexture("fence.png");
-       f.setTexture(0, "wood.png");
-       f.light_propagates = true;
-       f.param_type = CPT_LIGHT;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.selection_box.type = NODEBOX_FIXED;
-       f.selection_box.fixed = core::aabbox3d<f32>(
-                       -BS/7, -BS/2, -BS/7, BS/7, BS/2, BS/7);
-       f.furnace_burntime = 30/2;
-       setWoodLikeMaterialProperties(f.material, 0.75);
-       nodemgr->set(i, f);
-
-       i = CONTENT_RAIL;
-       f = ContentFeatures();
-       f.name = "rail";
-       f.drawtype = NDT_RAILLIKE;
-       f.setInventoryTexture("rail.png");
-       f.setTexture(0, "rail.png");
-       f.setTexture(1, "rail_curved.png");
-       f.setTexture(2, "rail_t_junction.png");
-       f.setTexture(3, "rail_crossing.png");
-       f.light_propagates = true;
-       f.param_type = CPT_LIGHT;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.walkable = false;
-       f.selection_box.type = NODEBOX_FIXED;
-       f.furnace_burntime = 5;
-       setDirtLikeMaterialProperties(f.material, 0.75);
-       nodemgr->set(i, f);
-
-       i = CONTENT_LADDER;
-       f = ContentFeatures();
-       f.name = "ladder";
-       f.drawtype = NDT_SIGNLIKE;
-       f.setAllTextures("ladder.png");
-       f.setInventoryTexture("ladder.png");
-       f.light_propagates = true;
-       f.param_type = CPT_LIGHT;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem ")+itos(i)+" 1";
-       f.wall_mounted = true;
-       f.walkable = false;
-       f.climbable = true;
-       f.selection_box.type = NODEBOX_WALLMOUNTED;
-       f.furnace_burntime = 5;
-       setWoodLikeMaterialProperties(f.material, 0.5);
-
-       nodemgr->set(i, f);
-
-       i = CONTENT_COALSTONE;
-       f = ContentFeatures();
-       f.name = "coalstone";
-       f.setAllTextures("stone.png^mineral_coal.png");
-       f.is_ground_content = true;
-       setStoneLikeMaterialProperties(f.material, 1.5);
-       nodemgr->set(i, f);
-
-       i = CONTENT_WOOD;
-       f = ContentFeatures();
-       f.name = "wood";
-       f.setAllTextures("wood.png");
-       f.setInventoryTextureCube("wood.png", "wood.png", "wood.png");
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.furnace_burntime = 30/4;
-       setWoodLikeMaterialProperties(f.material, 0.75);
-       nodemgr->set(i, f);
-
-       i = CONTENT_MESE;
-       f = ContentFeatures();
-       f.name = "mese";
-       f.setAllTextures("mese.png");
-       f.setInventoryTextureCube("mese.png", "mese.png", "mese.png");
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.furnace_burntime = 30;
-       setStoneLikeMaterialProperties(f.material, 0.5);
-       nodemgr->set(i, f);
-
-       i = CONTENT_CLOUD;
-       f = ContentFeatures();
-       f.name = "cloud";
-       f.setAllTextures("cloud.png");
-       f.setInventoryTextureCube("cloud.png", "cloud.png", "cloud.png");
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       nodemgr->set(i, f);
-
-       i = CONTENT_AIR;
-       f = ContentFeatures();
-       f.name = "air";
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = true;
-       f.sunlight_propagates = true;
-       f.walkable = false;
-       f.pointable = false;
-       f.diggable = false;
-       f.buildable_to = true;
-       nodemgr->set(i, f);
-
-       i = CONTENT_WATER;
-       f = ContentFeatures();
-       f.name = "water_flowing";
-       f.drawtype = NDT_FLOWINGLIQUID;
-       f.setAllTextures("water.png");
-       f.alpha = WATER_ALPHA;
-       f.setInventoryTextureCube("water.png", "water.png", "water.png");
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = true;
-       f.walkable = false;
-       f.pointable = false;
-       f.diggable = false;
-       f.buildable_to = true;
-       f.liquid_type = LIQUID_FLOWING;
-       f.liquid_alternative_flowing = "water_flowing";
-       f.liquid_alternative_source = "water_source";
-       f.liquid_viscosity = WATER_VISC;
-       f.post_effect_color = video::SColor(64, 100, 100, 200);
-       f.setSpecialMaterial(0, MaterialSpec("water.png", false));
-       f.setSpecialMaterial(1, MaterialSpec("water.png", true));
-       nodemgr->set(i, f);
-
-       i = CONTENT_WATERSOURCE;
-       f = ContentFeatures();
-       f.name = "water_source";
-       f.drawtype = NDT_LIQUID;
-       f.setAllTextures("water.png");
-       f.alpha = WATER_ALPHA;
-       f.setInventoryTextureCube("water.png", "water.png", "water.png");
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = true;
-       f.walkable = false;
-       f.pointable = false;
-       f.diggable = false;
-       f.buildable_to = true;
-       f.liquid_type = LIQUID_SOURCE;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.liquid_alternative_flowing = "water_flowing";
-       f.liquid_alternative_source = "water_source";
-       f.liquid_viscosity = WATER_VISC;
-       f.post_effect_color = video::SColor(64, 100, 100, 200);
-       // New-style water source material (mostly unused)
-       f.setSpecialMaterial(0, MaterialSpec("water.png", false));
-       nodemgr->set(i, f);
-
-       i = CONTENT_LAVA;
-       f = ContentFeatures();
-       f.name = "lava_flowing";
-       f.drawtype = NDT_FLOWINGLIQUID;
-       f.setAllTextures("lava.png");
-       f.setInventoryTextureCube("lava.png", "lava.png", "lava.png");
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = false;
-       f.light_source = LIGHT_MAX-1;
-       f.walkable = false;
-       f.pointable = false;
-       f.diggable = false;
-       f.buildable_to = true;
-       f.liquid_type = LIQUID_FLOWING;
-       f.liquid_alternative_flowing = "lava_flowing";
-       f.liquid_alternative_source = "lava_source";
-       f.liquid_viscosity = LAVA_VISC;
-       f.damage_per_second = 4*2;
-       f.post_effect_color = video::SColor(192, 255, 64, 0);
-       f.setSpecialMaterial(0, MaterialSpec("lava.png", false));
-       f.setSpecialMaterial(1, MaterialSpec("lava.png", true));
-       nodemgr->set(i, f);
-
-       i = CONTENT_LAVASOURCE;
-       f = ContentFeatures();
-       f.name = "lava_source";
-       f.drawtype = NDT_LIQUID;
-       f.setAllTextures("lava.png");
-       f.setInventoryTextureCube("lava.png", "lava.png", "lava.png");
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = false;
-       f.light_source = LIGHT_MAX-1;
-       f.walkable = false;
-       f.pointable = false;
-       f.diggable = false;
-       f.buildable_to = true;
-       f.liquid_type = LIQUID_SOURCE;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.liquid_alternative_flowing = "lava_flowing";
-       f.liquid_alternative_source = "lava_source";
-       f.liquid_viscosity = LAVA_VISC;
-       f.damage_per_second = 4*2;
-       f.post_effect_color = video::SColor(192, 255, 64, 0);
-       // New-style lava source material (mostly unused)
-       f.setSpecialMaterial(0, MaterialSpec("lava.png", false));
-       f.furnace_burntime = 60;
-       nodemgr->set(i, f);
-
-       i = CONTENT_TORCH;
-       f = ContentFeatures();
-       f.name = "torch";
-       f.drawtype = NDT_TORCHLIKE;
-       f.setTexture(0, "torch_on_floor.png");
-       f.setTexture(1, "torch_on_ceiling.png");
-       f.setTexture(2, "torch.png");
-       f.setInventoryTexture("torch_on_floor.png");
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = true;
-       f.sunlight_propagates = true;
-       f.walkable = false;
-       f.wall_mounted = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.light_source = LIGHT_MAX-1;
-       f.selection_box.type = NODEBOX_WALLMOUNTED;
-       f.selection_box.wall_top = core::aabbox3d<f32>(
-                       -BS/10, BS/2-BS/3.333*2, -BS/10, BS/10, BS/2, BS/10);
-       f.selection_box.wall_bottom = core::aabbox3d<f32>(
-                       -BS/10, -BS/2, -BS/10, BS/10, -BS/2+BS/3.333*2, BS/10);
-       f.selection_box.wall_side = core::aabbox3d<f32>(
-                       -BS/2, -BS/3.333, -BS/10, -BS/2+BS/3.333, BS/3.333, BS/10);
-       setConstantMaterialProperties(f.material, 0.0);
-       f.furnace_burntime = 4;
-       nodemgr->set(i, f);
-
-       i = CONTENT_SIGN_WALL;
-       f = ContentFeatures();
-       f.name = "sign_wall";
-       f.drawtype = NDT_SIGNLIKE;
-       f.setAllTextures("sign_wall.png");
-       f.setInventoryTexture("sign_wall.png");
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = true;
-       f.sunlight_propagates = true;
-       f.walkable = false;
-       f.wall_mounted = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.metadata_name = "sign";
-       setConstantMaterialProperties(f.material, 0.5);
-       f.selection_box.type = NODEBOX_WALLMOUNTED;
-       f.furnace_burntime = 10;
-       nodemgr->set(i, f);
-
-       i = CONTENT_CHEST;
-       f = ContentFeatures();
-       f.name = "chest";
-       f.param_type = CPT_FACEDIR_SIMPLE;
-       f.setAllTextures("chest_side.png");
-       f.setTexture(0, "chest_top.png");
-       f.setTexture(1, "chest_top.png");
-       f.setTexture(5, "chest_front.png"); // Z-
-       f.setInventoryTexture("chest_top.png");
-       //f.setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png");
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.metadata_name = "chest";
-       setWoodLikeMaterialProperties(f.material, 1.0);
-       f.furnace_burntime = 30;
-       nodemgr->set(i, f);
-
-       i = CONTENT_LOCKABLE_CHEST;
-       f = ContentFeatures();
-       f.name = "locked_chest";
-       f.param_type = CPT_FACEDIR_SIMPLE;
-       f.setAllTextures("chest_side.png");
-       f.setTexture(0, "chest_top.png");
-       f.setTexture(1, "chest_top.png");
-       f.setTexture(5, "chest_lock.png"); // Z-
-       f.setInventoryTexture("chest_lock.png");
-       //f.setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png");
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.metadata_name = "locked_chest";
-       setWoodLikeMaterialProperties(f.material, 1.0);
-       f.furnace_burntime = 30;
-       nodemgr->set(i, f);
-
-       i = CONTENT_FURNACE;
-       f = ContentFeatures();
-       f.name = "furnace";
-       f.param_type = CPT_FACEDIR_SIMPLE;
-       f.setAllTextures("furnace_side.png");
-       f.setTexture(5, "furnace_front.png"); // Z-
-       f.setInventoryTexture("furnace_front.png");
-       //f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 6";
-       f.metadata_name = "furnace";
-       setStoneLikeMaterialProperties(f.material, 3.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_COBBLE;
-       f = ContentFeatures();
-       f.name = "cobble";
-       f.setAllTextures("cobble.png");
-       f.setInventoryTextureCube("cobble.png", "cobble.png", "cobble.png");
-       f.param_type = CPT_NONE;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.cookresult_item = std::string("MaterialItem2 ")+itos(CONTENT_STONE)+" 1";
-       setStoneLikeMaterialProperties(f.material, 0.9);
-       nodemgr->set(i, f);
-
-       i = CONTENT_MOSSYCOBBLE;
-       f = ContentFeatures();
-       f.name = "mossycobble";
-       f.setAllTextures("mossycobble.png");
-       f.setInventoryTextureCube("mossycobble.png", "mossycobble.png", "mossycobble.png");
-       f.param_type = CPT_NONE;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setStoneLikeMaterialProperties(f.material, 0.8);
-       nodemgr->set(i, f);
-
-       i = CONTENT_STEEL;
-       f = ContentFeatures();
-       f.name = "steelblock";
-       f.setAllTextures("steel_block.png");
-       f.setInventoryTextureCube("steel_block.png", "steel_block.png",
-                       "steel_block.png");
-       f.param_type = CPT_NONE;
-       f.is_ground_content = true;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setStoneLikeMaterialProperties(f.material, 5.0);
-       nodemgr->set(i, f);
-
-       i = CONTENT_NC;
-       f = ContentFeatures();
-       f.name = "nyancat";
-       f.param_type = CPT_FACEDIR_SIMPLE;
-       f.setAllTextures("nc_side.png");
-       f.setTexture(5, "nc_front.png"); // Z-
-       f.setTexture(4, "nc_back.png"); // Z+
-       f.setInventoryTexture("nc_front.png");
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setStoneLikeMaterialProperties(f.material, 3.0);
-       f.furnace_burntime = 1;
-       nodemgr->set(i, f);
-
-       i = CONTENT_NC_RB;
-       f = ContentFeatures();
-       f.name = "nyancat_rainbow";
-       f.setAllTextures("nc_rb.png");
-       f.setInventoryTexture("nc_rb.png");
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       setStoneLikeMaterialProperties(f.material, 3.0);
-       f.furnace_burntime = 1;
-       nodemgr->set(i, f);
-
-       i = CONTENT_SAPLING;
-       f = ContentFeatures();
-       f.name = "sapling";
-       f.drawtype = NDT_PLANTLIKE;
-       f.visual_scale = 1.0;
-       f.setAllTextures("sapling.png");
-       f.setInventoryTexture("sapling.png");
-       f.param_type = CPT_LIGHT;
-       f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1";
-       f.light_propagates = true;
-       f.walkable = false;
-       setConstantMaterialProperties(f.material, 0.0);
-       f.furnace_burntime = 10;
-       nodemgr->set(i, f);
-
-       i = CONTENT_APPLE;
-       f = ContentFeatures();
-       f.name = "apple";
-       f.drawtype = NDT_PLANTLIKE;
-       f.visual_scale = 1.0;
-       f.setAllTextures("apple.png");
-       f.setInventoryTexture("apple.png");
-       f.param_type = CPT_LIGHT;
-       f.light_propagates = true;
-       f.sunlight_propagates = true;
-       f.walkable = false;
-       f.dug_item = std::string("CraftItem apple 1");
-       setConstantMaterialProperties(f.material, 0.0);
-       f.furnace_burntime = 3;
-       nodemgr->set(i, f);
-}
-
-
index b928e4407707e89ea4863b1a18f6c1eae5082c79..003b7edd7e32e61551fb5fa8088f548e1b590fb3 100644 (file)
@@ -26,12 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        Legacy node definitions
 */
 
-class IWritableNodeDefManager;
-
-// Initialize legacy node definitions
-// Not used used anywhere else than in test.cpp (and SHALL NOT BE)
-void content_mapnode_init(IWritableNodeDefManager *nodemgr);
-
 // Backwards compatibility for non-extended content types in v19
 extern content_t trans_table_19[21][2];
 MapNode mapnode_translate_from_internal(MapNode n_from, u8 version);
index 8666051a4a1538b16244dc48fc49a93ee3370240..9fb5450cfa42d42103dcae65b575c0eefaf662b8 100644 (file)
@@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "inventory.h"
 #include "log.h"
 #include "utility.h"
+#include "craftdef.h"
+#include "gamedef.h"
 
 class Inventory;
 
@@ -125,9 +127,14 @@ public:
        virtual bool step(float dtime);
        virtual bool nodeRemovalDisabled();
        virtual std::string getInventoryDrawSpecString();
+       
+protected:
+       bool getCookResult(bool remove, std::string &cookresult, float &cooktime);
+       bool getBurnResult(bool remove, float &burntime);
 
 private:
        Inventory *m_inventory;
+       std::string m_infotext;
        float m_step_accumulator;
        float m_fuel_totaltime;
        float m_fuel_time;
@@ -185,9 +192,7 @@ ChestNodeMetadata::ChestNodeMetadata(IGameDef *gamedef):
        NodeMetadata(gamedef)
 {
        NodeMetadata::registerType(typeId(), typeName(), create, create);
-       
-       m_inventory = new Inventory();
-       m_inventory->addList("0", 8*4);
+       m_inventory = NULL;
 }
 ChestNodeMetadata::~ChestNodeMetadata()
 {
@@ -200,18 +205,21 @@ u16 ChestNodeMetadata::typeId() const
 NodeMetadata* ChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
 {
        ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
-       d->m_inventory->deSerialize(is, gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->deSerialize(is);
        return d;
 }
 NodeMetadata* ChestNodeMetadata::create(IGameDef *gamedef)
 {
        ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->addList("0", 8*4);
        return d;
 }
 NodeMetadata* ChestNodeMetadata::clone(IGameDef *gamedef)
 {
        ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
-       *d->m_inventory = *m_inventory;
+       d->m_inventory = new Inventory(*m_inventory);
        return d;
 }
 void ChestNodeMetadata::serializeBody(std::ostream &os)
@@ -253,9 +261,7 @@ LockingChestNodeMetadata::LockingChestNodeMetadata(IGameDef *gamedef):
        NodeMetadata(gamedef)
 {
        NodeMetadata::registerType(typeId(), typeName(), create, create);
-
-       m_inventory = new Inventory();
-       m_inventory->addList("0", 8*4);
+       m_inventory = NULL;
 }
 LockingChestNodeMetadata::~LockingChestNodeMetadata()
 {
@@ -269,18 +275,21 @@ NodeMetadata* LockingChestNodeMetadata::create(std::istream &is, IGameDef *gamed
 {
        LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
        d->setOwner(deSerializeString(is));
-       d->m_inventory->deSerialize(is, gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->deSerialize(is);
        return d;
 }
 NodeMetadata* LockingChestNodeMetadata::create(IGameDef *gamedef)
 {
        LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->addList("0", 8*4);
        return d;
 }
 NodeMetadata* LockingChestNodeMetadata::clone(IGameDef *gamedef)
 {
        LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
-       *d->m_inventory = *m_inventory;
+       d->m_inventory = new Inventory(*m_inventory);
        return d;
 }
 void LockingChestNodeMetadata::serializeBody(std::ostream &os)
@@ -324,10 +333,9 @@ FurnaceNodeMetadata::FurnaceNodeMetadata(IGameDef *gamedef):
 {
        NodeMetadata::registerType(typeId(), typeName(), create, create);
        
-       m_inventory = new Inventory();
-       m_inventory->addList("fuel", 1);
-       m_inventory->addList("src", 1);
-       m_inventory->addList("dst", 4);
+       m_inventory = NULL;
+
+       m_infotext = "Furnace is inactive";
 
        m_step_accumulator = 0;
        m_fuel_totaltime = 0;
@@ -346,26 +354,52 @@ u16 FurnaceNodeMetadata::typeId() const
 NodeMetadata* FurnaceNodeMetadata::clone(IGameDef *gamedef)
 {
        FurnaceNodeMetadata *d = new FurnaceNodeMetadata(m_gamedef);
-       *d->m_inventory = *m_inventory;
+       d->m_inventory = new Inventory(*m_inventory);
        return d;
 }
 NodeMetadata* FurnaceNodeMetadata::create(std::istream &is, IGameDef *gamedef)
 {
        FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
 
-       d->m_inventory->deSerialize(is, gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->deSerialize(is);
 
-       int temp;
+       int temp = 0;
        is>>temp;
        d->m_fuel_totaltime = (float)temp/10;
+       temp = 0;
        is>>temp;
        d->m_fuel_time = (float)temp/10;
+       temp = 0;
+       is>>temp;
+       d->m_src_totaltime = (float)temp/10;
+       temp = 0;
+       is>>temp;
+       d->m_src_time = (float)temp/10;
+
+       if(is.eof())
+       {
+               // Old furnaces didn't serialize src_totaltime and src_time
+               d->m_src_totaltime = 0;
+               d->m_src_time = 0;
+               d->m_infotext = "";
+       }
+       else
+       {
+               // New furnaces also serialize the infotext (so that the
+               // client doesn't need to have the list of cooking recipes).
+               d->m_infotext = deSerializeJsonString(is);
+       }
 
        return d;
 }
 NodeMetadata* FurnaceNodeMetadata::create(IGameDef *gamedef)
 {
        FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->addList("fuel", 1);
+       d->m_inventory->addList("src", 1);
+       d->m_inventory->addList("dst", 4);
        return d;
 }
 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
@@ -373,36 +407,13 @@ void FurnaceNodeMetadata::serializeBody(std::ostream &os)
        m_inventory->serialize(os);
        os<<itos(m_fuel_totaltime*10)<<" ";
        os<<itos(m_fuel_time*10)<<" ";
+       os<<itos(m_src_totaltime*10)<<" ";
+       os<<itos(m_src_time*10)<<" ";
+       os<<serializeJsonString(m_infotext);
 }
 std::string FurnaceNodeMetadata::infoText()
 {
-       //return "Furnace";
-       if(m_fuel_time >= m_fuel_totaltime)
-       {
-               const InventoryList *src_list = m_inventory->getList("src");
-               assert(src_list);
-               const InventoryItem *src_item = src_list->getItem(0);
-
-               if(src_item && src_item->isCookable()) {
-                       InventoryList *dst_list = m_inventory->getList("dst");
-                       if(!dst_list->roomForCookedItem(src_item))
-                               return "Furnace is overloaded";
-                       return "Furnace is out of fuel";
-               }
-               else
-                       return "Furnace is inactive";
-       }
-       else
-       {
-               std::string s = "Furnace is active";
-               // Do this so it doesn't always show (0%) for weak fuel
-               if(m_fuel_totaltime > 3) {
-                       s += " (";
-                       s += itos(m_fuel_time/m_fuel_totaltime*100);
-                       s += "%)";
-               }
-               return s;
-       }
+       return m_infotext;
 }
 bool FurnaceNodeMetadata::nodeRemovalDisabled()
 {
@@ -440,86 +451,107 @@ bool FurnaceNodeMetadata::step(float dtime)
                dtime = interval;
 
                //infostream<<"Furnace step dtime="<<dtime<<std::endl;
-               
+
                InventoryList *dst_list = m_inventory->getList("dst");
                assert(dst_list);
 
-               InventoryList *src_list = m_inventory->getList("src");
-               assert(src_list);
-               InventoryItem *src_item = src_list->getItem(0);
-               
+               // Check
+               // 1. if the source item is cookable
+               // 2. if there is room for the cooked item
+               std::string cookresult;
+               float cooktime;
+               bool cookable = getCookResult(false, cookresult, cooktime);
+               ItemStack cookresult_item;
                bool room_available = false;
-               
-               if(src_item && src_item->isCookable())
-                       room_available = dst_list->roomForCookedItem(src_item);
-               
-               // Start only if there are free slots in dst, so that it can
-               // accomodate any result item
-               if(room_available)
+               if(cookable)
                {
-                       m_src_totaltime = src_item->getCookTime();
+                       cookresult_item.deSerialize(cookresult, m_gamedef->idef());
+                       room_available = dst_list->roomForItem(cookresult_item);
                }
-               else
+
+               // Step fuel time
+               bool burning = (m_fuel_time < m_fuel_totaltime);
+               if(burning)
                {
-                       m_src_time = 0;
-                       m_src_totaltime = 0;
+                       changed = true;
+                       m_fuel_time += dtime;
                }
-               
-               /*
-                       If fuel is burning, increment the burn counters.
-                       If item finishes cooking, move it to result.
-               */
-               if(m_fuel_time < m_fuel_totaltime)
+
+               std::string infotext;
+               if(room_available)
                {
-                       //infostream<<"Furnace is active"<<std::endl;
-                       m_fuel_time += dtime;
-                       m_src_time += dtime;
-                       if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
-                                       && src_item)
+                       float burntime;
+                       if(burning)
+                       {
+                               changed = true;
+                               m_src_time += dtime;
+                               m_src_totaltime = cooktime;
+                               infotext = "Furnace is cooking";
+                       }
+                       else if(getBurnResult(true, burntime))
+                       {
+                               // Fuel inserted
+                               changed = true;
+                               m_fuel_time = 0;
+                               m_fuel_totaltime = burntime;
+                               //m_src_time += dtime;
+                               //m_src_totaltime = cooktime;
+                               infotext = "Furnace is cooking";
+                       }
+                       else
                        {
-                               InventoryItem *cookresult = src_item->createCookResult();
-                               dst_list->addItem(cookresult);
-                               src_list->decrementMaterials(1);
                                m_src_time = 0;
                                m_src_totaltime = 0;
+                               infotext = "Furnace is out of fuel";
+                       }
+                       if(m_src_totaltime > 0.001 && m_src_time >= m_src_totaltime)
+                       {
+                               // One item fully cooked
+                               changed = true;
+                               dst_list->addItem(cookresult_item);
+                               getCookResult(true, cookresult, cooktime); // decrement source
+                               m_src_totaltime = 0;
+                               m_src_time = 0;
                        }
-                       changed = true;
-                       
-                       // If the fuel was not used up this step, just keep burning it
-                       if(m_fuel_time < m_fuel_totaltime)
-                               continue;
                }
-               
-               /*
-                       Get the source again in case it has all burned
-               */
-               src_item = src_list->getItem(0);
-               
-               /*
-                       If there is no source item, or the source item is not cookable,
-                       or the furnace is still cooking, or the furnace became overloaded, stop loop.
-               */
-               if(src_item == NULL || !room_available || m_fuel_time < m_fuel_totaltime ||
-                       dst_list->roomForCookedItem(src_item) == false)
+               else
                {
-                       m_step_accumulator = 0;
-                       break;
+                       // Not cookable or no room available
+                       m_src_totaltime = 0;
+                       m_src_time = 0;
+                       if(cookable)
+                               infotext = "Furnace is overloaded";
+                       else if(burning)
+                               infotext = "Furnace is active";
+                       else
+                       {
+                               infotext = "Furnace is inactive";
+                               m_fuel_totaltime = 0;
+                               m_fuel_time = 0;
+                       }
+               }
+
+               // Do this so it doesn't always show (0%) for weak fuel
+               if(m_fuel_totaltime > 3) {
+                       infotext += " (";
+                       infotext += itos(m_fuel_time/m_fuel_totaltime*100);
+                       infotext += "%)";
                }
-               
-               //infostream<<"Furnace is out of fuel"<<std::endl;
 
-               InventoryList *fuel_list = m_inventory->getList("fuel");
-               assert(fuel_list);
-               const InventoryItem *fuel_item = fuel_list->getItem(0);
+               if(infotext != m_infotext)
+               {
+                       m_infotext = infotext;
+                       changed = true;
+               }
 
-               if(fuel_item && fuel_item->getBurnTime() >= 0){
-                       m_fuel_totaltime = fuel_item->getBurnTime();
+               if(burning && m_fuel_time >= m_fuel_totaltime)
+               {
                        m_fuel_time = 0;
-                       fuel_list->decrementMaterials(1);
-                       changed = true;
-               } else {
-                       //infostream<<"No fuel found"<<std::endl;
-                       // No fuel, stop loop.
+                       m_fuel_totaltime = 0;
+               }
+
+               if(!changed)
+               {
                        m_step_accumulator = 0;
                        break;
                }
@@ -535,6 +567,43 @@ std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
                "list[current_name;dst;5,1;2,2;]"
                "list[current_player;main;0,5;8,4;]";
 }
+bool FurnaceNodeMetadata::getCookResult(bool remove,
+               std::string &cookresult, float &cooktime)
+{
+       std::vector<ItemStack> items;
+       InventoryList *src_list = m_inventory->getList("src");
+       assert(src_list);
+       items.push_back(src_list->getItem(0));
+
+       CraftInput ci(CRAFT_METHOD_COOKING, 1, items);
+       CraftOutput co;
+       bool found = m_gamedef->getCraftDefManager()->getCraftResult(
+                       ci, co, remove, m_gamedef);
+       if(remove)
+               src_list->changeItem(0, ci.items[0]);
+
+       cookresult = co.item;
+       cooktime = co.time;
+       return found;
+}
+bool FurnaceNodeMetadata::getBurnResult(bool remove, float &burntime)
+{
+       std::vector<ItemStack> items;
+       InventoryList *fuel_list = m_inventory->getList("fuel");
+       assert(fuel_list);
+       items.push_back(fuel_list->getItem(0));
+
+       CraftInput ci(CRAFT_METHOD_FUEL, 1, items);
+       CraftOutput co;
+       bool found = m_gamedef->getCraftDefManager()->getCraftResult(
+                       ci, co, remove, m_gamedef);
+       if(remove)
+               fuel_list->changeItem(0, ci.items[0]);
+
+       burntime = co.time;
+       return found;
+}
+
 
 /*
        GenericNodeMetadata
@@ -571,7 +640,7 @@ public:
        GenericNodeMetadata(IGameDef *gamedef):
                NodeMetadata(gamedef),
 
-               m_inventory(new Inventory()),
+               m_inventory(NULL),
                m_text(""),
                m_owner(""),
 
@@ -594,7 +663,7 @@ public:
        {
                GenericNodeMetadata *d = new GenericNodeMetadata(m_gamedef);
 
-               *d->m_inventory = *m_inventory;
+               d->m_inventory = new Inventory(*m_inventory);
                d->m_text = m_text;
                d->m_owner = m_owner;
 
@@ -610,13 +679,15 @@ public:
        static NodeMetadata* create(IGameDef *gamedef)
        {
                GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
+               d->m_inventory = new Inventory(gamedef->idef());
                return d;
        }
        static NodeMetadata* create(std::istream &is, IGameDef *gamedef)
        {
                GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
                
-               d->m_inventory->deSerialize(is, gamedef);
+               d->m_inventory = new Inventory(gamedef->idef());
+               d->m_inventory->deSerialize(is);
                d->m_text = deSerializeLongString(is);
                d->m_owner = deSerializeString(is);
                
index f195b16bd41e79385dc7832a4a20f8e2d66c1c8a..02be64c64fd20b85fa1a5c5e2d724974abcd602f 100644 (file)
@@ -24,8 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "main.h" // For g_profiler
 #include "profiler.h"
 #include "serialization.h" // For compressZlib
-#include "materials.h" // For MaterialProperties
-#include "tooldef.h" // ToolDiggingProperties
+#include "materials.h" // For MaterialProperties and ToolDiggingProperties
+#include "gamedef.h"
 
 core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
 
@@ -114,9 +114,10 @@ void TestSAO::step(float dtime, bool send_recommended)
 ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
 
 ItemSAO::ItemSAO(ServerEnvironment *env, v3f pos,
-               const std::string inventorystring):
+               const std::string itemstring):
        ServerActiveObject(env, pos),
-       m_inventorystring(inventorystring),
+       m_itemstring(itemstring),
+       m_itemstring_changed(false),
        m_speed_f(0,0,0),
        m_last_sent_position(0,0,0)
 {
@@ -134,10 +135,10 @@ ServerActiveObject* ItemSAO::create(ServerEnvironment *env, v3f pos,
        // check if version is supported
        if(version != 0)
                return NULL;
-       std::string inventorystring = deSerializeString(is);
+       std::string itemstring = deSerializeString(is);
        infostream<<"ItemSAO::create(): Creating item \""
-                       <<inventorystring<<"\""<<std::endl;
-       return new ItemSAO(env, pos, inventorystring);
+                       <<itemstring<<"\""<<std::endl;
+       return new ItemSAO(env, pos, itemstring);
 }
 
 void ItemSAO::step(float dtime, bool send_recommended)
@@ -175,17 +176,23 @@ void ItemSAO::step(float dtime, bool send_recommended)
                m_last_sent_position = pos_f;
 
                std::ostringstream os(std::ios::binary);
-               char buf[6];
                // command (0 = update position)
-               buf[0] = 0;
-               os.write(buf, 1);
+               writeU8(os, 0);
                // pos
-               writeS32((u8*)buf, m_base_position.X*1000);
-               os.write(buf, 4);
-               writeS32((u8*)buf, m_base_position.Y*1000);
-               os.write(buf, 4);
-               writeS32((u8*)buf, m_base_position.Z*1000);
-               os.write(buf, 4);
+               writeV3F1000(os, m_base_position);
+               // create message and add to list
+               ActiveObjectMessage aom(getId(), false, os.str());
+               m_messages_out.push_back(aom);
+       }
+       if(m_itemstring_changed)
+       {
+               m_itemstring_changed = false;
+
+               std::ostringstream os(std::ios::binary);
+               // command (1 = update itemstring)
+               writeU8(os, 1);
+               // itemstring
+               os<<serializeString(m_itemstring);
                // create message and add to list
                ActiveObjectMessage aom(getId(), false, os.str());
                m_messages_out.push_back(aom);
@@ -195,19 +202,12 @@ void ItemSAO::step(float dtime, bool send_recommended)
 std::string ItemSAO::getClientInitializationData()
 {
        std::ostringstream os(std::ios::binary);
-       char buf[6];
        // version
-       buf[0] = 0;
-       os.write(buf, 1);
+       writeU8(os, 0);
        // pos
-       writeS32((u8*)buf, m_base_position.X*1000);
-       os.write(buf, 4);
-       writeS32((u8*)buf, m_base_position.Y*1000);
-       os.write(buf, 4);
-       writeS32((u8*)buf, m_base_position.Z*1000);
-       os.write(buf, 4);
-       // inventorystring
-       os<<serializeString(m_inventorystring);
+       writeV3F1000(os, m_base_position);
+       // itemstring
+       os<<serializeString(m_itemstring);
        return os.str();
 }
 
@@ -215,42 +215,58 @@ std::string ItemSAO::getStaticData()
 {
        infostream<<__FUNCTION_NAME<<std::endl;
        std::ostringstream os(std::ios::binary);
-       char buf[1];
        // version
-       buf[0] = 0;
-       os.write(buf, 1);
-       // inventorystring
-       os<<serializeString(m_inventorystring);
+       writeU8(os, 0);
+       // itemstring
+       os<<serializeString(m_itemstring);
        return os.str();
 }
 
-InventoryItem * ItemSAO::createInventoryItem()
+ItemStack ItemSAO::createItemStack()
 {
        try{
-               std::istringstream is(m_inventorystring, std::ios_base::binary);
-               IGameDef *gamedef = m_env->getGameDef();
-               InventoryItem *item = InventoryItem::deSerialize(is, gamedef);
-               infostream<<__FUNCTION_NAME<<": m_inventorystring=\""
-                               <<m_inventorystring<<"\" -> item="<<item
+               IItemDefManager *idef = m_env->getGameDef()->idef();
+               ItemStack item;
+               item.deSerialize(m_itemstring, idef);
+               infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
+                               <<"\" -> item=\""<<item.getItemString()<<"\""
                                <<std::endl;
                return item;
        }
        catch(SerializationError &e)
        {
                infostream<<__FUNCTION_NAME<<": serialization error: "
-                               <<"m_inventorystring=\""<<m_inventorystring<<"\""<<std::endl;
-               return NULL;
+                               <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
+               return ItemStack();
        }
 }
 
 void ItemSAO::punch(ServerActiveObject *puncher, float time_from_last_punch)
 {
-       InventoryItem *item = createInventoryItem();
-       bool fits = puncher->addToInventory(item);
-       if(fits)
+       // Allow removing items in creative mode
+       if(g_settings->getBool("creative_mode") == true)
+       {
                m_removed = true;
-       else
-               delete item;
+               return;
+       }
+
+       ItemStack item = createItemStack();
+       Inventory *inv = puncher->getInventory();
+       if(inv != NULL)
+       {
+               std::string wieldlist = puncher->getWieldList();
+               ItemStack leftover = inv->addItem(wieldlist, item);
+               puncher->setInventoryModified();
+               if(leftover.empty())
+               {
+                       m_removed = true;
+               }
+               else
+               {
+                       m_itemstring = leftover.getItemString();
+                       m_itemstring_changed = true;
+               }
+       }
 }
 
 /*
@@ -436,14 +452,24 @@ std::string RatSAO::getStaticData()
 
 void RatSAO::punch(ServerActiveObject *puncher, float time_from_last_punch)
 {
-       std::istringstream is("CraftItem rat 1", std::ios_base::binary);
-       IGameDef *gamedef = m_env->getGameDef();
-       InventoryItem *item = InventoryItem::deSerialize(is, gamedef);
-       bool fits = puncher->addToInventory(item);
-       if(fits)
+       // Allow removing rats in creative mode
+       if(g_settings->getBool("creative_mode") == true)
+       {
                m_removed = true;
-       else
-               delete item;
+               return;
+       }
+
+       IItemDefManager *idef = m_env->getGameDef()->idef();
+       ItemStack item("rat", 1, 0, "", idef);
+       Inventory *inv = puncher->getInventory();
+       if(inv != NULL)
+       {
+               std::string wieldlist = puncher->getWieldList();
+               ItemStack leftover = inv->addItem(wieldlist, item);
+               puncher->setInventoryModified();
+               if(leftover.empty())
+                       m_removed = true;
+       }
 }
 
 /*
@@ -703,14 +729,20 @@ void Oerkki1SAO::punch(ServerActiveObject *puncher, float time_from_last_punch)
        mp.crackiness = -1.0;
        mp.cuttability = 1.0;
 
-       ToolDiggingProperties tp;
-       puncher->getWieldDiggingProperties(&tp);
+       IItemDefManager *idef = m_env->getGameDef()->idef();
+        ItemStack punchitem = puncher->getWieldedItem();
+        ToolDiggingProperties tp =
+                       punchitem.getToolDiggingProperties(idef);
 
        HittingProperties hitprop = getHittingProperties(&mp, &tp,
                        time_from_last_punch);
 
        doDamage(hitprop.hp);
-       puncher->damageWieldedItem(hitprop.wear);
+       if(g_settings->getBool("creative_mode") == false)
+       {
+               punchitem.addWear(hitprop.wear, idef);
+               puncher->setWieldedItem(punchitem);
+       }
 }
 
 void Oerkki1SAO::doDamage(u16 d)
@@ -1393,14 +1425,20 @@ void MobV2SAO::punch(ServerActiveObject *puncher, float time_from_last_punch)
        mp.crackiness = -1.0;
        mp.cuttability = 1.0;
 
-       ToolDiggingProperties tp;
-       puncher->getWieldDiggingProperties(&tp);
+       IItemDefManager *idef = m_env->getGameDef()->idef();
+        ItemStack punchitem = puncher->getWieldedItem();
+        ToolDiggingProperties tp =
+                       punchitem.getToolDiggingProperties(idef);
 
        HittingProperties hitprop = getHittingProperties(&mp, &tp,
                        time_from_last_punch);
 
        doDamage(hitprop.hp);
-       puncher->damageWieldedItem(hitprop.wear);
+       if(g_settings->getBool("creative_mode") == false)
+       {
+               punchitem.addWear(hitprop.wear, idef);
+               puncher->setWieldedItem(punchitem);
+       }
 }
 
 bool MobV2SAO::isPeaceful()
index c2bb9c3f5148abcc9824a8d5071918f715542b6e..f0c9cea9052c3a2a5fe2f582ec0c7f8bd1dcea0a 100644 (file)
@@ -40,8 +40,7 @@ private:
 class ItemSAO : public ServerActiveObject
 {
 public:
-       ItemSAO(ServerEnvironment *env, v3f pos,
-                       const std::string inventorystring);
+       ItemSAO(ServerEnvironment *env, v3f pos, const std::string itemstring);
        u8 getType() const
                {return ACTIVEOBJECT_TYPE_ITEM;}
        static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
@@ -49,11 +48,12 @@ public:
        void step(float dtime, bool send_recommended);
        std::string getClientInitializationData();
        std::string getStaticData();
-       InventoryItem* createInventoryItem();
+       ItemStack createItemStack();
        void punch(ServerActiveObject *puncher, float time_from_last_punch);
        float getMinimumSavedMovement(){ return 0.1*BS; }
 private:
-       std::string m_inventorystring;
+       std::string m_itemstring;
+       bool m_itemstring_changed;
        v3f m_speed_f;
        v3f m_last_sent_position;
        IntervalLimiter m_move_interval;
index 0cbb74ea07cae5e9f5ae2c718e496004a7d047b9..5bcbf6f941fdb5b4241d9a1655075183a3e1a2d0 100644 (file)
@@ -22,88 +22,738 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes.h"
 #include "log.h"
 #include <sstream>
+#include <set>
 #include "utility.h"
 #include "gamedef.h"
 #include "inventory.h"
-#include "inventorymanager.h" // checkItemCombination
 
-CraftPointerInput::~CraftPointerInput()
+
+// Deserialize an itemstring then return the name of the item
+static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef)
+{
+       ItemStack item;
+       item.deSerialize(itemstring, gamedef->idef());
+       return item.name;
+}
+
+// (mapcar craftGetItemName itemstrings)
+static std::vector<std::string> craftGetItemNames(
+               const std::vector<std::string> &itemstrings, IGameDef *gamedef)
+{
+       std::vector<std::string> result;
+       for(std::vector<std::string>::const_iterator
+                       i = itemstrings.begin();
+                       i != itemstrings.end(); i++)
+       {
+               result.push_back(craftGetItemName(*i, gamedef));
+       }
+       return result;
+}
+
+// Get name of each item, and return them as a new list.
+static std::vector<std::string> craftGetItemNames(
+               const std::vector<ItemStack> &items, IGameDef *gamedef)
 {
-       for(u32 i=0; i<items.size(); i++)
-               delete items[i];
+       std::vector<std::string> result;
+       for(std::vector<ItemStack>::const_iterator
+                       i = items.begin();
+                       i != items.end(); i++)
+       {
+               result.push_back(i->name);
+       }
+       return result;
 }
 
-CraftPointerInput createPointerInput(const CraftInput &ci, IGameDef *gamedef)
+// Compute bounding rectangle given a matrix of items
+// Returns false if every item is ""
+static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
+               unsigned int &min_x, unsigned int &max_x,
+               unsigned int &min_y, unsigned int &max_y)
 {
-       std::vector<InventoryItem*> items;
-       for(u32 i=0; i<ci.items.size(); i++){
-               InventoryItem *item = NULL;
-               if(ci.items[i] != ""){
-                       std::istringstream iss(ci.items[i], std::ios::binary);
-                       item = InventoryItem::deSerialize(iss, gamedef);
+       bool success = false;
+       unsigned int x = 0;
+       unsigned int y = 0;
+       for(std::vector<std::string>::const_iterator
+                       i = items.begin();
+                       i != items.end(); i++)
+       {
+               if(*i != "")  // Is this an actual item?
+               {
+                       if(!success)
+                       {
+                               // This is the first nonempty item
+                               min_x = max_x = x;
+                               min_y = max_y = y;
+                               success = true;
+                       }
+                       else
+                       {
+                               if(x < min_x) min_x = x;
+                               if(x > max_x) max_x = x;
+                               if(y < min_y) min_y = y;
+                               if(y > max_y) max_y = y;
+                       }
                }
-               items.push_back(item);
+
+               // Step coordinate
+               x++;
+               if(x == width)
+               {
+                       x = 0;
+                       y++;
+               }
+       }
+       return success;
+}
+
+// Convert a list of item names to a multiset
+static std::multiset<std::string> craftMakeMultiset(const std::vector<std::string> &names)
+{
+       std::multiset<std::string> set;
+       for(std::vector<std::string>::const_iterator
+                       i = names.begin();
+                       i != names.end(); i++)
+       {
+               if(*i != "")
+                       set.insert(*i);
        }
-       return CraftPointerInput(ci.width, items);
+       return set;
 }
 
-CraftInput createInput(const CraftPointerInput &cpi)
+// Removes 1 from each item stack
+static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
 {
-       std::vector<std::string> items;
-       for(u32 i=0; i<cpi.items.size(); i++){
-               if(cpi.items[i] == NULL)
-                       items.push_back("");
-               else{
-                       std::ostringstream oss(std::ios::binary);
-                       cpi.items[i]->serialize(oss);
-                       items.push_back(oss.str());
+       for(std::vector<ItemStack>::iterator
+                       i = input.items.begin();
+                       i != input.items.end(); i++)
+       {
+               if(i->count != 0)
+                       i->remove(1);
+       }
+}
+
+// Removes 1 from each item stack with replacement support
+// Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
+//   a water bucket will not be removed but replaced by an empty bucket.
+static void craftDecrementOrReplaceInput(CraftInput &input,
+               const CraftReplacements &replacements,
+               IGameDef *gamedef)
+{
+       if(replacements.pairs.empty())
+       {
+               craftDecrementInput(input, gamedef);
+               return;
+       }
+
+       // Make a copy of the replacements pair list
+       std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;
+
+       for(std::vector<ItemStack>::iterator
+                       i = input.items.begin();
+                       i != input.items.end(); i++)
+       {
+               if(i->count == 1)
+               {
+                       // Find an appropriate replacement
+                       bool found_replacement = false;
+                       for(std::vector<std::pair<std::string, std::string> >::iterator
+                                       j = pairs.begin();
+                                       j != pairs.end(); j++)
+                       {
+                               ItemStack from_item;
+                               from_item.deSerialize(j->first, gamedef->idef());
+                               if(i->name == from_item.name)
+                               {
+                                       i->deSerialize(j->second, gamedef->idef());
+                                       found_replacement = true;
+                                       pairs.erase(j);
+                                       break;
+                               }
+                       }
+                       // No replacement was found, simply decrement count to zero
+                       if(!found_replacement)
+                               i->remove(1);
+               }
+               else if(i->count >= 2)
+               {
+                       // Ignore replacements for items with count >= 2
+                       i->remove(1);
+               }
+       }
+}
+
+// Dump an itemstring matrix
+static std::string craftDumpMatrix(const std::vector<std::string> &items,
+               unsigned int width)
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"{ ";
+       unsigned int x = 0;
+       for(std::vector<std::string>::const_iterator
+                       i = items.begin();
+                       i != items.end(); i++, x++)
+       {
+               if(x == width)
+               {
+                       os<<"; ";
+                       x = 0;
+               }
+               else if(x != 0)
+               {
+                       os<<",";
+               }
+               os<<"\""<<(*i)<<"\"";
+       }
+       os<<" }";
+       return os.str();
+}
+
+// Dump an item matrix
+std::string craftDumpMatrix(const std::vector<ItemStack> &items,
+               unsigned int width)
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"{ ";
+       unsigned int x = 0;
+       for(std::vector<ItemStack>::const_iterator
+                       i = items.begin();
+                       i != items.end(); i++, x++)
+       {
+               if(x == width)
+               {
+                       os<<"; ";
+                       x = 0;
+               }
+               else if(x != 0)
+               {
+                       os<<",";
                }
+               os<<"\""<<(i->getItemString())<<"\"";
        }
-       return CraftInput(cpi.width, items);
+       os<<" }";
+       return os.str();
 }
 
+
+/*
+       CraftInput
+*/
+
 std::string CraftInput::dump() const
 {
        std::ostringstream os(std::ios::binary);
-       os<<"(width="<<width<<"){";
-       for(u32 i=0; i<items.size(); i++)
-               os<<"\""<<items[i]<<"\",";
-       os<<"}";
+       os<<"(method="<<((int)method)<<", items="<<craftDumpMatrix(items, width)<<")";
        return os.str();
 }
 
-std::string CraftDefinition::dump() const
+/*
+       CraftOutput
+*/
+
+std::string CraftOutput::dump() const
 {
        std::ostringstream os(std::ios::binary);
-       os<<"{output=\""<<output<<"\", input={";
-       for(u32 i=0; i<input.items.size(); i++)
-               os<<"\""<<input.items[i]<<"\",";
-       os<<"}, (input.width="<<input.width<<")}";
+       os<<"(item=\""<<item<<"\", time="<<time<<")";
        return os.str();
 }
 
+/*
+       CraftReplacements
+*/
+std::string CraftReplacements::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"{";
+       const char *sep = "";
+       for(std::vector<std::pair<std::string, std::string> >::const_iterator
+                       i = pairs.begin();
+                       i != pairs.end(); i++)
+       {
+               os<<sep<<"\""<<(i->first)<<"\"=>\""<<(i->second)<<"\"";
+               sep = ",";
+       }
+       os<<"}";
+       return os.str();
+}
+
+
+/*
+       CraftDefinition
+*/
+
 void CraftDefinition::serialize(std::ostream &os) const
 {
-       writeU8(os, 0); // version
-       os<<serializeString(output);
-       writeU8(os, input.width);
-       writeU16(os, input.items.size());
-       for(u32 i=0; i<input.items.size(); i++)
-               os<<serializeString(input.items[i]);
+       writeU8(os, 1); // version
+       os<<serializeString(getName());
+       serializeBody(os);
 }
 
-void CraftDefinition::deSerialize(std::istream &is)
+CraftDefinition* CraftDefinition::deSerialize(std::istream &is)
 {
        int version = readU8(is);
-       if(version != 0) throw SerializationError(
+       if(version != 1) throw SerializationError(
                        "unsupported CraftDefinition version");
+       std::string name = deSerializeString(is);
+       CraftDefinition *def = NULL;
+       if(name == "shaped")
+       {
+               def = new CraftDefinitionShaped;
+       }
+       else if(name == "shapeless")
+       {
+               def = new CraftDefinitionShapeless;
+       }
+       else if(name == "toolrepair")
+       {
+               def = new CraftDefinitionToolRepair;
+       }
+       else if(name == "cooking")
+       {
+               def = new CraftDefinitionCooking;
+       }
+       else if(name == "fuel")
+       {
+               def = new CraftDefinitionFuel;
+       }
+       else
+       {
+               infostream<<"Unknown CraftDefinition name=\""<<name<<"\""<<std::endl;
+                throw SerializationError("Unknown CraftDefinition name");
+       }
+       def->deSerializeBody(is, version);
+       return def;
+}
+
+/*
+       CraftDefinitionShaped
+*/
+
+std::string CraftDefinitionShaped::getName() const
+{
+       return "shaped";
+}
+
+bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
+{
+       if(input.method != CRAFT_METHOD_NORMAL)
+               return false;
+
+       // Get input item matrix
+       std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
+       unsigned int inp_width = input.width;
+       if(inp_width == 0)
+               return false;
+       while(inp_names.size() % inp_width != 0)
+               inp_names.push_back("");
+
+       // Get input bounds
+       unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0;
+       if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
+               return false;  // it was empty
+
+       // Get recipe item matrix
+       std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
+       unsigned int rec_width = width;
+       if(rec_width == 0)
+               return false;
+       while(rec_names.size() % rec_width != 0)
+               rec_names.push_back("");
+
+       // Get recipe bounds
+       unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
+       if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y))
+               return false;  // it was empty
+
+       // Different sizes?
+       if(inp_max_x - inp_min_x != rec_max_x - rec_min_x)
+               return false;
+       if(inp_max_y - inp_min_y != rec_max_y - rec_min_y)
+               return false;
+
+       // Verify that all item names in the bounding box are equal
+       unsigned int w = inp_max_x - inp_min_x + 1;
+       unsigned int h = inp_max_y - inp_min_y + 1;
+       for(unsigned int y=0; y<h; y++)
+       for(unsigned int x=0; x<w; x++)
+       {
+               unsigned int inp_x = inp_min_x + x;
+               unsigned int inp_y = inp_min_y + y;
+               unsigned int rec_x = rec_min_x + x;
+               unsigned int rec_y = rec_min_y + y;
+
+               if(
+                       inp_names[inp_y * inp_width + inp_x] !=
+                       rec_names[rec_y * rec_width + rec_x]
+               ){
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
+{
+       return CraftOutput(output, 0);
+}
+
+void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const
+{
+       craftDecrementOrReplaceInput(input, replacements, gamedef);
+}
+
+std::string CraftDefinitionShaped::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"(shaped, output=\""<<output
+               <<"\", recipe="<<craftDumpMatrix(recipe, width)
+               <<", replacements="<<replacements.dump()<<")";
+       return os.str();
+}
+
+void CraftDefinitionShaped::serializeBody(std::ostream &os) const
+{
+       os<<serializeString(output);
+       writeU16(os, width);
+       writeU16(os, recipe.size());
+       for(u32 i=0; i<recipe.size(); i++)
+               os<<serializeString(recipe[i]);
+       writeU16(os, replacements.pairs.size());
+       for(u32 i=0; i<replacements.pairs.size(); i++)
+       {
+               os<<serializeString(replacements.pairs[i].first);
+               os<<serializeString(replacements.pairs[i].second);
+       }
+}
+
+void CraftDefinitionShaped::deSerializeBody(std::istream &is, int version)
+{
+       if(version != 1) throw SerializationError(
+                       "unsupported CraftDefinitionShaped version");
+       output = deSerializeString(is);
+       width = readU16(is);
+       recipe.clear();
+       u32 count = readU16(is);
+       for(u32 i=0; i<count; i++)
+               recipe.push_back(deSerializeString(is));
+       replacements.pairs.clear();
+       count = readU16(is);
+       for(u32 i=0; i<count; i++)
+       {
+               std::string first = deSerializeString(is);
+               std::string second = deSerializeString(is);
+               replacements.pairs.push_back(std::make_pair(first, second));
+       }
+}
+
+/*
+       CraftDefinitionShapeless
+*/
+
+std::string CraftDefinitionShapeless::getName() const
+{
+       return "shapeless";
+}
+
+bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
+{
+       if(input.method != CRAFT_METHOD_NORMAL)
+               return false;
+
+       // Get input item multiset
+       std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
+       std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
+
+       // Get recipe item multiset
+       std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
+       std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names);
+
+       // Recipe is matched when the multisets coincide
+       return inp_names_multiset == rec_names_multiset;
+}
+
+CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
+{
+       return CraftOutput(output, 0);
+}
+
+void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const
+{
+       craftDecrementOrReplaceInput(input, replacements, gamedef);
+}
+
+std::string CraftDefinitionShapeless::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"(shapeless, output=\""<<output
+               <<"\", recipe="<<craftDumpMatrix(recipe, recipe.size())
+               <<", replacements="<<replacements.dump()<<")";
+       return os.str();
+}
+
+void CraftDefinitionShapeless::serializeBody(std::ostream &os) const
+{
+       os<<serializeString(output);
+       writeU16(os, recipe.size());
+       for(u32 i=0; i<recipe.size(); i++)
+               os<<serializeString(recipe[i]);
+       writeU16(os, replacements.pairs.size());
+       for(u32 i=0; i<replacements.pairs.size(); i++)
+       {
+               os<<serializeString(replacements.pairs[i].first);
+               os<<serializeString(replacements.pairs[i].second);
+       }
+}
+
+void CraftDefinitionShapeless::deSerializeBody(std::istream &is, int version)
+{
+       if(version != 1) throw SerializationError(
+                       "unsupported CraftDefinitionShapeless version");
        output = deSerializeString(is);
-       input.width = readU8(is);
+       recipe.clear();
        u32 count = readU16(is);
        for(u32 i=0; i<count; i++)
-               input.items.push_back(deSerializeString(is));
+               recipe.push_back(deSerializeString(is));
+       replacements.pairs.clear();
+       count = readU16(is);
+       for(u32 i=0; i<count; i++)
+       {
+               std::string first = deSerializeString(is);
+               std::string second = deSerializeString(is);
+               replacements.pairs.push_back(std::make_pair(first, second));
+       }
+}
+
+/*
+       CraftDefinitionToolRepair
+*/
+
+static ItemStack craftToolRepair(
+               const ItemStack &item1,
+               const ItemStack &item2,
+               float additional_wear,
+               IGameDef *gamedef)
+{
+       IItemDefManager *idef = gamedef->idef();
+       if(item1.count != 1 || item2.count != 1 || item1.name != item2.name
+                       || idef->get(item1.name).type != ITEM_TOOL
+                       || idef->get(item2.name).type != ITEM_TOOL)
+       {
+               // Failure
+               return ItemStack();
+       }
+
+       s32 item1_uses = 65536 - (u32) item1.wear;
+       s32 item2_uses = 65536 - (u32) item2.wear;
+       s32 new_uses = item1_uses + item2_uses;
+       s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
+       if(new_wear >= 65536)
+               return ItemStack();
+       if(new_wear < 0)
+               new_wear = 0;
+
+       ItemStack repaired = item1;
+       repaired.wear = new_wear;
+       return repaired;
+}
+
+std::string CraftDefinitionToolRepair::getName() const
+{
+       return "toolrepair";
+}
+
+bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
+{
+       if(input.method != CRAFT_METHOD_NORMAL)
+               return false;
+
+       ItemStack item1;
+       ItemStack item2;
+       for(std::vector<ItemStack>::const_iterator
+                       i = input.items.begin();
+                       i != input.items.end(); i++)
+       {
+               if(!i->empty())
+               {
+                       if(item1.empty())
+                               item1 = *i;
+                       else if(item2.empty())
+                               item2 = *i;
+                       else
+                               return false;
+               }
+       }
+       ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
+       return !repaired.empty();
+}
+
+CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
+{
+       ItemStack item1;
+       ItemStack item2;
+       for(std::vector<ItemStack>::const_iterator
+                       i = input.items.begin();
+                       i != input.items.end(); i++)
+       {
+               if(!i->empty())
+               {
+                       if(item1.empty())
+                               item1 = *i;
+                       else if(item2.empty())
+                               item2 = *i;
+               }
+       }
+       ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
+       return CraftOutput(repaired.getItemString(), 0);
+}
+
+void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const
+{
+       craftDecrementInput(input, gamedef);
+}
+
+std::string CraftDefinitionToolRepair::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"(toolrepair, additional_wear="<<additional_wear<<")";
+       return os.str();
+}
+
+void CraftDefinitionToolRepair::serializeBody(std::ostream &os) const
+{
+       writeF1000(os, additional_wear);
+}
+
+void CraftDefinitionToolRepair::deSerializeBody(std::istream &is, int version)
+{
+       if(version != 1) throw SerializationError(
+                       "unsupported CraftDefinitionToolRepair version");
+       additional_wear = readF1000(is);
+}
+
+/*
+       CraftDefinitionCooking
+*/
+
+std::string CraftDefinitionCooking::getName() const
+{
+       return "cooking";
 }
 
+bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
+{
+       if(input.method != CRAFT_METHOD_COOKING)
+               return false;
+
+       // Get input item multiset
+       std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
+       std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
+
+       // Get recipe item multiset
+       std::multiset<std::string> rec_names_multiset;
+       rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
+
+       // Recipe is matched when the multisets coincide
+       return inp_names_multiset == rec_names_multiset;
+}
+
+CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
+{
+       return CraftOutput(output, cooktime);
+}
+
+void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const
+{
+       craftDecrementInput(input, gamedef);
+}
+
+std::string CraftDefinitionCooking::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"(cooking, output=\""<<output
+               <<"\", recipe=\""<<recipe
+               <<"\", cooktime="<<cooktime<<")";
+       return os.str();
+}
+
+void CraftDefinitionCooking::serializeBody(std::ostream &os) const
+{
+       os<<serializeString(output);
+       os<<serializeString(recipe);
+       writeF1000(os, cooktime);
+}
+
+void CraftDefinitionCooking::deSerializeBody(std::istream &is, int version)
+{
+       if(version != 1) throw SerializationError(
+                       "unsupported CraftDefinitionCooking version");
+       output = deSerializeString(is);
+       recipe = deSerializeString(is);
+       cooktime = readF1000(is);
+}
+
+/*
+       CraftDefinitionFuel
+*/
+
+std::string CraftDefinitionFuel::getName() const
+{
+       return "fuel";
+}
+
+bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
+{
+       if(input.method != CRAFT_METHOD_FUEL)
+               return false;
+
+       // Get input item multiset
+       std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
+       std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
+
+       // Get recipe item multiset
+       std::multiset<std::string> rec_names_multiset;
+       rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
+
+       // Recipe is matched when the multisets coincide
+       return inp_names_multiset == rec_names_multiset;
+}
+
+CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
+{
+       return CraftOutput("", burntime);
+}
+
+void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const
+{
+       craftDecrementInput(input, gamedef);
+}
+
+std::string CraftDefinitionFuel::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"(fuel, recipe=\""<<recipe
+               <<"\", burntime="<<burntime<<")";
+       return os.str();
+}
+
+void CraftDefinitionFuel::serializeBody(std::ostream &os) const
+{
+       os<<serializeString(recipe);
+       writeF1000(os, burntime);
+}
+
+void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
+{
+       if(version != 1) throw SerializationError(
+                       "unsupported CraftDefinitionFuel version");
+       recipe = deSerializeString(is);
+       burntime = readF1000(is);
+}
+
+/*
+       Craft definition manager
+*/
+
 class CCraftDefManager: public IWritableCraftDefManager
 {
 public:
@@ -111,58 +761,46 @@ public:
        {
                clear();
        }
-       virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi,
-                       IGameDef *gamedef) const
+       virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
+                       bool decrementInput, IGameDef *gamedef) const
        {
-               if(input_cpi.width > 3){
-                       errorstream<<"getCraftResult(): ERROR: "
-                                       <<"input_cpi.width > 3; Failing to craft."<<std::endl;
-                       return NULL;
-               }
-               InventoryItem *input_items[9];
-               for(u32 y=0; y<3; y++)
-               for(u32 x=0; x<3; x++)
+               output.item = "";
+               output.time = 0;
+
+               // If all input items are empty, abort.
+               bool all_empty = true;
+               for(std::vector<ItemStack>::const_iterator
+                               i = input.items.begin();
+                               i != input.items.end(); i++)
                {
-                       u32 i=y*3+x;
-                       if(x >= input_cpi.width || y >= input_cpi.height())
-                               input_items[i] = NULL;
-                       else
-                               input_items[i] = input_cpi.items[y*input_cpi.width+x];
+                       if(!i->empty())
+                       {
+                               all_empty = false;
+                               break;
+                       }
                }
-               for(core::list<CraftDefinition*>::ConstIterator
-                               i = m_craft_definitions.begin();
-                               i != m_craft_definitions.end(); i++)
+               if(all_empty)
+                       return false;
+
+               // Walk crafting definitions from back to front, so that later
+               // definitions can override earlier ones.
+               for(std::vector<CraftDefinition*>::const_reverse_iterator
+                               i = m_craft_definitions.rbegin();
+                               i != m_craft_definitions.rend(); i++)
                {
                        CraftDefinition *def = *i;
 
-                       /*infostream<<"Checking "<<createInput(input_cpi).dump()<<std::endl
-                                       <<" against "<<def->input.dump()
-                                       <<" (output=\""<<def->output<<"\")"<<std::endl;*/
+                       /*infostream<<"Checking "<<input.dump()<<std::endl
+                                       <<" against "<<def->dump()<<std::endl;*/
 
                        try {
-                               CraftPointerInput spec_cpi = createPointerInput(def->input, gamedef);
-                               if(spec_cpi.width > 3){
-                                       errorstream<<"getCraftResult: ERROR: "
-                                                       <<"spec_cpi.width > 3 in recipe "
-                                                       <<def->dump()<<std::endl;
-                                       continue;
-                               }
-                               InventoryItem *spec_items[9];
-                               for(u32 y=0; y<3; y++)
-                               for(u32 x=0; x<3; x++)
+                               if(def->check(input, gamedef))
                                {
-                                       u32 i=y*3+x;
-                                       if(x >= spec_cpi.width || y >= spec_cpi.height())
-                                               spec_items[i] = NULL;
-                                       else
-                                               spec_items[i] = spec_cpi.items[y*spec_cpi.width+x];
-                               }
-
-                               bool match = checkItemCombination(input_items, spec_items);
-
-                               if(match){
-                                       std::istringstream iss(def->output, std::ios::binary);
-                                       return InventoryItem::deSerialize(iss, gamedef);
+                                       // Get output, then decrement input (if requested)
+                                       output = def->getOutput(input, gamedef);
+                                       if(decrementInput)
+                                               def->decrementInput(input, gamedef);
+                                       return true;
                                }
                        }
                        catch(SerializationError &e)
@@ -173,34 +811,41 @@ public:
                                // then go on with the next craft definition
                        }
                }
-               return NULL;
+               return false;
        }
-       virtual void registerCraft(const CraftDefinition &def)
+       virtual std::string dump() const
        {
-               infostream<<"registerCraft: registering craft definition: "
-                               <<def.dump()<<std::endl;
-               if(def.input.width > 3 || def.input.height() > 3){
-                       errorstream<<"registerCraft: input size is larger than 3x3,"
-                                       <<" ignoring"<<std::endl;
-                       return;
+               std::ostringstream os(std::ios::binary);
+               os<<"Crafting definitions:\n";
+               for(std::vector<CraftDefinition*>::const_iterator
+                               i = m_craft_definitions.begin();
+                               i != m_craft_definitions.end(); i++)
+               {
+                       os<<(*i)->dump()<<"\n";
                }
-               m_craft_definitions.push_back(new CraftDefinition(def));
+               return os.str();
+       }
+       virtual void registerCraft(CraftDefinition *def)
+       {
+               infostream<<"registerCraft: registering craft definition: "
+                               <<def->dump()<<std::endl;
+               m_craft_definitions.push_back(def);
        }
        virtual void clear()
        {
-               for(core::list<CraftDefinition*>::Iterator
+               for(std::vector<CraftDefinition*>::iterator
                                i = m_craft_definitions.begin();
                                i != m_craft_definitions.end(); i++){
                        delete *i;
                }
                m_craft_definitions.clear();
        }
-       virtual void serialize(std::ostream &os)
+       virtual void serialize(std::ostream &os) const
        {
                writeU8(os, 0); // version
                u16 count = m_craft_definitions.size();
                writeU16(os, count);
-               for(core::list<CraftDefinition*>::Iterator
+               for(std::vector<CraftDefinition*>::const_iterator
                                i = m_craft_definitions.begin();
                                i != m_craft_definitions.end(); i++){
                        CraftDefinition *def = *i;
@@ -222,14 +867,13 @@ public:
                for(u16 i=0; i<count; i++){
                        // Deserialize a string and grab a CraftDefinition from it
                        std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
-                       CraftDefinition def;
-                       def.deSerialize(tmp_is);
+                       CraftDefinition *def = CraftDefinition::deSerialize(tmp_is);
                        // Register
                        registerCraft(def);
                }
        }
 private:
-       core::list<CraftDefinition*> m_craft_definitions;
+       std::vector<CraftDefinition*> m_craft_definitions;
 };
 
 IWritableCraftDefManager* createCraftDefManager()
index cfd58ad10d0a5a68cd3e0698959c4a3fea2fd0fc..57f26f049ce4fd3e995ce6da9233335deb53ca07 100644 (file)
@@ -23,71 +23,328 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <string>
 #include <iostream>
 #include <vector>
-class IGameDef;
-class InventoryItem;
+#include <utility>
+#include "gamedef.h"
+#include "inventory.h"
 
-struct CraftPointerInput
+/*
+       Crafting methods.
+
+       The crafting method depends on the inventory list
+       that the crafting input comes from.
+*/
+enum CraftMethod
+{
+       // Crafting grid
+       CRAFT_METHOD_NORMAL,
+       // Cooking something in a furnace
+       CRAFT_METHOD_COOKING,
+       // Using something as fuel for a furnace
+       CRAFT_METHOD_FUEL,
+};
+
+/*
+       Input: The contents of the crafting slots, arranged in matrix form
+*/
+struct CraftInput
 {
+       CraftMethod method;
        unsigned int width;
-       std::vector<InventoryItem*> items;
+       std::vector<ItemStack> items;
 
-       CraftPointerInput(unsigned int width_, const std::vector<InventoryItem*> &items_):
-               width(width_),
-               items(items_)
+       CraftInput():
+               method(CRAFT_METHOD_NORMAL), width(0), items()
        {}
-       CraftPointerInput():
-               width(0)
+       CraftInput(CraftMethod method_, unsigned int width_,
+                       const std::vector<ItemStack> &items_):
+               method(method_), width(width_), items(items_)
        {}
-       ~CraftPointerInput();
-       unsigned int height() const{
-               return (items.size() + width - 1) / width;
-       }
+       std::string dump() const;
 };
 
-struct CraftInput
+/*
+       Output: Result of crafting operation
+*/
+struct CraftOutput
 {
-       unsigned int width;
-       std::vector<std::string> items;
+       // Used for normal crafting and cooking, itemstring
+       std::string item;
+       // Used for cooking (cook time) and fuel (burn time), seconds
+       float time;
 
-       CraftInput(unsigned int width_, const std::vector<std::string> &items_):
-               width(width_),
-               items(items_)
+       CraftOutput():
+               item(""), time(0)
        {}
-       CraftInput():
-               width(0)
+       CraftOutput(std::string item_, float time_):
+               item(item_), time(time_)
        {}
-       unsigned int height() const{
-               return (items.size() + width - 1) / width;
-       }
        std::string dump() const;
 };
 
-struct CraftDefinition
+/*
+       A list of replacements. A replacement indicates that a specific
+       input item should not be deleted (when crafting) but replaced with
+       a different item. Each replacements is a pair (itemstring to remove,
+       itemstring to replace with)
+
+       Example: If ("bucket:bucket_water", "bucket:bucket_empty") is a
+       replacement pair, the crafting input slot that contained a water
+       bucket will contain an empty bucket after crafting.
+
+       Note: replacements only work correctly when stack_max of the item
+       to be replaced is 1. It is up to the mod writer to ensure this.
+*/
+struct CraftReplacements
 {
-       std::string output;
-       CraftInput input;
+       // List of replacements
+       std::vector<std::pair<std::string, std::string> > pairs;
 
-       CraftDefinition(){}
-       CraftDefinition(const std::string &output_, unsigned int width_,
-                       const std::vector<std::string> &input_):
-               output(output_),
-               input(width_, input_)
+       CraftReplacements():
+               pairs()
+       {}
+       CraftReplacements(std::vector<std::pair<std::string, std::string> > pairs_):
+               pairs(pairs_)
        {}
-       
        std::string dump() const;
+};
+
+/*
+       Crafting definition base class
+*/
+class CraftDefinition
+{
+public:
+       CraftDefinition(){}
+       virtual ~CraftDefinition(){}
+
        void serialize(std::ostream &os) const;
-       void deSerialize(std::istream &is);
+       static CraftDefinition* deSerialize(std::istream &is);
+
+       // Returns type of crafting definition
+       virtual std::string getName() const=0;
+
+       // Checks whether the recipe is applicable
+       virtual bool check(const CraftInput &input, IGameDef *gamedef) const=0;
+       // Returns the output structure, meaning depends on crafting method
+       // The implementation can assume that check(input) returns true
+       virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const=0;
+       // Decreases count of every input item
+       virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const=0;
+
+       virtual std::string dump() const=0;
+
+protected:
+       virtual void serializeBody(std::ostream &os) const=0;
+       virtual void deSerializeBody(std::istream &is, int version)=0;
+};
+
+/*
+       A plain-jane (shaped) crafting definition
+
+       Supported crafting method: CRAFT_METHOD_NORMAL.
+       Requires the input items to be arranged exactly like in the recipe.
+*/
+class CraftDefinitionShaped: public CraftDefinition
+{
+public:
+       CraftDefinitionShaped():
+               output(""), width(1), recipe(), replacements()
+       {}
+       CraftDefinitionShaped(
+                       const std::string &output_,
+                       unsigned int width_,
+                       const std::vector<std::string> &recipe_,
+                       const CraftReplacements &replacements_):
+               output(output_), width(width_), recipe(recipe_), replacements(replacements_)
+       {}
+       virtual ~CraftDefinitionShaped(){}
+
+       virtual std::string getName() const;
+       virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
+       virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
+       virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+
+       virtual std::string dump() const;
+
+protected:
+       virtual void serializeBody(std::ostream &os) const;
+       virtual void deSerializeBody(std::istream &is, int version);
+
+private:
+       // Output itemstring
+       std::string output;
+       // Width of recipe
+       unsigned int width;
+       // Recipe matrix (itemstrings)
+       std::vector<std::string> recipe;
+       // Replacement items for decrementInput()
+       CraftReplacements replacements;
+};
+
+/*
+       A shapeless crafting definition
+       Supported crafting method: CRAFT_METHOD_NORMAL.
+       Input items can arranged in any way.
+*/
+class CraftDefinitionShapeless: public CraftDefinition
+{
+public:
+       CraftDefinitionShapeless():
+               output(""), recipe(), replacements()
+       {}
+       CraftDefinitionShapeless(
+                       const std::string &output_,
+                       const std::vector<std::string> &recipe_,
+                       const CraftReplacements &replacements_):
+               output(output_), recipe(recipe_), replacements(replacements_)
+       {}
+       virtual ~CraftDefinitionShapeless(){}
+
+       virtual std::string getName() const;
+       virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
+       virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
+       virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+
+       virtual std::string dump() const;
+
+protected:
+       virtual void serializeBody(std::ostream &os) const;
+       virtual void deSerializeBody(std::istream &is, int version);
+
+private:
+       // Output itemstring
+       std::string output;
+       // Recipe list (itemstrings)
+       std::vector<std::string> recipe;
+       // Replacement items for decrementInput()
+       CraftReplacements replacements;
+};
+
+/*
+       Tool repair crafting definition
+       Supported crafting method: CRAFT_METHOD_NORMAL.
+       Put two damaged tools into the crafting grid, get one tool back.
+       There should only be one crafting definition of this type.
+*/
+class CraftDefinitionToolRepair: public CraftDefinition
+{
+public:
+       CraftDefinitionToolRepair():
+               additional_wear(0)
+       {}
+       CraftDefinitionToolRepair(float additional_wear_):
+               additional_wear(additional_wear_)
+       {}
+       virtual ~CraftDefinitionToolRepair(){}
+
+       virtual std::string getName() const;
+       virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
+       virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
+       virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+
+       virtual std::string dump() const;
+
+protected:
+       virtual void serializeBody(std::ostream &os) const;
+       virtual void deSerializeBody(std::istream &is, int version);
+
+private:
+       // This is a constant that is added to the wear of the result.
+       // May be positive or negative, allowed range [-1,1].
+       // 1 = new tool is completely broken
+       // 0 = simply add remaining uses of both input tools
+       // -1 = new tool is completely pristine
+       float additional_wear;
+};
+
+/*
+       A cooking (in furnace) definition
+       Supported crafting method: CRAFT_METHOD_COOKING.
+*/
+class CraftDefinitionCooking: public CraftDefinition
+{
+public:
+       CraftDefinitionCooking():
+               output(""), recipe(""), cooktime()
+       {}
+       CraftDefinitionCooking(
+                       const std::string &output_,
+                       const std::string &recipe_,
+                       float cooktime_):
+               output(output_), recipe(recipe_), cooktime(cooktime_)
+       {}
+       virtual ~CraftDefinitionCooking(){}
+
+       virtual std::string getName() const;
+       virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
+       virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
+       virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+
+       virtual std::string dump() const;
+
+protected:
+       virtual void serializeBody(std::ostream &os) const;
+       virtual void deSerializeBody(std::istream &is, int version);
+
+private:
+       // Output itemstring
+       std::string output;
+       // Recipe itemstring
+       std::string recipe;
+       // Time in seconds
+       float cooktime;
 };
 
+/*
+       A fuel (for furnace) definition
+       Supported crafting method: CRAFT_METHOD_FUEL.
+*/
+class CraftDefinitionFuel: public CraftDefinition
+{
+public:
+       CraftDefinitionFuel():
+               recipe(""), burntime()
+       {}
+       CraftDefinitionFuel(std::string recipe_, float burntime_):
+               recipe(recipe_), burntime(burntime_)
+       {}
+       virtual ~CraftDefinitionFuel(){}
+
+       virtual std::string getName() const;
+       virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
+       virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
+       virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+
+       virtual std::string dump() const;
+
+protected:
+       virtual void serializeBody(std::ostream &os) const;
+       virtual void deSerializeBody(std::istream &is, int version);
+
+private:
+       // Recipe itemstring
+       std::string recipe;
+       // Time in seconds
+       float burntime;
+};
+
+/*
+       Crafting definition manager
+*/
 class ICraftDefManager
 {
 public:
        ICraftDefManager(){}
        virtual ~ICraftDefManager(){}
-       virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi,
-                       IGameDef *gamedef) const=0;
+
+       // The main crafting function
+       virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
+                       bool decrementInput, IGameDef *gamedef) const=0;
        
-       virtual void serialize(std::ostream &os)=0;
+       // Print crafting recipes for debugging
+       virtual std::string dump() const=0;
+
+       virtual void serialize(std::ostream &os) const=0;
 };
 
 class IWritableCraftDefManager : public ICraftDefManager
@@ -95,13 +352,21 @@ class IWritableCraftDefManager : public ICraftDefManager
 public:
        IWritableCraftDefManager(){}
        virtual ~IWritableCraftDefManager(){}
-       virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi,
-                       IGameDef *gamedef) const=0;
-                       
-       virtual void registerCraft(const CraftDefinition &def)=0;
+
+       // The main crafting function
+       virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
+                       bool decrementInput, IGameDef *gamedef) const=0;
+
+       // Print crafting recipes for debugging
+       virtual std::string dump() const=0;
+
+       // Add a crafting definition.
+       // After calling this, the pointer belongs to the manager.
+       virtual void registerCraft(CraftDefinition *def)=0;
+       // Delete all crafting definitions
        virtual void clear()=0;
 
-       virtual void serialize(std::ostream &os)=0;
+       virtual void serialize(std::ostream &os) const=0;
        virtual void deSerialize(std::istream &is)=0;
 };
 
diff --git a/src/craftitemdef.cpp b/src/craftitemdef.cpp
deleted file mode 100644 (file)
index 4461e38..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
-Minetest-c55
-Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2011 Kahrl <kahrl@gmx.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 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 General Public License for more details.
-
-You should have received a copy of the GNU 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.
-*/
-
-#include "craftitemdef.h"
-#include "irrlichttypes.h"
-#include "log.h"
-#include <sstream>
-#include "utility.h"
-#include <map>
-
-CraftItemDefinition::CraftItemDefinition():
-       imagename(""),
-       cookresult_item(""),
-       furnace_cooktime(3.0),
-       furnace_burntime(-1.0),
-       usable(false),
-       liquids_pointable(false),
-       dropcount(-1),
-       stack_max(99)
-{}
-
-std::string CraftItemDefinition::dump()
-{
-       std::ostringstream os(std::ios::binary);
-       os<<"imagename="<<imagename;
-       os<<", cookresult_item="<<cookresult_item;
-       os<<", furnace_cooktime="<<furnace_cooktime;
-       os<<", furnace_burntime="<<furnace_burntime;
-       os<<", usable="<<usable;
-       os<<", liquids_pointable="<<liquids_pointable;
-       os<<", dropcount="<<dropcount;
-       os<<", stack_max="<<stack_max;
-       return os.str();
-}
-
-void CraftItemDefinition::serialize(std::ostream &os)
-{
-       writeU8(os, 0); // version
-       os<<serializeString(imagename);
-       os<<serializeString(cookresult_item);
-       writeF1000(os, furnace_cooktime);
-       writeF1000(os, furnace_burntime);
-       writeU8(os, usable);
-       writeU8(os, liquids_pointable);
-       writeS16(os, dropcount);
-       writeS16(os, stack_max);
-}
-
-void CraftItemDefinition::deSerialize(std::istream &is)
-{
-       int version = readU8(is);
-       if(version != 0) throw SerializationError(
-                       "unsupported CraftItemDefinition version");
-       imagename = deSerializeString(is);
-       cookresult_item = deSerializeString(is);
-       furnace_cooktime = readF1000(is);
-       furnace_burntime = readF1000(is);
-       usable = readU8(is);
-       liquids_pointable = readU8(is);
-       dropcount = readS16(is);
-       stack_max = readS16(is);
-}
-
-class CCraftItemDefManager: public IWritableCraftItemDefManager
-{
-public:
-       virtual ~CCraftItemDefManager()
-       {
-               clear();
-       }
-       virtual const CraftItemDefinition* getCraftItemDefinition(const std::string &itemname_) const
-       {
-               // Convert name according to possible alias
-               std::string itemname = getAlias(itemname_);
-               // Get the definition
-               core::map<std::string, CraftItemDefinition*>::Node *n;
-               n = m_item_definitions.find(itemname);
-               if(n == NULL)
-                       return NULL;
-               return n->getValue();
-       }
-       virtual std::string getImagename(const std::string &itemname) const
-       {
-               const CraftItemDefinition *def = getCraftItemDefinition(itemname);
-               if(def == NULL)
-                       return "";
-               return def->imagename;
-       }
-       virtual std::string getAlias(const std::string &name) const
-       {
-               std::map<std::string, std::string>::const_iterator i;
-               i = m_aliases.find(name);
-               if(i != m_aliases.end())
-                       return i->second;
-               return name;
-       }
-       virtual bool registerCraftItem(std::string itemname, const CraftItemDefinition &def)
-       {
-               infostream<<"registerCraftItem: registering CraftItem \""<<itemname<<"\""<<std::endl;
-               m_item_definitions[itemname] = new CraftItemDefinition(def);
-
-               // Remove conflicting alias if it exists
-               bool alias_removed = (m_aliases.erase(itemname) != 0);
-               if(alias_removed)
-                       infostream<<"cidef: erased alias "<<itemname
-                                       <<" because item was defined"<<std::endl;
-               
-               return true;
-       }
-       virtual void clear()
-       {
-               for(core::map<std::string, CraftItemDefinition*>::Iterator
-                               i = m_item_definitions.getIterator();
-                               i.atEnd() == false; i++){
-                       delete i.getNode()->getValue();
-               }
-               m_item_definitions.clear();
-               m_aliases.clear();
-       }
-       virtual void setAlias(const std::string &name,
-                       const std::string &convert_to)
-       {
-               if(getCraftItemDefinition(name) != NULL){
-                       infostream<<"nidef: not setting alias "<<name<<" -> "<<convert_to
-                                       <<": "<<name<<" is already defined"<<std::endl;
-                       return;
-               }
-               infostream<<"nidef: setting alias "<<name<<" -> "<<convert_to
-                               <<std::endl;
-               m_aliases[name] = convert_to;
-       }
-       virtual void serialize(std::ostream &os)
-       {
-               writeU8(os, 0); // version
-               u16 count = m_item_definitions.size();
-               writeU16(os, count);
-               for(core::map<std::string, CraftItemDefinition*>::Iterator
-                               i = m_item_definitions.getIterator();
-                               i.atEnd() == false; i++){
-                       std::string name = i.getNode()->getKey();
-                       CraftItemDefinition *def = i.getNode()->getValue();
-                       // Serialize name
-                       os<<serializeString(name);
-                       // Serialize CraftItemDefinition and write wrapped in a string
-                       std::ostringstream tmp_os(std::ios::binary);
-                       def->serialize(tmp_os);
-                       os<<serializeString(tmp_os.str());
-               }
-
-               writeU16(os, m_aliases.size());
-               for(std::map<std::string, std::string>::const_iterator
-                               i = m_aliases.begin(); i != m_aliases.end(); i++)
-               {
-                       os<<serializeString(i->first);
-                       os<<serializeString(i->second);
-               }
-       }
-       virtual void deSerialize(std::istream &is)
-       {
-               // Clear everything
-               clear();
-               // Deserialize
-               int version = readU8(is);
-               if(version != 0) throw SerializationError(
-                               "unsupported CraftItemDefManager version");
-               u16 count = readU16(is);
-               for(u16 i=0; i<count; i++){
-                       // Deserialize name
-                       std::string name = deSerializeString(is);
-                       // Deserialize a string and grab a CraftItemDefinition from it
-                       std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
-                       CraftItemDefinition def;
-                       def.deSerialize(tmp_is);
-                       // Register
-                       registerCraftItem(name, def);
-               }
-
-               u16 num_aliases = readU16(is);
-               if(!is.eof()){
-                       for(u16 i=0; i<num_aliases; i++){
-                               std::string name = deSerializeString(is);
-                               std::string convert_to = deSerializeString(is);
-                               m_aliases[name] = convert_to;
-                       }
-               }
-       }
-private:
-       // Key is name
-       core::map<std::string, CraftItemDefinition*> m_item_definitions;
-       // Aliases
-       std::map<std::string, std::string> m_aliases;
-};
-
-IWritableCraftItemDefManager* createCraftItemDefManager()
-{
-       return new CCraftItemDefManager();
-}
diff --git a/src/craftitemdef.h b/src/craftitemdef.h
deleted file mode 100644 (file)
index b5d4b93..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-Minetest-c55
-Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2011 Kahrl <kahrl@gmx.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 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 General Public License for more details.
-
-You should have received a copy of the GNU 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.
-*/
-
-#ifndef CRAFTITEMDEF_HEADER
-#define CRAFTITEMDEF_HEADER
-
-#include "common_irrlicht.h"
-#include <string>
-#include <iostream>
-
-struct CraftItemDefinition
-{
-       std::string imagename;
-       std::string cookresult_item;
-       float furnace_cooktime;
-       float furnace_burntime;
-       bool usable;
-       bool liquids_pointable;
-       s16 dropcount;
-       s16 stack_max;
-
-       CraftItemDefinition();
-       std::string dump();
-       void serialize(std::ostream &os);
-       void deSerialize(std::istream &is);
-};
-
-class ICraftItemDefManager
-{
-public:
-       ICraftItemDefManager(){}
-       virtual ~ICraftItemDefManager(){}
-       virtual const CraftItemDefinition* getCraftItemDefinition(const std::string &itemname) const=0;
-       virtual std::string getImagename(const std::string &itemname) const =0;
-       virtual std::string getAlias(const std::string &name) const =0;
-
-       virtual void serialize(std::ostream &os)=0;
-};
-
-class IWritableCraftItemDefManager : public ICraftItemDefManager
-{
-public:
-       IWritableCraftItemDefManager(){}
-       virtual ~IWritableCraftItemDefManager(){}
-       virtual const CraftItemDefinition* getCraftItemDefinition(const std::string &itemname) const=0;
-       virtual std::string getImagename(const std::string &itemname) const =0;
-
-       virtual bool registerCraftItem(std::string itemname, const CraftItemDefinition &def)=0;
-       virtual void clear()=0;
-       // Set an alias so that entries named <name> will load as <convert_to>.
-       // Alias is not set if <name> has already been defined.
-       // Alias will be removed if <name> is defined at a later point of time.
-       virtual void setAlias(const std::string &name,
-                       const std::string &convert_to)=0;
-
-       virtual void serialize(std::ostream &os)=0;
-       virtual void deSerialize(std::istream &is)=0;
-};
-
-IWritableCraftItemDefManager* createCraftItemDefManager();
-
-#endif
index beb49885c558bed51858189a5daba427fbb089a3..89c9fd67640a63771d1e955661bc4f26235f6e3b 100644 (file)
@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "activeobject.h"
 
 class Server;
+class ServerEnvironment;
 class ActiveBlockModifier;
 class ServerActiveObject;
 typedef struct lua_State lua_State;
index 0d08074ad93b5152513d2f7be1d81683d0644485..7bd5d958736f2ee3b969c42578a9c5fb0ee70761 100644 (file)
@@ -40,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "profiler.h"
 #include "mainmenumanager.h"
-#include "craftitemdef.h"
 #include "gettext.h"
 #include "log.h"
 #include "filesys.h"
@@ -48,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodedef.h"
 #include "nodemetadata.h"
 #include "main.h" // For g_settings
-#include "tooldef.h"
+#include "itemdef.h"
 #include "tile.h" // For TextureSource
 #include "logoutputbuffer.h"
 
@@ -79,15 +78,6 @@ struct ChatLine
        std::wstring text;
 };
 
-/*
-       Inventory stuff
-*/
-
-// Inventory actions from the menu are buffered here before sending
-Queue<InventoryAction*> inventory_action_queue;
-// This is a copy of the inventory that the client's environment has
-Inventory local_inventory;
-
 /*
        Text input system
 */
@@ -156,7 +146,7 @@ private:
        Hotbar draw routine
 */
 void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
-               ITextureSource *tsrc,
+               IGameDef *gamedef,
                v2s32 centerlowerpos, s32 imgsize, s32 itemcount,
                Inventory *inventory, s32 halfheartcount, u16 playeritem)
 {
@@ -184,7 +174,7 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
 
        for(s32 i=0; i<itemcount; i++)
        {
-               InventoryItem *item = mainlist->getItem(i);
+               const ItemStack &item = mainlist->getItem(i);
                
                core::rect<s32> rect = imgrect + pos
                                + v2s32(padding+i*(imgsize+padding*2), padding);
@@ -245,17 +235,14 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font,
 
                video::SColor bgcolor2(128,0,0,0);
                driver->draw2DRectangle(bgcolor2, rect, NULL);
-
-               if(item != NULL)
-               {
-                       drawInventoryItem(driver, font, item, rect, NULL, tsrc);
-               }
+               drawItemStack(driver, font, item, rect, NULL, gamedef);
        }
        
        /*
                Draw hearts
        */
-       video::ITexture *heart_texture = tsrc->getTextureRaw("heart.png");
+       video::ITexture *heart_texture =
+               gamedef->getTextureSource()->getTextureRaw("heart.png");
        if(heart_texture)
        {
                v2s32 p = pos + v2s32(0, -20);
@@ -691,12 +678,10 @@ void the_game(
        IWritableTextureSource *tsrc = createTextureSource(device);
        
        // These will be filled by data received from the server
-       // Create tool definition manager
-       IWritableToolDefManager *tooldef = createToolDefManager();
+       // Create item definition manager
+       IWritableItemDefManager *itemdef = createItemDefManager();
        // Create node definition manager
        IWritableNodeDefManager *nodedef = createNodeDefManager();
-       // Create CraftItem definition manager
-       IWritableCraftItemDefManager *craftitemdef = createCraftItemDefManager();
 
        // Add chat log output for errors to be shown in chat
        LogOutputBuffer chat_log_error_buf(LMT_ERROR);
@@ -725,7 +710,7 @@ void the_game(
        MapDrawControl draw_control;
 
        Client client(device, playername.c_str(), password, draw_control,
-                       tsrc, tooldef, nodedef, craftitemdef);
+                       tsrc, itemdef, nodedef);
        
        // Client acts as our GameDef
        IGameDef *gamedef = &client;
@@ -835,9 +820,8 @@ void the_game(
                        
                        // End condition
                        if(client.texturesReceived() &&
-                                       client.tooldefReceived() &&
-                                       client.nodedefReceived() &&
-                                       client.craftitemdefReceived()){
+                                       client.itemdefReceived() &&
+                                       client.nodedefReceived()){
                                got_content = true;
                                break;
                        }
@@ -853,12 +837,10 @@ void the_game(
                        ss<<(int)(timeout - time_counter + 1.0);
                        ss<<L" seconds)\n";
 
-                       ss<<(client.tooldefReceived()?L"[X]":L"[  ]");
-                       ss<<L" Tool definitions\n";
+                       ss<<(client.itemdefReceived()?L"[X]":L"[  ]");
+                       ss<<L" Item definitions\n";
                        ss<<(client.nodedefReceived()?L"[X]":L"[  ]");
                        ss<<L" Node definitions\n";
-                       ss<<(client.craftitemdefReceived()?L"[X]":L"[  ]");
-                       ss<<L" Item definitions\n";
                        //ss<<(client.texturesReceived()?L"[X]":L"[  ]");
                        ss<<L"["<<(int)(client.textureReceiveProgress()*100+0.5)<<L"%] ";
                        ss<<L" Textures\n";
@@ -871,6 +853,12 @@ void the_game(
                }
        }
 
+       /*
+               After all content has been received:
+               Update cached textures, meshes and materials
+       */
+       client.afterContentReceived();
+
        /*
                Create skybox
        */
@@ -910,6 +898,11 @@ void the_game(
                farmesh = new FarMesh(smgr->getRootSceneNode(), smgr, -1, client.getMapSeed(), &client);
        }
 
+       /*
+               A copy of the local inventory
+       */
+       Inventory local_inventory(itemdef);
+
        /*
                Move into game
        */
@@ -1289,7 +1282,7 @@ void the_game(
                        // drop selected item
                        IDropAction *a = new IDropAction();
                        a->count = 0;
-                       a->from_inv = "current_player";
+                       a->from_inv.setCurrentPlayer();
                        a->from_list = "main";
                        a->from_i = client.getPlayerItem();
                        client.inventoryAction(a);
@@ -1302,18 +1295,20 @@ void the_game(
                        GUIInventoryMenu *menu =
                                new GUIInventoryMenu(guienv, guiroot, -1,
                                        &g_menumgr, v2s16(8,7),
-                                       client.getInventoryContext(),
-                                       &client, tsrc);
+                                       &client, gamedef);
+
+                       InventoryLocation inventoryloc;
+                       inventoryloc.setCurrentPlayer();
 
                        core::array<GUIInventoryMenu::DrawSpec> draw_spec;
                        draw_spec.push_back(GUIInventoryMenu::DrawSpec(
-                                       "list", "current_player", "main",
+                                       "list", inventoryloc, "main",
                                        v2s32(0, 3), v2s32(8, 4)));
                        draw_spec.push_back(GUIInventoryMenu::DrawSpec(
-                                       "list", "current_player", "craft",
+                                       "list", inventoryloc, "craft",
                                        v2s32(3, 0), v2s32(3, 3)));
                        draw_spec.push_back(GUIInventoryMenu::DrawSpec(
-                                       "list", "current_player", "craftresult",
+                                       "list", inventoryloc, "craftresult",
                                        v2s32(7, 1), v2s32(1, 1)));
 
                        menu->setDrawSpec(draw_spec);
@@ -1691,31 +1686,20 @@ void the_game(
 
                /*
                        For interaction purposes, get info about the held item
-                       - Is it a tool, and what is the toolname?
+                       - What item is it?
                        - Is it a usable item?
                        - Can it point to liquids?
                */
-               std::string playeritem_toolname = "";
+               ItemStack playeritem;
                bool playeritem_usable = false;
                bool playeritem_liquids_pointable = false;
                {
                        InventoryList *mlist = local_inventory.getList("main");
                        if(mlist != NULL)
                        {
-                               InventoryItem *item = mlist->getItem(client.getPlayerItem());
-                               if(item)
-                               {
-                                       if((std::string)item->getName() == "ToolItem")
-                                       {
-                                               ToolItem *titem = (ToolItem*)item;
-                                               playeritem_toolname = titem->getToolName();
-                                       }
-
-                                       playeritem_usable = item->isUsable();
-
-                                       playeritem_liquids_pointable =
-                                               item->areLiquidsPointable();
-                               }
+                               playeritem = mlist->getItem(client.getPlayerItem());
+                               playeritem_usable = playeritem.getDefinition(itemdef).usable;
+                               playeritem_liquids_pointable = playeritem.getDefinition(itemdef).liquids_pointable;
                        }
                }
 
@@ -1845,7 +1829,7 @@ void the_game(
                                // Get digging properties for material and tool
                                content_t material = n.getContent();
                                ToolDiggingProperties tp =
-                                               tooldef->getDiggingProperties(playeritem_toolname);
+                                               playeritem.getToolDiggingProperties(itemdef);
                                DiggingProperties prop =
                                                getDiggingProperties(material, &tp, nodedef);
 
@@ -1853,9 +1837,6 @@ void the_game(
 
                                if(prop.diggable == false)
                                {
-                                       /*infostream<<"Material "<<(int)material
-                                                       <<" not diggable with \""
-                                                       <<playeritem_toolname<<"\""<<std::endl;*/
                                        // I guess nobody will wait for this long
                                        dig_time_complete = 10000000.0;
                                }
@@ -1922,17 +1903,11 @@ void the_game(
                                if(meta && meta->getInventoryDrawSpecString() != "" && !random_input)
                                {
                                        infostream<<"Launching custom inventory view"<<std::endl;
-                                       /*
-                                               Construct the unique identification string of the node
-                                       */
-                                       std::string current_name;
-                                       current_name += "nodemeta:";
-                                       current_name += itos(nodepos.X);
-                                       current_name += ",";
-                                       current_name += itos(nodepos.Y);
-                                       current_name += ",";
-                                       current_name += itos(nodepos.Z);
+
+                                       InventoryLocation inventoryloc;
+                                       inventoryloc.setNodeMeta(nodepos);
                                        
+
                                        /*
                                                Create menu
                                        */
@@ -1942,13 +1917,12 @@ void the_game(
                                                GUIInventoryMenu::makeDrawSpecArrayFromString(
                                                        draw_spec,
                                                        meta->getInventoryDrawSpecString(),
-                                                       current_name);
+                                                       inventoryloc);
 
                                        GUIInventoryMenu *menu =
                                                new GUIInventoryMenu(guienv, guiroot, -1,
                                                        &g_menumgr, invsize,
-                                                       client.getInventoryContext(),
-                                                       &client, tsrc);
+                                                       &client, gamedef);
                                        menu->setDrawSpec(draw_spec);
                                        menu->drop();
                                }
@@ -2001,7 +1975,7 @@ void the_game(
                                        v3f objpos = selected_object->getPosition();
                                        v3f dir = (objpos - player_position).normalize();
 
-                                       bool disable_send = selected_object->directReportPunch(playeritem_toolname, dir);
+                                       bool disable_send = selected_object->directReportPunch(playeritem.name, dir);
                                        if(!disable_send)
                                                client.interact(0, pointed);
                                }
@@ -2285,24 +2259,12 @@ void the_game(
                        update_wielded_item_trigger = false;
                        // Update wielded tool
                        InventoryList *mlist = local_inventory.getList("main");
-                       InventoryItem *item = NULL;
+                       ItemStack item;
                        if(mlist != NULL)
                                item = mlist->getItem(client.getPlayerItem());
                        camera.wield(item, gamedef);
                }
                
-               /*
-                       Send actions returned by the inventory menu
-               */
-               while(inventory_action_queue.size() != 0)
-               {
-                       InventoryAction *a = inventory_action_queue.pop_front();
-
-                       client.sendInventoryAction(a);
-                       // Eat it
-                       delete a;
-               }
-
                /*
                        Drawing begins
                */
@@ -2411,7 +2373,7 @@ void the_game(
                        Draw hotbar
                */
                {
-                       draw_hotbar(driver, font, tsrc,
+                       draw_hotbar(driver, font, gamedef,
                                        v2s32(displaycenter.X, screensize.Y),
                                        hotbar_imagesize, hotbar_itemcount, &local_inventory,
                                        client.getHP(), client.getPlayerItem());
@@ -2482,9 +2444,9 @@ void the_game(
 
        } // Client scope (must be destructed before destructing *def and tsrc
 
-       delete tooldef;
        delete tsrc;
        delete nodedef;
+       delete itemdef;
 }
 
 
index c450568b7eda139fdafd414e42ed154ff16cf877..8df6988ad2a8a866924df46cb422021de6823fb4 100644 (file)
@@ -21,11 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define GAMEDEF_HEADER
 
 #include <string>
+#include "irrlichttypes.h"
 
-class IToolDefManager;
+class IItemDefManager;
 class INodeDefManager;
 class ICraftDefManager;
-class ICraftItemDefManager;
 // Mineral too?
 class ITextureSource;
 
@@ -39,10 +39,9 @@ class IGameDef
 public:
        // These are thread-safe IF they are not edited while running threads.
        // Thus, first they are set up and then they are only read.
-       virtual IToolDefManager* getToolDefManager()=0;
+       virtual IItemDefManager* getItemDefManager()=0;
        virtual INodeDefManager* getNodeDefManager()=0;
        virtual ICraftDefManager* getCraftDefManager()=0;
-       virtual ICraftItemDefManager* getCraftItemDefManager()=0;
 
        // This is always thread-safe, but referencing the irrlicht texture
        // pointers in other threads than main thread will make things explode.
@@ -52,10 +51,9 @@ public:
        virtual u16 allocateUnknownNodeId(const std::string &name)=0;
 
        // Shorthands
-       IToolDefManager* tdef(){return getToolDefManager();}
+       IItemDefManager* idef(){return getItemDefManager();}
        INodeDefManager* ndef(){return getNodeDefManager();}
        ICraftDefManager* cdef(){return getCraftDefManager();}
-       ICraftItemDefManager* cidef(){return getCraftItemDefManager();}
        ITextureSource* tsrc(){return getTextureSource();}
 };
 
index 552e10db2940acd66ee9397f27f36f5f2a0c7caf..9b2aed3774256f421153961dfe03dde5d60a4f6f 100644 (file)
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "guiInventoryMenu.h"
 #include "constants.h"
+#include "gamedef.h"
 #include "keycode.h"
 #include "strfnd.h"
 #include <IGUICheckBox.h>
@@ -28,20 +29,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <IGUIStaticText.h>
 #include <IGUIFont.h>
 #include "log.h"
-#include "inventorymanager.h"
 
-void drawInventoryItem(video::IVideoDriver *driver,
+void drawItemStack(video::IVideoDriver *driver,
                gui::IGUIFont *font,
-               InventoryItem *item, core::rect<s32> rect,
+               const ItemStack &item,
+               const core::rect<s32> &rect,
                const core::rect<s32> *clip,
-               ITextureSource *tsrc)
+               IGameDef *gamedef)
 {
-       if(item == NULL)
+       if(item.empty())
                return;
        
-       video::ITexture *texture = NULL;
-       texture = item->getImage();
+       const ItemDefinition &def = item.getDefinition(gamedef->idef());
+       video::ITexture *texture = def.inventory_texture;
 
+       // Draw the inventory texture
        if(texture != NULL)
        {
                const video::SColor color(255,255,255,255);
@@ -51,34 +53,59 @@ void drawInventoryItem(video::IVideoDriver *driver,
                        core::dimension2di(texture->getOriginalSize())),
                        clip, colors, true);
        }
-       else
+
+       if(def.type == ITEM_TOOL && item.wear != 0)
        {
-               video::SColor bgcolor(255,50,50,128);
-               driver->draw2DRectangle(bgcolor, rect, clip);
+               // Draw a progressbar
+               float barheight = rect.getHeight()/16;
+               float barpad_x = rect.getWidth()/16;
+               float barpad_y = rect.getHeight()/16;
+               core::rect<s32> progressrect(
+                       rect.UpperLeftCorner.X + barpad_x,
+                       rect.LowerRightCorner.Y - barpad_y - barheight,
+                       rect.LowerRightCorner.X - barpad_x,
+                       rect.LowerRightCorner.Y - barpad_y);
+
+               // Shrink progressrect by amount of tool damage
+               float wear = item.wear / 65535.0;
+               progressrect.LowerRightCorner.X =
+                       wear * progressrect.UpperLeftCorner.X +
+                       (1-wear) * progressrect.LowerRightCorner.X;
+
+               // Compute progressbar color
+               //   wear = 0.0: green
+               //   wear = 0.5: yellow
+               //   wear = 1.0: red
+               video::SColor color(255,255,255,255);
+               int wear_i = floor(wear * 511);
+               wear_i = MYMIN(wear_i + 10, 511);
+               if(wear_i <= 255)
+                       color.set(255, wear_i, 255, 0);
+               else
+                       color.set(255, 255, 511-wear_i, 0);
+
+               driver->draw2DRectangle(color, progressrect, clip);
        }
 
-       if(font != NULL)
+       if(font != NULL && item.count >= 2)
        {
-               std::string text = item->getText();
-               if(font && text != "")
-               {
-                       v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
-                       v2s32 sdim(dim.X,dim.Y);
-
-                       core::rect<s32> rect2(
-                               /*rect.UpperLeftCorner,
-                               core::dimension2d<u32>(rect.getWidth(), 15)*/
-                               rect.LowerRightCorner - sdim,
-                               sdim
-                       );
-
-                       video::SColor bgcolor(128,0,0,0);
-                       driver->draw2DRectangle(bgcolor, rect2, clip);
-                       
-                       font->draw(text.c_str(), rect2,
-                                       video::SColor(255,255,255,255), false, false,
-                                       clip);
-               }
+               // Get the item count as a string
+               std::string text = itos(item.count);
+               v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
+               v2s32 sdim(dim.X,dim.Y);
+
+               core::rect<s32> rect2(
+                       /*rect.UpperLeftCorner,
+                       core::dimension2d<u32>(rect.getWidth(), 15)*/
+                       rect.LowerRightCorner - sdim,
+                       sdim
+               );
+
+               video::SColor bgcolor(128,0,0,0);
+               driver->draw2DRectangle(bgcolor, rect2, clip);
+
+               video::SColor color(255,255,255,255);
+               font->draw(text.c_str(), rect2, color, false, false, clip);
        }
 }
 
@@ -90,15 +117,13 @@ GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
                gui::IGUIElement* parent, s32 id,
                IMenuManager *menumgr,
                v2s16 menu_size,
-               InventoryContext *c,
                InventoryManager *invmgr,
-               ITextureSource *tsrc
+               IGameDef *gamedef
                ):
        GUIModalMenu(env, parent, id, menumgr),
        m_menu_size(menu_size),
-       m_c(c),
        m_invmgr(invmgr),
-       m_tsrc(tsrc)
+       m_gamedef(gamedef)
 {
        m_selected_item = NULL;
 }
@@ -214,15 +239,15 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
                        core::rect<s32> rect = imgrect + s.pos + p0;
                        if(rect.isPointInside(p))
                        {
-                               return ItemSpec(s.inventoryname, s.listname, i);
+                               return ItemSpec(s.inventoryloc, s.listname, i);
                        }
                }
        }
 
-       return ItemSpec("", "", -1);
+       return ItemSpec(InventoryLocation(), "", -1);
 }
 
-void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc)
+void GUIInventoryMenu::drawList(const ListDrawSpec &s)
 {
        video::IVideoDriver* driver = Environment->getVideoDriver();
 
@@ -232,7 +257,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc)
        if (skin)
                font = skin->getFont();
        
-       Inventory *inv = m_invmgr->getInventory(m_c, s.inventoryname);
+       Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
        assert(inv);
        InventoryList *ilist = inv->getList(s.listname);
        
@@ -244,7 +269,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc)
                s32 y = (i/s.geom.X) * spacing.Y;
                v2s32 p(x,y);
                core::rect<s32> rect = imgrect + s.pos + p;
-               InventoryItem *item = NULL;
+               ItemStack item;
                if(ilist)
                        item = ilist->getItem(i);
 
@@ -278,10 +303,10 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc)
                    driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
                }
 
-               if(item)
+               if(!item.empty())
                {
-                       drawInventoryItem(driver, font, item,
-                                       rect, &AbsoluteClippingRect, tsrc);
+                       drawItemStack(driver, font, item,
+                                       rect, &AbsoluteClippingRect, m_gamedef);
                }
 
        }
@@ -303,8 +328,7 @@ void GUIInventoryMenu::drawMenu()
        
        for(u32 i=0; i<m_draw_spec.size(); i++)
        {
-               ListDrawSpec &s = m_draw_spec[i];
-               drawList(s, m_tsrc);
+               drawList(m_draw_spec[i]);
        }
 
        /*
@@ -352,14 +376,14 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
                        //infostream<<"Mouse action at p=("<<p.X<<","<<p.Y<<")"<<std::endl;
                        if(s.isValid())
                        {
-                               infostream<<"Mouse action on "<<s.inventoryname
+                               infostream<<"Mouse action on "<<s.inventoryloc.dump()
                                                <<"/"<<s.listname<<" "<<s.i<<std::endl;
                                if(m_selected_item)
                                {
-                                       Inventory *inv_from = m_invmgr->getInventory(m_c,
-                                                       m_selected_item->inventoryname);
-                                       Inventory *inv_to = m_invmgr->getInventory(m_c,
-                                                       s.inventoryname);
+                                       Inventory *inv_from = m_invmgr->getInventory(
+                                                       m_selected_item->inventoryloc);
+                                       Inventory *inv_to = m_invmgr->getInventory(
+                                                       s.inventoryloc);
                                        assert(inv_from);
                                        assert(inv_to);
                                        InventoryList *list_from =
@@ -373,21 +397,21 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
                                        // Indicates whether source slot completely empties
                                        bool source_empties = false;
                                        if(list_from && list_to
-                                                       && list_from->getItem(m_selected_item->i) != NULL)
+                                                       && !list_from->getItem(m_selected_item->i).empty())
                                        {
                                                infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
                                                IMoveAction *a = new IMoveAction();
                                                a->count = amount;
-                                               a->from_inv = m_selected_item->inventoryname;
+                                               a->from_inv = m_selected_item->inventoryloc;
                                                a->from_list = m_selected_item->listname;
                                                a->from_i = m_selected_item->i;
-                                               a->to_inv = s.inventoryname;
+                                               a->to_inv = s.inventoryloc;
                                                a->to_list = s.listname;
                                                a->to_i = s.i;
                                                //ispec.actions->push_back(a);
                                                m_invmgr->inventoryAction(a);
                                                
-                                               if(list_from->getItem(m_selected_item->i)->getCount()<=amount)
+                                               if(list_from->getItem(m_selected_item->i).count<=amount)
                                                        source_empties = true;
                                        }
                                        // Remove selection if target was left-clicked or source
@@ -401,13 +425,13 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
                                else
                                {
                                        /*
-                                               Select if non-NULL
+                                               Select if nonempty
                                        */
-                                       Inventory *inv = m_invmgr->getInventory(m_c,
-                                                       s.inventoryname);
+                                       Inventory *inv = m_invmgr->getInventory(
+                                                       s.inventoryloc);
                                        assert(inv);
                                        InventoryList *list = inv->getList(s.listname);
-                                       if(list->getItem(s.i) != NULL)
+                                       if(!list->getItem(s.i).empty())
                                        {
                                                m_selected_item = new ItemSpec(s);
                                        }
@@ -489,7 +513,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
 v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString(
                core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
                const std::string &data,
-               const std::string &current_name)
+               const InventoryLocation &current_location)
 {
        v2s16 invsize(8,9);
        Strfnd f(data);
@@ -500,8 +524,11 @@ v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString(
                if(type == "list")
                {
                        std::string name = f.next(";");
+                       InventoryLocation loc;
                        if(name == "current_name")
-                               name = current_name;
+                               loc = current_location;
+                       else
+                               loc.deSerialize(name);
                        std::string subname = f.next(";");
                        s32 pos_x = stoi(f.next(","));
                        s32 pos_y = stoi(f.next(";"));
@@ -512,7 +539,7 @@ v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString(
                                        <<", geom=("<<geom_x<<","<<geom_y<<")"
                                        <<std::endl;
                        draw_spec.push_back(GUIInventoryMenu::DrawSpec(
-                                       type, name, subname,
+                                       type, loc, subname,
                                        v2s32(pos_x,pos_y),v2s32(geom_x,geom_y)));
                        f.next("]");
                }
index 359268687ad7239f08b2549439afbe36210c9e57..c3b3e5a646037c3b36aa50dd731b0cd349809631 100644 (file)
@@ -23,18 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "common_irrlicht.h"
 #include "inventory.h"
+#include "inventorymanager.h"
 #include "utility.h"
 #include "modalMenu.h"
 
-class ITextureSource;
-class InventoryContext;
+class IGameDef;
 class InventoryManager;
 
-void drawInventoryItem(video::IVideoDriver *driver,
+void drawItemStack(video::IVideoDriver *driver,
                gui::IGUIFont *font,
-               InventoryItem *item, core::rect<s32> rect,
+               const ItemStack &item,
+               const core::rect<s32> &rect,
                const core::rect<s32> *clip,
-               ITextureSource *tsrc);
+               IGameDef *gamedef);
 
 class GUIInventoryMenu : public GUIModalMenu
 {
@@ -44,11 +45,11 @@ class GUIInventoryMenu : public GUIModalMenu
                {
                        i = -1;
                }
-               ItemSpec(const std::string &a_inventoryname,
+               ItemSpec(const InventoryLocation &a_inventoryloc,
                                const std::string &a_listname,
                                s32 a_i)
                {
-                       inventoryname = a_inventoryname;
+                       inventoryloc = a_inventoryloc;
                        listname = a_listname;
                        i = a_i;
                }
@@ -57,7 +58,7 @@ class GUIInventoryMenu : public GUIModalMenu
                        return i != -1;
                }
 
-               std::string inventoryname;
+               InventoryLocation inventoryloc;
                std::string listname;
                s32 i;
        };
@@ -67,17 +68,17 @@ class GUIInventoryMenu : public GUIModalMenu
                ListDrawSpec()
                {
                }
-               ListDrawSpec(const std::string &a_inventoryname,
+               ListDrawSpec(const InventoryLocation &a_inventoryloc,
                                const std::string &a_listname,
                                v2s32 a_pos, v2s32 a_geom)
                {
-                       inventoryname = a_inventoryname;
+                       inventoryloc = a_inventoryloc;
                        listname = a_listname;
                        pos = a_pos;
                        geom = a_geom;
                }
 
-               std::string inventoryname;
+               InventoryLocation inventoryloc;
                std::string listname;
                v2s32 pos;
                v2s32 geom;
@@ -89,7 +90,7 @@ public:
                {
                }
                DrawSpec(const std::string &a_type,
-                               const std::string &a_name,
+                               const InventoryLocation &a_name,
                                const std::string &a_subname,
                                v2s32 a_pos,
                                v2s32 a_geom)
@@ -102,7 +103,7 @@ public:
                }
 
                std::string type;
-               std::string name;
+               InventoryLocation name;
                std::string subname;
                v2s32 pos;
                v2s32 geom;
@@ -112,15 +113,14 @@ public:
        static v2s16 makeDrawSpecArrayFromString(
                        core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
                        const std::string &data,
-                       const std::string &current_name);
+                       const InventoryLocation &current_location);
 
        GUIInventoryMenu(gui::IGUIEnvironment* env,
                        gui::IGUIElement* parent, s32 id,
                        IMenuManager *menumgr,
                        v2s16 menu_size,
-                       InventoryContext *c,
                        InventoryManager *invmgr,
-                       ITextureSource *tsrc
+                       IGameDef *gamedef
                        );
        ~GUIInventoryMenu();
 
@@ -136,7 +136,7 @@ public:
        void regenerateGui(v2u32 screensize);
        
        ItemSpec getItemAtPos(v2s32 p) const;
-       void drawList(const ListDrawSpec &s, ITextureSource *tsrc);
+       void drawList(const ListDrawSpec &s);
        void drawMenu();
 
        bool OnEvent(const SEvent& event);
@@ -153,9 +153,8 @@ protected:
        v2s32 spacing;
        v2s32 imgsize;
        
-       InventoryContext *m_c;
        InventoryManager *m_invmgr;
-       ITextureSource *m_tsrc;
+       IGameDef *m_gamedef;
 
        core::array<DrawSpec> m_init_draw_spec;
        core::array<ListDrawSpec> m_draw_spec;
index 0d38bed78bae09d4664e87e6c089c2974010ac5f..ebd0b9c23b471bb579e8aaf27c5083c6e99fb4a6 100644 (file)
@@ -22,545 +22,429 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "utility.h"
 #include "debug.h"
 #include <sstream>
-#include "main.h" // For tsrc, g_toolmanager
-#include "serverobject.h"
-#include "content_mapnode.h"
-#include "content_sao.h"
-#include "environment.h"
-#include "mapblock.h"
-#include "player.h"
 #include "log.h"
-#include "nodedef.h"
-#include "tooldef.h"
-#include "craftitemdef.h"
-#include "gamedef.h"
-#include "scriptapi.h"
+#include "itemdef.h"
 #include "strfnd.h"
+#include "content_mapnode.h" // For loading legacy MaterialItems
 #include "nameidmapping.h" // For loading legacy MaterialItems
 
 /*
-       InventoryItem
+       ItemStack
 */
 
-InventoryItem::InventoryItem(IGameDef *gamedef, u16 count):
-       m_gamedef(gamedef),
-       m_count(count)
+static content_t content_translate_from_19_to_internal(content_t c_from)
 {
-       assert(m_gamedef);
+       for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
+       {
+               if(trans_table_19[i][1] == c_from)
+               {
+                       return trans_table_19[i][0];
+               }
+       }
+       return c_from;
 }
 
-InventoryItem::~InventoryItem()
+// If the string contains spaces, quotes or control characters, encodes as JSON.
+// Else returns the string unmodified.
+static std::string serializeJsonStringIfNeeded(const std::string &s)
 {
+       for(size_t i = 0; i < s.size(); ++i)
+       {
+               if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
+                       return serializeJsonString(s);
+       }
+       return s;
 }
 
-content_t content_translate_from_19_to_internal(content_t c_from)
+// Parses a string serialized by serializeJsonStringIfNeeded.
+static std::string deSerializeJsonStringIfNeeded(std::istream &is)
 {
-       for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
+       std::ostringstream tmp_os;
+       bool expect_initial_quote = true;
+       bool is_json = false;
+       bool was_backslash = false;
+       for(;;)
        {
-               if(trans_table_19[i][1] == c_from)
+               char c = is.get();
+               if(is.eof())
+                       break;
+               if(expect_initial_quote && c == '"')
                {
-                       return trans_table_19[i][0];
+                       tmp_os << c;
+                       is_json = true;
+               }
+               else if(is_json)
+               {
+                       tmp_os << c;
+                       if(was_backslash)
+                               was_backslash = false;
+                       else if(c == '\\')
+                               was_backslash = true;
+                       else if(c == '"')
+                               break; // Found end of string
+               }
+               else
+               {
+                       if(c == ' ')
+                       {
+                               // Found end of word
+                               is.unget();
+                               break;
+                       }
+                       else
+                       {
+                               tmp_os << c;
+                       }
                }
+               expect_initial_quote = false;
        }
-       return c_from;
+       if(is_json)
+       {
+               std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
+               return deSerializeJsonString(tmp_is);
+       }
+       else
+               return tmp_os.str();
+}
+
+
+ItemStack::ItemStack(std::string name_, u16 count_,
+               u16 wear_, std::string metadata_,
+               IItemDefManager *itemdef)
+{
+       name = itemdef->getAlias(name_);
+       count = count_;
+       wear = wear_;
+       metadata = metadata_;
+
+       if(name.empty() || count == 0)
+               clear();
+       else if(itemdef->get(name).type == ITEM_TOOL)
+               count = 1;
 }
 
-InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef)
+void ItemStack::serialize(std::ostream &os) const
 {
        DSTACK(__FUNCTION_NAME);
 
-       //is.imbue(std::locale("C"));
+       if(empty())
+               return;
+
+       // Check how many parts of the itemstring are needed
+       int parts = 1;
+       if(count != 1)
+               parts = 2;
+       if(wear != 0)
+               parts = 3;
+       if(metadata != "")
+               parts = 4;
+
+       os<<serializeJsonStringIfNeeded(name);
+       if(parts >= 2)
+               os<<" "<<count;
+       if(parts >= 3)
+               os<<" "<<wear;
+       if(parts >= 4)
+               os<<" "<<serializeJsonStringIfNeeded(metadata);
+}
+
+void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       clear();
+
        // Read name
-       std::string name;
-       std::getline(is, name, ' ');
+       name = deSerializeJsonStringIfNeeded(is);
+
+       // Skip space
+       std::string tmp;
+       std::getline(is, tmp, ' ');
+       if(!tmp.empty())
+               throw SerializationError("Unexpected text after item name");
        
        if(name == "MaterialItem")
        {
-               // u16 reads directly as a number (u8 doesn't)
+               // Obsoleted on 2011-07-30
+
                u16 material;
                is>>material;
-               u16 count;
-               is>>count;
+               u16 materialcount;
+               is>>materialcount;
                // Convert old materials
                if(material <= 0xff)
                        material = content_translate_from_19_to_internal(material);
                if(material > MAX_CONTENT)
                        throw SerializationError("Too large material number");
-               return new MaterialItem(gamedef, material, count);
+               // Convert old id to name
+               NameIdMapping legacy_nimap;
+               content_mapnode_get_name_id_mapping(&legacy_nimap);
+               legacy_nimap.getName(material, name);
+               if(name == "")
+                       name = "unknown_block";
+               name = itemdef->getAlias(name);
+               count = materialcount;
        }
        else if(name == "MaterialItem2")
        {
+               // Obsoleted on 2011-11-16
+
                u16 material;
                is>>material;
-               u16 count;
-               is>>count;
+               u16 materialcount;
+               is>>materialcount;
                if(material > MAX_CONTENT)
                        throw SerializationError("Too large material number");
-               return new MaterialItem(gamedef, material, count);
+               // Convert old id to name
+               NameIdMapping legacy_nimap;
+               content_mapnode_get_name_id_mapping(&legacy_nimap);
+               legacy_nimap.getName(material, name);
+               if(name == "")
+                       name = "unknown_block";
+               name = itemdef->getAlias(name);
+               count = materialcount;
        }
-       else if(name == "node" || name == "NodeItem" || name == "MaterialItem3")
+       else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
+                       || name == "craft" || name == "CraftItem")
        {
+               // Obsoleted on 2012-01-07
+
                std::string all;
                std::getline(is, all, '\n');
-               std::string nodename;
                // First attempt to read inside ""
                Strfnd fnd(all);
                fnd.next("\"");
                // If didn't skip to end, we have ""s
                if(!fnd.atend()){
-                       nodename = fnd.next("\"");
+                       name = fnd.next("\"");
                } else { // No luck, just read a word then
                        fnd.start(all);
-                       nodename = fnd.next(" ");
+                       name = fnd.next(" ");
                }
                fnd.skip_over(" ");
-               u16 count = stoi(trim(fnd.next("")));
+               name = itemdef->getAlias(name);
+               count = stoi(trim(fnd.next("")));
                if(count == 0)
                        count = 1;
-               return new MaterialItem(gamedef, nodename, count);
        }
        else if(name == "MBOItem")
        {
-               std::string inventorystring;
-               std::getline(is, inventorystring, '|');
+               // Obsoleted on 2011-10-14
                throw SerializationError("MBOItem not supported anymore");
        }
-       else if(name == "craft" || name == "CraftItem")
-       {
-               std::string all;
-               std::getline(is, all, '\n');
-               std::string subname;
-               // First attempt to read inside ""
-               Strfnd fnd(all);
-               fnd.next("\"");
-               // If didn't skip to end, we have ""s
-               if(!fnd.atend()){
-                       subname = fnd.next("\"");
-               } else { // No luck, just read a word then
-                       fnd.start(all);
-                       subname = fnd.next(" ");
-               }
-               // Then read count
-               fnd.skip_over(" ");
-               u16 count = stoi(trim(fnd.next("")));
-               if(count == 0)
-                       count = 1;
-               return new CraftItem(gamedef, subname, count);
-       }
        else if(name == "tool" || name == "ToolItem")
        {
+               // Obsoleted on 2012-01-07
+
                std::string all;
                std::getline(is, all, '\n');
-               std::string toolname;
                // First attempt to read inside ""
                Strfnd fnd(all);
                fnd.next("\"");
                // If didn't skip to end, we have ""s
                if(!fnd.atend()){
-                       toolname = fnd.next("\"");
+                       name = fnd.next("\"");
                } else { // No luck, just read a word then
                        fnd.start(all);
-                       toolname = fnd.next(" ");
+                       name = fnd.next(" ");
                }
+               count = 1;
                // Then read wear
                fnd.skip_over(" ");
-               u16 wear = stoi(trim(fnd.next("")));
-               return new ToolItem(gamedef, toolname, wear);
+               wear = stoi(trim(fnd.next("")));
        }
        else
        {
-               infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
-               throw SerializationError("Unknown InventoryItem name");
-       }
-}
-
-InventoryItem* InventoryItem::deSerialize(const std::string &str,
-               IGameDef *gamedef)
-{
-       std::istringstream is(str, std::ios_base::binary);
-       return deSerialize(is, gamedef);
-}
-
-std::string InventoryItem::getItemString() {
-       // Get item string
-       std::ostringstream os(std::ios_base::binary);
-       serialize(os);
-       return os.str();
-}
-
-bool InventoryItem::dropOrPlace(ServerEnvironment *env,
-               ServerActiveObject *dropper,
-               v3f pos, bool place, s16 count)
-{
-       /*
-               Ensure that the block is loaded so that the item
-               can properly be added to the static list too
-       */
-       v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
-       MapBlock *block = env->getMap().emergeBlock(blockpos, false);
-       if(block==NULL)
-       {
-               infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: "
-                               <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
-                               <<std::endl;
-               return false;
-       }
-
-       /*
-               Take specified number of items,
-               but limit to getDropCount().
-       */
-       s16 dropcount = getDropCount();
-       if(count < 0 || count > dropcount)
-               count = dropcount;
-       if(count < 0 || count > getCount())
-               count = getCount();
-       if(count > 0)
-       {
-               /*
-                       Create an ItemSAO
-               */
-               pos.Y -= BS*0.25; // let it drop a bit
-               // Randomize a bit
-               //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-               //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-               // Create object
-               ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
-               // Add the object to the environment
-               env->addActiveObject(obj);
-               infostream<<"Dropped item"<<std::endl;
-
-               setCount(getCount() - count);
-       }
-
-       return getCount() < 1; // delete the item?
-}
-
-/*
-       MaterialItem
-*/
-
-MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
-       InventoryItem(gamedef, count)
-{
-       if(nodename == "")
-               nodename = "unknown_block";
-
-       // Convert directly to the correct name through aliases
-       m_nodename = gamedef->ndef()->getAlias(nodename);
-}
-// Legacy constructor
-MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
-       InventoryItem(gamedef, count)
-{
-       NameIdMapping legacy_nimap;
-       content_mapnode_get_name_id_mapping(&legacy_nimap);
-       std::string nodename;
-       legacy_nimap.getName(content, nodename);
-       if(nodename == "")
-               nodename = "unknown_block";
-       m_nodename = nodename;
-}
-
-#ifndef SERVER
-video::ITexture * MaterialItem::getImage() const
-{
-       return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
-}
-#endif
-
-bool MaterialItem::isCookable() const
-{
-       INodeDefManager *ndef = m_gamedef->ndef();
-       const ContentFeatures &f = ndef->get(m_nodename);
-       return (f.cookresult_item != "");
-}
-
-InventoryItem *MaterialItem::createCookResult() const
-{
-       INodeDefManager *ndef = m_gamedef->ndef();
-       const ContentFeatures &f = ndef->get(m_nodename);
-       std::istringstream is(f.cookresult_item, std::ios::binary);
-       return InventoryItem::deSerialize(is, m_gamedef);
-}
-
-float MaterialItem::getCookTime() const
-{
-       INodeDefManager *ndef = m_gamedef->ndef();
-       const ContentFeatures &f = ndef->get(m_nodename);
-       return f.furnace_cooktime;
-}
-
-float MaterialItem::getBurnTime() const
-{
-       INodeDefManager *ndef = m_gamedef->ndef();
-       const ContentFeatures &f = ndef->get(m_nodename);
-       return f.furnace_burntime;
-}
-
-content_t MaterialItem::getMaterial() const
-{
-       INodeDefManager *ndef = m_gamedef->ndef();
-       content_t id = CONTENT_IGNORE;
-       ndef->getId(m_nodename, id);
-       return id;
-}
-
-/*
-       ToolItem
-*/
-
-ToolItem::ToolItem(IGameDef *gamedef, std::string toolname, u16 wear):
-       InventoryItem(gamedef, 1)
-{
-       // Convert directly to the correct name through aliases
-       m_toolname = gamedef->tdef()->getAlias(toolname);
-       
-       m_wear = wear;
-}
-
-std::string ToolItem::getImageBasename() const
-{
-       return m_gamedef->getToolDefManager()->getImagename(m_toolname);
-}
-
-#ifndef SERVER
-video::ITexture * ToolItem::getImage() const
-{
-       ITextureSource *tsrc = m_gamedef->tsrc();
-
-       std::string basename = getImageBasename();
-       
-       /*
-               Calculate a progress value with sane amount of
-               maximum states
-       */
-       u32 maxprogress = 30;
-       u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
-       
-       float value_f = (float)toolprogress / (float)maxprogress;
-       std::ostringstream os;
-       os<<basename<<"^[progressbar"<<value_f;
+               do  // This loop is just to allow "break;"
+               {
+                       // The real thing
 
-       return tsrc->getTextureRaw(os.str());
-}
+                       // Apply item aliases
+                       name = itemdef->getAlias(name);
 
-video::ITexture * ToolItem::getImageRaw() const
-{
-       ITextureSource *tsrc = m_gamedef->tsrc();
-       
-       return tsrc->getTextureRaw(getImageBasename());
-}
-#endif
+                       // Read the count
+                       std::string count_str;
+                       std::getline(is, count_str, ' ');
+                       if(count_str.empty())
+                       {
+                               count = 1;
+                               break;
+                       }
+                       else
+                               count = stoi(count_str);
 
-bool ToolItem::isKnown() const
-{
-       IToolDefManager *tdef = m_gamedef->tdef();
-       const ToolDefinition *def = tdef->getToolDefinition(m_toolname);
-       return (def != NULL);
-}
+                       // Read the wear
+                       std::string wear_str;
+                       std::getline(is, wear_str, ' ');
+                       if(wear_str.empty())
+                               break;
+                       else
+                               wear = stoi(wear_str);
 
-/*
-       CraftItem
-*/
-
-CraftItem::CraftItem(IGameDef *gamedef, std::string subname, u16 count):
-       InventoryItem(gamedef, count)
-{
-       // Convert directly to the correct name through aliases
-       m_subname = gamedef->cidef()->getAlias(subname);
-}
+                       // Read metadata
+                       metadata = deSerializeJsonStringIfNeeded(is);
 
-#ifndef SERVER
-video::ITexture * CraftItem::getImage() const
-{
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       ITextureSource *tsrc = m_gamedef->tsrc();
-       std::string imagename = cidef->getImagename(m_subname);
-       return tsrc->getTextureRaw(imagename);
-}
-#endif
+                       // In case fields are added after metadata, skip space here:
+                       //std::getline(is, tmp, ' ');
+                       //if(!tmp.empty())
+                       //      throw SerializationError("Unexpected text after metadata");
 
-bool CraftItem::isKnown() const
-{
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       return (def != NULL);
-}
-
-u16 CraftItem::getStackMax() const
-{
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       if(def == NULL)
-               return InventoryItem::getStackMax();
-       return def->stack_max;
-}
-
-bool CraftItem::isUsable() const
-{
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       return def != NULL && def->usable;
-}
+               } while(false);
+       }
 
-bool CraftItem::isCookable() const
-{
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       return def != NULL && def->cookresult_item != "";
+       if(name.empty() || count == 0)
+               clear();
+       else if(itemdef->get(name).type == ITEM_TOOL)
+               count = 1;
 }
 
-InventoryItem *CraftItem::createCookResult() const
+void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
 {
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       if(def == NULL)
-               return InventoryItem::createCookResult();
-       std::istringstream is(def->cookresult_item, std::ios::binary);
-       return InventoryItem::deSerialize(is, m_gamedef);
+       std::istringstream is(str, std::ios::binary);
+       deSerialize(is, itemdef);
 }
 
-float CraftItem::getCookTime() const
+std::string ItemStack::getItemString() const
 {
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       if (def == NULL)
-               return InventoryItem::getCookTime();
-       return def->furnace_cooktime;
+       // Get item string
+       std::ostringstream os(std::ios::binary);
+       serialize(os);
+       return os.str();
 }
 
-float CraftItem::getBurnTime() const
+ItemStack ItemStack::addItem(const ItemStack &newitem_,
+               IItemDefManager *itemdef)
 {
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       if (def == NULL)
-               return InventoryItem::getBurnTime();
-       return def->furnace_burntime;
-}
+       ItemStack newitem = newitem_;
 
-s16 CraftItem::getDropCount() const
-{
-       // Special cases
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       if(def != NULL && def->dropcount >= 0)
-               return def->dropcount;
-       // Default
-       return InventoryItem::getDropCount();
-}
+       // If the item is empty or the position invalid, bail out
+       if(newitem.empty())
+       {
+               // nothing can be added trivially
+       }
+       // If this is an empty item, it's an easy job.
+       else if(empty())
+       {
+               *this = newitem;
+               newitem.clear();
+       }
+       // If item name differs, bail out
+       else if(name != newitem.name)
+       {
+               // cannot be added
+       }
+       // If the item fits fully, add counter and delete it
+       else if(newitem.count <= freeSpace(itemdef))
+       {
+               add(newitem.count);
+               newitem.clear();
+       }
+       // Else the item does not fit fully. Add all that fits and return
+       // the rest.
+       else
+       {
+               u16 freespace = freeSpace(itemdef);
+               add(freespace);
+               newitem.remove(freespace);
+       }
 
-bool CraftItem::areLiquidsPointable() const
-{
-       ICraftItemDefManager *cidef = m_gamedef->cidef();
-       const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname);
-       return def != NULL && def->liquids_pointable;
+       return newitem;
 }
 
-bool CraftItem::dropOrPlace(ServerEnvironment *env,
-               ServerActiveObject *dropper,
-               v3f pos, bool place, s16 count)
+bool ItemStack::itemFits(const ItemStack &newitem_,
+               ItemStack *restitem,
+               IItemDefManager *itemdef) const
 {
-       if(count == 0)
-               return false;
-
-       bool callback_exists = false;
-       bool result = false;
+       ItemStack newitem = newitem_;
 
-       if(place)
+       // If the item is empty or the position invalid, bail out
+       if(newitem.empty())
        {
-               result = scriptapi_craftitem_on_place_on_ground(
-                               env->getLua(),
-                               m_subname.c_str(), dropper, pos,
-                               callback_exists);
+               // nothing can be added trivially
        }
-
-       // note: on_drop is fallback for on_place_on_ground
-
-       if(!callback_exists)
+       // If this is an empty item, it's an easy job.
+       else if(empty())
        {
-               result = scriptapi_craftitem_on_drop(
-                               env->getLua(),
-                               m_subname.c_str(), dropper, pos,
-                               callback_exists);
+               newitem.clear();
        }
-
-       if(callback_exists)
+       // If item name differs, bail out
+       else if(name != newitem.name)
        {
-               // If the callback returned true, drop one item
-               if(result)
-                       setCount(getCount() - 1);
-               return getCount() < 1;
+               // cannot be added
        }
+       // If the item fits fully, delete it
+       else if(newitem.count <= freeSpace(itemdef))
+       {
+               newitem.clear();
+       }
+       // Else the item does not fit fully. Return the rest.
+       // the rest.
        else
        {
-               // If neither on_place_on_ground (if place==true)
-               // nor on_drop exists, call the base implementation
-               return InventoryItem::dropOrPlace(env, dropper, pos, place, count);
+               u16 freespace = freeSpace(itemdef);
+               newitem.remove(freespace);
        }
+
+       if(restitem)
+               *restitem = newitem;
+       return newitem.empty();
 }
 
-bool CraftItem::use(ServerEnvironment *env,
-               ServerActiveObject *user,
-               const PointedThing& pointed)
+ItemStack ItemStack::takeItem(u32 takecount)
 {
-       bool callback_exists = false;
-       bool result = false;
+       if(takecount == 0 || count == 0)
+               return ItemStack();
 
-       result = scriptapi_craftitem_on_use(
-                       env->getLua(),
-                       m_subname.c_str(), user, pointed,
-                       callback_exists);
-
-       if(callback_exists)
+       ItemStack result = *this;
+       if(takecount >= count)
        {
-               // If the callback returned true, drop one item
-               if(result)
-                       setCount(getCount() - 1);
-               return getCount() < 1;
+               // Take all
+               clear();
        }
        else
        {
-               // If neither on_place_on_ground (if place==true)
-               // nor on_drop exists, call the base implementation
-               return InventoryItem::use(env, user, pointed);
+               // Take part
+               remove(takecount);
+               result.count = takecount;
        }
+       return result;
+}
+
+ItemStack ItemStack::peekItem(u32 peekcount) const
+{
+       if(peekcount == 0 || count == 0)
+               return ItemStack();
+
+       ItemStack result = *this;
+       if(peekcount < count)
+               result.count = peekcount;
+       return result;
 }
 
 /*
        Inventory
 */
 
-InventoryList::InventoryList(std::string name, u32 size)
+InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef)
 {
        m_name = name;
        m_size = size;
+       m_itemdef = itemdef;
        clearItems();
        //m_dirty = false;
 }
 
 InventoryList::~InventoryList()
 {
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               if(m_items[i])
-                       delete m_items[i];
-       }
 }
 
 void InventoryList::clearItems()
 {
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               if(m_items[i])
-                       delete m_items[i];
-       }
-
        m_items.clear();
 
        for(u32 i=0; i<m_size; i++)
        {
-               m_items.push_back(NULL);
+               m_items.push_back(ItemStack());
        }
 
        //setDirty(true);
@@ -568,17 +452,8 @@ void InventoryList::clearItems()
 
 void InventoryList::setSize(u32 newsize)
 {
-       if(newsize < m_items.size()){
-               for(u32 i=newsize; i<m_items.size(); i++){
-                       if(m_items[i])
-                               delete m_items[i];
-               }
-               m_items.erase(newsize, m_items.size() - newsize);
-       } else {
-               for(u32 i=m_items.size(); i<newsize; i++){
-                       m_items.push_back(NULL);
-               }
-       }
+       if(newsize != m_items.size())
+               m_items.resize(newsize);
        m_size = newsize;
 }
 
@@ -588,15 +463,15 @@ void InventoryList::serialize(std::ostream &os) const
        
        for(u32 i=0; i<m_items.size(); i++)
        {
-               InventoryItem *item = m_items[i];
-               if(item != NULL)
+               const ItemStack &item = m_items[i];
+               if(item.empty())
                {
-                       os<<"Item ";
-                       item->serialize(os);
+                       os<<"Empty";
                }
                else
                {
-                       os<<"Empty";
+                       os<<"Item ";
+                       item.serialize(os);
                }
                os<<"\n";
        }
@@ -604,7 +479,7 @@ void InventoryList::serialize(std::ostream &os) const
        os<<"EndInventoryList\n";
 }
 
-void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
+void InventoryList::deSerialize(std::istream &is)
 {
        //is.imbue(std::locale("C"));
 
@@ -635,14 +510,15 @@ void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
                {
                        if(item_i > getSize() - 1)
                                throw SerializationError("too many items");
-                       InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
+                       ItemStack item;
+                       item.deSerialize(iss, m_itemdef);
                        m_items[item_i++] = item;
                }
                else if(name == "Empty")
                {
                        if(item_i > getSize() - 1)
                                throw SerializationError("too many items");
-                       m_items[item_i++] = NULL;
+                       m_items[item_i++].clear();
                }
                else
                {
@@ -653,26 +529,15 @@ void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
 
 InventoryList::InventoryList(const InventoryList &other)
 {
-       /*
-               Do this so that the items get cloned. Otherwise the pointers
-               in the array will just get copied.
-       */
        *this = other;
 }
 
 InventoryList & InventoryList::operator = (const InventoryList &other)
 {
-       m_name = other.m_name;
+       m_items = other.m_items;
        m_size = other.m_size;
-       clearItems();
-       for(u32 i=0; i<other.m_items.size(); i++)
-       {
-               InventoryItem *item = other.m_items[i];
-               if(item != NULL)
-               {
-                       m_items[i] = item->clone();
-               }
-       }
+       m_name = other.m_name;
+       m_itemdef = other.m_itemdef;
        //setDirty(true);
 
        return *this;
@@ -683,48 +548,45 @@ const std::string &InventoryList::getName() const
        return m_name;
 }
 
-u32 InventoryList::getSize()
+u32 InventoryList::getSize() const
 {
        return m_items.size();
 }
 
-u32 InventoryList::getUsedSlots()
+u32 InventoryList::getUsedSlots() const
 {
        u32 num = 0;
        for(u32 i=0; i<m_items.size(); i++)
        {
-               InventoryItem *item = m_items[i];
-               if(item != NULL)
+               if(!m_items[i].empty())
                        num++;
        }
        return num;
 }
 
-u32 InventoryList::getFreeSlots()
+u32 InventoryList::getFreeSlots() const
 {
        return getSize() - getUsedSlots();
 }
 
-const InventoryItem * InventoryList::getItem(u32 i) const
+const ItemStack& InventoryList::getItem(u32 i) const
 {
-       if(i >= m_items.size())
-               return NULL;
+       assert(i < m_size);
        return m_items[i];
 }
 
-InventoryItem * InventoryList::getItem(u32 i)
+ItemStack& InventoryList::getItem(u32 i)
 {
-       if(i >= m_items.size())
-               return NULL;
+       assert(i < m_size);
        return m_items[i];
 }
 
-InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
+ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
 {
        if(i >= m_items.size())
                return newitem;
 
-       InventoryItem *olditem = m_items[i];
+       ItemStack olditem = m_items[i];
        m_items[i] = newitem;
        //setDirty(true);
        return olditem;
@@ -733,15 +595,15 @@ InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
 void InventoryList::deleteItem(u32 i)
 {
        assert(i < m_items.size());
-       InventoryItem *item = changeItem(i, NULL);
-       if(item)
-               delete item;
+       m_items[i].clear();
 }
 
-InventoryItem * InventoryList::addItem(InventoryItem *newitem)
+ItemStack InventoryList::addItem(const ItemStack &newitem_)
 {
-       if(newitem == NULL)
-               return NULL;
+       ItemStack newitem = newitem_;
+
+       if(newitem.empty())
+               return newitem;
        
        /*
                First try to find if it could be added to some existing items
@@ -749,12 +611,12 @@ InventoryItem * InventoryList::addItem(InventoryItem *newitem)
        for(u32 i=0; i<m_items.size(); i++)
        {
                // Ignore empty slots
-               if(m_items[i] == NULL)
+               if(m_items[i].empty())
                        continue;
                // Try adding
                newitem = addItem(i, newitem);
-               if(newitem == NULL)
-                       return NULL; // All was eaten
+               if(newitem.empty())
+                       return newitem; // All was eaten
        }
 
        /*
@@ -763,150 +625,112 @@ InventoryItem * InventoryList::addItem(InventoryItem *newitem)
        for(u32 i=0; i<m_items.size(); i++)
        {
                // Ignore unempty slots
-               if(m_items[i] != NULL)
+               if(!m_items[i].empty())
                        continue;
                // Try adding
                newitem = addItem(i, newitem);
-               if(newitem == NULL)
-                       return NULL; // All was eaten
+               if(newitem.empty())
+                       return newitem; // All was eaten
        }
 
        // Return leftover
        return newitem;
 }
 
-InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
+ItemStack InventoryList::addItem(u32 i, const ItemStack &newitem)
 {
-       if(newitem == NULL)
-               return NULL;
        if(i >= m_items.size())
                return newitem;
-       
-       //setDirty(true);
-       
-       // If it is an empty position, it's an easy job.
-       InventoryItem *to_item = getItem(i);
-       if(to_item == NULL)
-       {
-               m_items[i] = newitem;
-               return NULL;
-       }
-       
-       // If not addable, return the item
-       if(newitem->addableTo(to_item) == false)
-               return newitem;
-       
-       // If the item fits fully in the slot, add counter and delete it
-       if(newitem->getCount() <= to_item->freeSpace())
-       {
-               to_item->add(newitem->getCount());
-               delete newitem;
-               return NULL;
-       }
-       // Else the item does not fit fully. Add all that fits and return
-       // the rest.
-       else
-       {
-               u16 freespace = to_item->freeSpace();
-               to_item->add(freespace);
-               newitem->remove(freespace);
-               return newitem;
-       }
+
+       ItemStack leftover = m_items[i].addItem(newitem, m_itemdef);
+       //if(leftover != newitem)
+       //      setDirty(true);
+       return leftover;
 }
 
-bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
+bool InventoryList::itemFits(const u32 i, const ItemStack &newitem,
+               ItemStack *restitem) const
 {
-       // If it is an empty position, it's an easy job.
-       const InventoryItem *to_item = getItem(i);
-       if(to_item == NULL)
+       if(i >= m_items.size())
        {
-               return true;
-       }
-       
-       // If not addable, fail
-       if(newitem->addableTo(to_item) == false)
+               if(restitem)
+                       *restitem = newitem;
                return false;
-       
-       // If the item fits fully in the slot, pass
-       if(newitem->getCount() <= to_item->freeSpace())
-       {
-               return true;
        }
 
-       return false;
+       return m_items[i].itemFits(newitem, restitem, m_itemdef);
 }
 
-bool InventoryList::roomForItem(const InventoryItem *item)
+bool InventoryList::roomForItem(const ItemStack &item_) const
 {
+       ItemStack item = item_;
+       ItemStack leftover;
        for(u32 i=0; i<m_items.size(); i++)
-               if(itemFits(i, item))
+       {
+               if(itemFits(i, item, &leftover))
                        return true;
+               item = leftover;
+       }
        return false;
 }
 
-bool InventoryList::roomForCookedItem(const InventoryItem *item)
-{
-       if(!item)
-               return false;
-       const InventoryItem *cook = item->createCookResult();
-       if(!cook)
-               return false;
-       bool room = roomForItem(cook);
-       delete cook;
-       return room;
-}
-
-InventoryItem * InventoryList::takeItem(u32 i, u32 count)
+bool InventoryList::containsItem(const ItemStack &item) const
 {
+       u32 count = item.count;
        if(count == 0)
-               return NULL;
-       
-       //setDirty(true);
-
-       InventoryItem *item = getItem(i);
-       // If it is an empty position, return NULL
-       if(item == NULL)
-               return NULL;
-       
-       if(count >= item->getCount())
-       {
-               // Get the item by swapping NULL to its place
-               return changeItem(i, NULL);
-       }
-       else
+               return true;
+       for(std::vector<ItemStack>::const_reverse_iterator
+                       i = m_items.rbegin();
+                       i != m_items.rend(); i++)
        {
-               InventoryItem *item2 = item->clone();
-               item->remove(count);
-               item2->setCount(count);
-               return item2;
+               if(count == 0)
+                       break;
+               if(i->name == item.name)
+               {
+                       if(i->count >= count)
+                               return true;
+                       else
+                               count -= i->count;
+               }
        }
-       
        return false;
 }
 
-void InventoryList::decrementMaterials(u16 count)
+ItemStack InventoryList::removeItem(const ItemStack &item)
 {
-       for(u32 i=0; i<m_items.size(); i++)
+       ItemStack removed;
+       for(std::vector<ItemStack>::reverse_iterator
+                       i = m_items.rbegin();
+                       i != m_items.rend(); i++)
        {
-               InventoryItem *item = takeItem(i, count);
-               if(item)
-                       delete item;
+               if(i->name == item.name)
+               {
+                       u32 still_to_remove = item.count - removed.count;
+                       removed.addItem(i->takeItem(still_to_remove), m_itemdef);
+                       if(removed.count == item.count)
+                               break;
+               }
        }
+       return removed;
 }
 
-void InventoryList::print(std::ostream &o)
+ItemStack InventoryList::takeItem(u32 i, u32 takecount)
 {
-       o<<"InventoryList:"<<std::endl;
-       for(u32 i=0; i<m_items.size(); i++)
-       {
-               InventoryItem *item = m_items[i];
-               if(item != NULL)
-               {
-                       o<<i<<": ";
-                       item->serialize(o);
-                       o<<"\n";
-               }
-       }
+       if(i >= m_items.size())
+               return ItemStack();
+
+       ItemStack taken = m_items[i].takeItem(takecount);
+       //if(!taken.empty())
+       //      setDirty(true);
+       return taken;
+}
+
+ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
+{
+       if(i >= m_items.size())
+               return ItemStack();
+
+       return m_items[i].peekItem(peekcount);
 }
 
 /*
@@ -927,8 +751,9 @@ void Inventory::clear()
        m_lists.clear();
 }
 
-Inventory::Inventory()
+Inventory::Inventory(IItemDefManager *itemdef)
 {
+       m_itemdef = itemdef;
 }
 
 Inventory::Inventory(const Inventory &other)
@@ -938,10 +763,15 @@ Inventory::Inventory(const Inventory &other)
 
 Inventory & Inventory::operator = (const Inventory &other)
 {
-       clear();
-       for(u32 i=0; i<other.m_lists.size(); i++)
+       // Gracefully handle self assignment
+       if(this != &other)
        {
-               m_lists.push_back(new InventoryList(*other.m_lists[i]));
+               clear();
+               m_itemdef = other.m_itemdef;
+               for(u32 i=0; i<other.m_lists.size(); i++)
+               {
+                       m_lists.push_back(new InventoryList(*other.m_lists[i]));
+               }
        }
        return *this;
 }
@@ -958,7 +788,7 @@ void Inventory::serialize(std::ostream &os) const
        os<<"EndInventory\n";
 }
 
-void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
+void Inventory::deSerialize(std::istream &is)
 {
        clear();
 
@@ -989,8 +819,8 @@ void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
                        std::getline(iss, listname, ' ');
                        iss>>listsize;
 
-                       InventoryList *list = new InventoryList(listname, listsize);
-                       list->deSerialize(is, gamedef);
+                       InventoryList *list = new InventoryList(listname, listsize, m_itemdef);
+                       list->deSerialize(is);
 
                        m_lists.push_back(list);
                }
@@ -1009,14 +839,15 @@ InventoryList * Inventory::addList(const std::string &name, u32 size)
                if(m_lists[i]->getSize() != size)
                {
                        delete m_lists[i];
-                       m_lists[i] = new InventoryList(name, size);
+                       m_lists[i] = new InventoryList(name, size, m_itemdef);
                }
                return m_lists[i];
        }
        else
        {
-               m_lists.push_back(new InventoryList(name, size));
-               return m_lists.getLast();
+               InventoryList *list = new InventoryList(name, size, m_itemdef);
+               m_lists.push_back(list);
+               return list;
        }
 }
 
@@ -1034,7 +865,7 @@ bool Inventory::deleteList(const std::string &name)
        if(i == -1)
                return false;
        delete m_lists[i];
-       m_lists.erase(i);
+       m_lists.erase(m_lists.begin() + i);
        return true;
 }
 
index 15de3c8e7d8cdd1ffc2180964a57598d74456638..3f5d835895f65130fa31646daedd932652b8db14 100644 (file)
@@ -23,460 +23,221 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 #include <sstream>
 #include <string>
+#include <vector>
 #include "common_irrlicht.h"
 #include "debug.h"
-#include "mapnode.h" // For content_t
+#include "itemdef.h"
 
-#define QUANTITY_ITEM_MAX_COUNT 99
+struct ToolDiggingProperties;
 
-class ServerActiveObject;
-class ServerEnvironment;
-struct PointedThing;
-class ITextureSource;
-class IGameDef;
-
-class InventoryItem
+struct ItemStack
 {
-public:
-       InventoryItem(IGameDef *gamedef, u16 count);
-       virtual ~InventoryItem();
-       
-       static InventoryItem* deSerialize(std::istream &is, IGameDef *gamedef);
-       static InventoryItem* deSerialize(const std::string &str,
-                       IGameDef *gamedef);
-       
-       virtual const char* getName() const = 0;
-       // Shall write the name and the parameters
-       virtual void serialize(std::ostream &os) const = 0;
-       // Shall make an exact clone of the item
-       virtual InventoryItem* clone() = 0;
-       // Return the name of the image for this item
-       virtual std::string getImageBasename() const { return ""; }
-#ifndef SERVER
-       // Shall return an image of the item (or NULL)
-       virtual video::ITexture * getImage() const
-               { return NULL; }
-       // Shall return an image of the item without embellishments (or NULL)
-       virtual video::ITexture * getImageRaw() const
-               { return getImage(); }
-#endif
-       // Shall return a text to show in the GUI
-       virtual std::string getText() { return ""; }
+       ItemStack(): name(""), count(0), wear(0), metadata("") {}
+       ItemStack(std::string name_, u16 count_,
+                       u16 wear, std::string metadata_,
+                       IItemDefManager *itemdef);
+       ~ItemStack() {}
+
+       // Serialization
+       void serialize(std::ostream &os) const;
+       void deSerialize(std::istream &is, IItemDefManager *itemdef);
+       void deSerialize(const std::string &s, IItemDefManager *itemdef);
+
        // Returns the string used for inventory
-       virtual std::string getItemString();
-       
-       // Shall return false if item is not known and cannot be used
-       virtual bool isKnown() const { return true; }
+       std::string getItemString() const;
 
        /*
                Quantity methods
        */
 
-       // Return true if the item can be add()ed to the other
-       virtual bool addableTo(const InventoryItem *other) const
-       { return false; }
-       // Return true if the other item contains this item
-       virtual bool isSubsetOf(const InventoryItem *other) const
-       { return false; }
-       // Remove the other item from this one if possible and return true
-       // Return false if not possible
-       virtual bool removeOther(const InventoryItem *other)
-       { return false; }
-       
-       u16 getCount() const
-       { return m_count; }
-       void setCount(u16 count)
-       { m_count = count; }
-
-       u16 freeSpace() const
+       bool empty() const
        {
-               u16 max = getStackMax();
-               if(m_count > max)
-                       return 0;
-               return max - m_count;
+               return count == 0;
        }
 
-       void add(u16 count)
+       void clear()
        {
-               m_count += count;
-       }
-       void remove(u16 count)
-       {
-               assert(m_count >= count);
-               m_count -= count;
+               name = "";
+               count = 0;
+               wear = 0;
+               metadata = "";
        }
 
-       /*
-               Other properties
-       */
-
-       // Maximum size of a stack
-       virtual u16 getStackMax() const {return 1;}
-       // Whether it can be used
-       virtual bool isUsable() const {return false;}
-       // Whether it can be cooked
-       virtual bool isCookable() const {return false;}
-       // Result of cooking (can randomize)
-       virtual InventoryItem *createCookResult() const {return NULL;}
-       // Time of cooking
-       virtual float getCookTime() const {return 3.0;}
-       // Whether it can be burned (<0 = cannot be burned)
-       virtual float getBurnTime() const {return -1;}
-       // Gets amount of items that dropping one ItemSAO will decrement
-       // -1 means as many as possible
-       virtual s16 getDropCount() const { return -1; }
-       // Whether this item can point to liquids
-       virtual bool areLiquidsPointable() const { return false; }
-
-       // Creates an object from the item and places it in the world.
-       // If return value is true, item should be removed.
-       virtual bool dropOrPlace(ServerEnvironment *env,
-                       ServerActiveObject *dropper,
-                       v3f pos, bool place, s16 count);
-
-       // Eat, press, activate, whatever.
-       // Called when item is left-clicked while in hand.
-       // If returns true, item shall be deleted.
-       virtual bool use(ServerEnvironment *env,
-                       ServerActiveObject *user,
-                       const PointedThing& pointed){return false;}
-
-protected:
-       IGameDef *m_gamedef;
-       u16 m_count;
-};
-
-class MaterialItem : public InventoryItem
-{
-public:
-       MaterialItem(IGameDef *gamedef, std::string nodename, u16 count);
-       // Legacy constructor
-       MaterialItem(IGameDef *gamedef, content_t content, u16 count);
-       /*
-               Implementation interface
-       */
-       virtual const char* getName() const
-       {
-               return "MaterialItem";
-       }
-       virtual void serialize(std::ostream &os) const
-       {
-               os<<"node";
-               os<<" \"";
-               os<<m_nodename;
-               os<<"\" ";
-               os<<m_count;
-       }
-       virtual InventoryItem* clone()
-       {
-               return new MaterialItem(m_gamedef, m_nodename, m_count);
-       }
-#ifndef SERVER
-       video::ITexture * getImage() const;
-#endif
-       std::string getText()
+       void add(u16 n)
        {
-               std::ostringstream os;
-               os<<m_count;
-               return os.str();
+               count += n;
        }
 
-       virtual bool addableTo(const InventoryItem *other) const
+       void remove(u16 n)
        {
-               if(std::string(other->getName()) != "MaterialItem")
-                       return false;
-               MaterialItem *m = (MaterialItem*)other;
-               if(m->m_nodename != m_nodename)
-                       return false;
-               return true;
-       }
-       virtual bool isSubsetOf(const InventoryItem *other) const
-       {
-               if(std::string(other->getName()) != "MaterialItem")
-                       return false;
-               MaterialItem *m = (MaterialItem*)other;
-               if(m->m_nodename != m_nodename)
-                       return false;
-               return m_count <= m->m_count;
+               assert(count >= n);
+               count -= n;
+               if(count == 0)
+                       clear(); // reset name, wear and metadata too
        }
-       virtual bool removeOther(const InventoryItem *other)
-       {
-               if(!other->isSubsetOf(this))
-                       return false;
-               MaterialItem *m = (MaterialItem*)other;
-               m_count += m->m_count;
-               return true;
-       }
-
-       u16 getStackMax() const
-       {
-               return QUANTITY_ITEM_MAX_COUNT;
-       }
-
-       /*
-               Other properties
-       */
-       bool isCookable() const;
-       InventoryItem *createCookResult() const;
-       float getCookTime() const;
-       float getBurnTime() const;
-       /*
-               Special properties (not part of virtual interface)
-       */
-       std::string getNodeName() const
-       { return m_nodename; }
-       content_t getMaterial() const;
-private:
-       std::string m_nodename;
-};
 
-/*
-       An item that is used as a mid-product when crafting.
-       Subnames:
-       - Stick
-*/
-class CraftItem : public InventoryItem
-{
-public:
-       CraftItem(IGameDef *gamedef, std::string subname, u16 count);
-       /*
-               Implementation interface
-       */
-       virtual const char* getName() const
-       {
-               return "CraftItem";
-       }
-       virtual void serialize(std::ostream &os) const
-       {
-               os<<"craft";
-               os<<" \"";
-               os<<m_subname;
-               os<<"\" ";
-               os<<m_count;
-       }
-       virtual InventoryItem* clone()
-       {
-               return new CraftItem(m_gamedef, m_subname, m_count);
-       }
-#ifndef SERVER
-       video::ITexture * getImage() const;
-#endif
-       std::string getText()
-       {
-               std::ostringstream os;
-               os<<m_count;
-               return os.str();
-       }
-
-       virtual bool isKnown() const;
-
-       virtual bool addableTo(const InventoryItem *other) const
-       {
-               if(std::string(other->getName()) != "CraftItem")
-                       return false;
-               CraftItem *m = (CraftItem*)other;
-               if(m->m_subname != m_subname)
-                       return false;
-               return true;
-       }
-       virtual bool isSubsetOf(const InventoryItem *other) const
-       {
-               if(std::string(other->getName()) != "CraftItem")
-                       return false;
-               CraftItem *m = (CraftItem*)other;
-               if(m->m_subname != m_subname)
-                       return false;
-               return m_count <= m->m_count;
-       }
-       virtual bool removeOther(const InventoryItem *other)
+       // Maximum size of a stack
+       u16 getStackMax(IItemDefManager *itemdef) const
        {
-               if(!other->isSubsetOf(this))
-                       return false;
-               CraftItem *m = (CraftItem*)other;
-               m_count += m->m_count;
-               return true;
+               s16 max = itemdef->get(name).stack_max;
+               return (max >= 0) ? max : 0;
        }
 
-       /*
-               Other properties
-       */
-
-       u16 getStackMax() const;
-       bool isUsable() const;
-       bool isCookable() const;
-       InventoryItem *createCookResult() const;
-       float getCookTime() const;
-       float getBurnTime() const;
-       s16 getDropCount() const;
-       bool areLiquidsPointable() const;
-
-       bool dropOrPlace(ServerEnvironment *env,
-                       ServerActiveObject *dropper,
-                       v3f pos, bool place, s16 count);
-       bool use(ServerEnvironment *env,
-                       ServerActiveObject *user,
-                       const PointedThing& pointed);
-
-       /*
-               Special methods
-       */
-       std::string getSubName()
+       // Number of items that can be added to this stack
+       u16 freeSpace(IItemDefManager *itemdef) const
        {
-               return m_subname;
+               u16 max = getStackMax(itemdef);
+               if(count > max)
+                       return 0;
+               return max - count;
        }
-private:
-       std::string m_subname;
-};
 
-class ToolItem : public InventoryItem
-{
-public:
-       ToolItem(IGameDef *gamedef, std::string toolname, u16 wear);
-       /*
-               Implementation interface
-       */
-       virtual const char* getName() const
-       {
-               return "ToolItem";
-       }
-       virtual void serialize(std::ostream &os) const
-       {
-               os<<"tool";
-               os<<" \"";
-               os<<m_toolname;
-               os<<"\" ";
-               os<<m_wear;
-       }
-       virtual InventoryItem* clone()
+       // Returns false if item is not known and cannot be used
+       bool isKnown(IItemDefManager *itemdef) const
        {
-               return new ToolItem(m_gamedef, m_toolname, m_wear);
+               return itemdef->isKnown(name);
        }
 
-       std::string getImageBasename() const;
-#ifndef SERVER
-       video::ITexture * getImage() const;
-       video::ITexture * getImageRaw() const;
-#endif
-
-       std::string getText()
+       // Returns a pointer to the item definition struct,
+       // or a fallback one (name="unknown") if the item is unknown.
+       const ItemDefinition& getDefinition(
+                       IItemDefManager *itemdef) const
        {
-               return "";
+               return itemdef->get(name);
        }
-       
-       virtual bool isKnown() const;
 
-       virtual bool isSubsetOf(const InventoryItem *other) const
+       // Get tool digging properties, or those of the hand if not a tool
+       const ToolDiggingProperties& getToolDiggingProperties(
+                       IItemDefManager *itemdef) const
        {
-               if(std::string(other->getName()) != "ToolItem")
-                       return false;
-               ToolItem *m = (ToolItem*)other;
-               if(m->m_toolname != m_toolname)
-                       return false;
-               return m_wear <= m->m_wear;
-       }
-       virtual bool removeOther(const InventoryItem *other)
-       {
-               if(!other->isSubsetOf(this))
-                       return false;
-               ToolItem *m = (ToolItem*)other;
-               m_wear -= m->m_wear;
-               return true;
+               ToolDiggingProperties *prop;
+               prop = itemdef->get(name).tool_digging_properties;
+               if(prop == NULL)
+                       prop = itemdef->get("").tool_digging_properties;
+               assert(prop != NULL);
+               return *prop;
        }
 
-       /*
-               Special methods
-       */
-       std::string getToolName()
-       {
-               return m_toolname;
-       }
-       u16 getWear()
+       // Wear out (only tools)
+       // Returns true if the item is (was) a tool
+       bool addWear(s32 amount, IItemDefManager *itemdef)
        {
-               return m_wear;
-       }
-       // Returns true if weared out
-       bool addWear(u16 add)
-       {
-               if(m_wear >= 65535 - add)
+               if(getDefinition(itemdef).type == ITEM_TOOL)
                {
-                       m_wear = 65535;
+                       if(amount > 65535 - wear)
+                               clear();
+                       else if(amount < -wear)
+                               wear = 0;
+                       else
+                               wear += amount;
                        return true;
                }
                else
                {
-                       m_wear += add;
                        return false;
                }
        }
-private:
-       std::string m_toolname;
-       u16 m_wear;
+
+       // If possible, adds newitem to this item.
+       // If cannot be added at all, returns the item back.
+       // If can be added partly, decremented item is returned back.
+       // If can be added fully, empty item is returned.
+       ItemStack addItem(const ItemStack &newitem,
+                       IItemDefManager *itemdef);
+
+       // Checks whether newitem could be added.
+       // If restitem is non-NULL, it receives the part of newitem that
+       // would be left over after adding.
+       bool itemFits(const ItemStack &newitem,
+                       ItemStack *restitem,  // may be NULL
+                       IItemDefManager *itemdef) const;
+
+       // Takes some items.
+       // If there are not enough, takes as many as it can.
+       // Returns empty item if couldn't take any.
+       ItemStack takeItem(u32 takecount);
+
+       // Similar to takeItem, but keeps this ItemStack intact.
+       ItemStack peekItem(u32 peekcount) const;
+
+       /*
+               Properties
+       */
+       std::string name;
+       u16 count;
+       u16 wear;
+       std::string metadata;
 };
 
 class InventoryList
 {
 public:
-       InventoryList(std::string name, u32 size);
+       InventoryList(std::string name, u32 size, IItemDefManager *itemdef);
        ~InventoryList();
        void clearItems();
        void setSize(u32 newsize);
        void serialize(std::ostream &os) const;
-       void deSerialize(std::istream &is, IGameDef *gamedef);
+       void deSerialize(std::istream &is);
 
        InventoryList(const InventoryList &other);
        InventoryList & operator = (const InventoryList &other);
 
        const std::string &getName() const;
-       u32 getSize();
+       u32 getSize() const;
        // Count used slots
-       u32 getUsedSlots();
-       u32 getFreeSlots();
-
-       /*bool getDirty(){ return m_dirty; }
-       void setDirty(bool dirty=true){ m_dirty = dirty; }*/
-       
-       // Get pointer to item
-       const InventoryItem * getItem(u32 i) const;
-       InventoryItem * getItem(u32 i);
-       // Returns old item (or NULL). Parameter can be NULL.
-       InventoryItem * changeItem(u32 i, InventoryItem *newitem);
+       u32 getUsedSlots() const;
+       u32 getFreeSlots() const;
+
+       // Get reference to item
+       const ItemStack& getItem(u32 i) const;
+       ItemStack& getItem(u32 i);
+       // Returns old item. Parameter can be an empty item.
+       ItemStack changeItem(u32 i, const ItemStack &newitem);
        // Delete item
        void deleteItem(u32 i);
 
-       // Adds an item to a suitable place. Returns leftover item.
-       // If all went into the list, returns NULL.
-       InventoryItem * addItem(InventoryItem *newitem);
+       // Adds an item to a suitable place. Returns leftover item (possibly empty).
+       ItemStack addItem(const ItemStack &newitem);
 
        // If possible, adds item to given slot.
        // If cannot be added at all, returns the item back.
        // If can be added partly, decremented item is returned back.
-       // If can be added fully, NULL is returned.
-       InventoryItem * addItem(u32 i, InventoryItem *newitem);
+       // If can be added fully, empty item is returned.
+       ItemStack addItem(u32 i, const ItemStack &newitem);
 
        // Checks whether the item could be added to the given slot
-       bool itemFits(const u32 i, const InventoryItem *newitem);
+       // If restitem is non-NULL, it receives the part of newitem that
+       // would be left over after adding.
+       bool itemFits(const u32 i, const ItemStack &newitem,
+                       ItemStack *restitem = NULL) const;
 
        // Checks whether there is room for a given item
-       bool roomForItem(const InventoryItem *item);
+       bool roomForItem(const ItemStack &item) const;
+
+       // Checks whether the given count of the given item name
+       // exists in this inventory list.
+       bool containsItem(const ItemStack &item) const;
 
-       // Checks whether there is room for a given item aftr it has been cooked
-       bool roomForCookedItem(const InventoryItem *item);
+       // Removes the given count of the given item name from
+       // this inventory list. Walks the list in reverse order.
+       // If not as many items exist as requested, removes as
+       // many as possible.
+       // Returns the items that were actually removed.
+       ItemStack removeItem(const ItemStack &item);
 
        // Takes some items from a slot.
        // If there are not enough, takes as many as it can.
-       // Returns NULL if couldn't take any.
-       InventoryItem * takeItem(u32 i, u32 count);
+       // Returns empty item if couldn't take any.
+       ItemStack takeItem(u32 i, u32 takecount);
 
-       // Decrements amount of every material item
-       void decrementMaterials(u16 count);
+       // Similar to takeItem, but keeps the slot intact.
+       ItemStack peekItem(u32 i, u32 peekcount) const;
 
-       void print(std::ostream &o);
-       
 private:
-       core::array<InventoryItem*> m_items;
+       std::vector<ItemStack> m_items;
        u32 m_size;
        std::string m_name;
-       //bool m_dirty;
+       IItemDefManager *m_itemdef;
 };
 
 class Inventory
@@ -486,20 +247,19 @@ public:
 
        void clear();
 
-       Inventory();
+       Inventory(IItemDefManager *itemdef);
        Inventory(const Inventory &other);
        Inventory & operator = (const Inventory &other);
        
        void serialize(std::ostream &os) const;
-       void deSerialize(std::istream &is, IGameDef *gamedef);
+       void deSerialize(std::istream &is);
 
        InventoryList * addList(const std::string &name, u32 size);
        InventoryList * getList(const std::string &name);
        const InventoryList * getList(const std::string &name) const;
        bool deleteList(const std::string &name);
-       // A shorthand for adding items.
-       // Returns NULL if the item was fully added, leftover otherwise.
-       InventoryItem * addItem(const std::string &listname, InventoryItem *newitem)
+       // A shorthand for adding items. Returns leftover item (possibly empty).
+       ItemStack addItem(const std::string &listname, const ItemStack &newitem)
        {
                InventoryList *list = getList(listname);
                if(list == NULL)
@@ -511,7 +271,8 @@ private:
        // -1 if not found
        const s32 getListIndex(const std::string &name) const;
 
-       core::array<InventoryList*> m_lists;
+       std::vector<InventoryList*> m_lists;
+       IItemDefManager *m_itemdef;
 };
 
 #endif
index 195438d266013415799ac99205b296b9f252f21e..0149fd9abe758c154bf799659b384bd0d71104be 100644 (file)
@@ -18,73 +18,91 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "inventorymanager.h"
-#include "serverremoteplayer.h"
 #include "log.h"
-#include "mapblock.h" // getNodeBlockPos
+#include "environment.h"
+#include "scriptapi.h"
+#include "serverobject.h"
+#include "main.h"  // for g_settings
+#include "settings.h"
+#include "utility.h"
 
 /*
-       InventoryManager
+       InventoryLocation
 */
 
-// Wrapper for old code
-Inventory* InventoryManager::getInventory(InventoryContext *c, std::string id)
+std::string InventoryLocation::dump() const
 {
-       if(id == "current_player")
-       {
-               assert(c->current_player);
-               InventoryLocation loc;
-               loc.setPlayer(c->current_player->getName());
-               return getInventory(loc);
-       }
-       
-       Strfnd fn(id);
-       std::string id0 = fn.next(":");
-
-       if(id0 == "nodemeta")
-       {
-               v3s16 p;
-               p.X = stoi(fn.next(","));
-               p.Y = stoi(fn.next(","));
-               p.Z = stoi(fn.next(","));
+       std::ostringstream os(std::ios::binary);
+       serialize(os);
+       return os.str();
+}
 
-               InventoryLocation loc;
-               loc.setNodeMeta(p);
-               return getInventory(loc);
+void InventoryLocation::serialize(std::ostream &os) const
+{
+        switch(type){
+        case InventoryLocation::UNDEFINED:
+        {
+               os<<"undefined";
        }
-
-       errorstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
-       return NULL;
+        break;
+        case InventoryLocation::CURRENT_PLAYER:
+        {
+               os<<"current_player";
+        }
+        break;
+        case InventoryLocation::PLAYER:
+        {
+               os<<"player:"<<name;
+        }
+        break;
+        case InventoryLocation::NODEMETA:
+        {
+               os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
+        }
+        break;
+        default:
+                assert(0);
+        }
 }
-// Wrapper for old code
-void InventoryManager::inventoryModified(InventoryContext *c, std::string id)
+
+void InventoryLocation::deSerialize(std::istream &is)
 {
-       if(id == "current_player")
+       std::string tname;
+       std::getline(is, tname, ':');
+       if(tname == "undefined")
        {
-               assert(c->current_player);
-               InventoryLocation loc;
-               loc.setPlayer(c->current_player->getName());
-               setInventoryModified(loc);
-               return;
+               type = InventoryLocation::UNDEFINED;
        }
-       
-       Strfnd fn(id);
-       std::string id0 = fn.next(":");
-
-       if(id0 == "nodemeta")
+       else if(tname == "current_player")
+       {
+               type = InventoryLocation::CURRENT_PLAYER;
+       }
+       else if(tname == "player")
+       {
+               type = InventoryLocation::PLAYER;
+               std::getline(is, name, '\n');
+       }
+       else if(tname == "nodemeta")
        {
-               v3s16 p;
+               type = InventoryLocation::NODEMETA;
+               std::string pos;
+               std::getline(is, pos, '\n');
+               Strfnd fn(pos);
                p.X = stoi(fn.next(","));
                p.Y = stoi(fn.next(","));
                p.Z = stoi(fn.next(","));
-               v3s16 blockpos = getNodeBlockPos(p);
-
-               InventoryLocation loc;
-               loc.setNodeMeta(p);
-               setInventoryModified(loc);
-               return;
        }
+       else
+       {
+               infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
+               throw SerializationError("Unknown InventoryLocation type");
+       }
+}
 
-       errorstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
+void InventoryLocation::deSerialize(std::string s)
+{
+       std::istringstream is(s, std::ios::binary);
+       deSerialize(is);
 }
 
 /*
@@ -110,14 +128,6 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is)
        return a;
 }
 
-static std::string describeC(const struct InventoryContext *c)
-{
-       if(c->current_player == NULL)
-               return "current_player=NULL";
-       else
-               return std::string("current_player=") + c->current_player->getName();
-}
-
 IMoveAction::IMoveAction(std::istream &is)
 {
        std::string ts;
@@ -125,14 +135,16 @@ IMoveAction::IMoveAction(std::istream &is)
        std::getline(is, ts, ' ');
        count = stoi(ts);
 
-       std::getline(is, from_inv, ' ');
+       std::getline(is, ts, ' ');
+       from_inv.deSerialize(ts);
 
        std::getline(is, from_list, ' ');
 
        std::getline(is, ts, ' ');
        from_i = stoi(ts);
 
-       std::getline(is, to_inv, ' ');
+       std::getline(is, ts, ' ');
+       to_inv.deSerialize(ts);
 
        std::getline(is, to_list, ' ');
 
@@ -140,22 +152,21 @@ IMoveAction::IMoveAction(std::istream &is)
        to_i = stoi(ts);
 }
 
-void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
-               ServerEnvironment *env)
+void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player)
 {
-       Inventory *inv_from = mgr->getInventory(c, from_inv);
-       Inventory *inv_to = mgr->getInventory(c, to_inv);
+       Inventory *inv_from = mgr->getInventory(from_inv);
+       Inventory *inv_to = mgr->getInventory(to_inv);
        
        if(!inv_from){
                infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
-                               <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
-                               <<", to_inv=\""<<to_inv<<"\""<<std::endl;
+                               <<"from_inv=\""<<from_inv.dump()<<"\""
+                               <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
                return;
        }
        if(!inv_to){
                infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
-                               "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
-                               <<", to_inv=\""<<to_inv<<"\""<<std::endl;
+                               <<"from_inv=\""<<from_inv.dump()<<"\""
+                               <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
                return;
        }
 
@@ -167,20 +178,20 @@ void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
        */
        if(!list_from){
                infostream<<"IMoveAction::apply(): FAIL: source list not found: "
-                               <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
+                               <<"from_inv=\""<<from_inv.dump()<<"\""
                                <<", from_list=\""<<from_list<<"\""<<std::endl;
                return;
        }
        if(!list_to){
                infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
-                               <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
+                               <<"to_inv=\""<<to_inv.dump()<<"\""
                                <<", to_list=\""<<to_list<<"\""<<std::endl;
                return;
        }
-       if(list_from->getItem(from_i) == NULL)
+       if(list_from->getItem(from_i).empty())
        {
                infostream<<"IMoveAction::apply(): FAIL: source item not found: "
-                               <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
+                               <<"from_inv=\""<<from_inv.dump()<<"\""
                                <<", from_list=\""<<from_list<<"\""
                                <<" from_i="<<from_i<<std::endl;
                return;
@@ -191,27 +202,28 @@ void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
        if(inv_from == inv_to && list_from == list_to && from_i == to_i)
        {
                infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
-                               <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
+                               <<"are the same: inv=\""<<from_inv.dump()
+                               <<"\" list=\""<<from_list
                                <<"\" i="<<from_i<<std::endl;
                return;
        }
        
        // Take item from source list
-       InventoryItem *item1 = NULL;
+       ItemStack item1;
        if(count == 0)
-               item1 = list_from->changeItem(from_i, NULL);
+               item1 = list_from->changeItem(from_i, ItemStack());
        else
                item1 = list_from->takeItem(from_i, count);
 
        // Try to add the item to destination list
-       InventoryItem *olditem = item1;
+       int oldcount = item1.count;
        item1 = list_to->addItem(to_i, item1);
 
        // If something is returned, the item was not fully added
-       if(item1 != NULL)
+       if(!item1.empty())
        {
                // If olditem is returned, nothing was added.
-               bool nothing_added = (item1 == olditem);
+               bool nothing_added = (item1.count == oldcount);
                
                // If something else is returned, part of the item was left unadded.
                // Add the other part back to the source item
@@ -222,24 +234,24 @@ void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
                if(nothing_added)
                {
                        // Take item from source list
-                       item1 = list_from->changeItem(from_i, NULL);
+                       item1 = list_from->changeItem(from_i, ItemStack());
                        // Adding was not possible, swap the items.
-                       InventoryItem *item2 = list_to->changeItem(to_i, item1);
+                       ItemStack item2 = list_to->changeItem(to_i, item1);
                        // Put item from destination list to the source list
                        list_from->changeItem(from_i, item2);
                }
        }
 
-       mgr->inventoryModified(c, from_inv);
-       if(from_inv != to_inv)
-               mgr->inventoryModified(c, to_inv);
+       mgr->setInventoryModified(from_inv);
+       if(inv_from != inv_to)
+               mgr->setInventoryModified(to_inv);
        
        infostream<<"IMoveAction::apply(): moved at "
-                       <<"["<<describeC(c)<<"]"
-                       <<" from inv=\""<<from_inv<<"\""
+                       <<" count="<<count<<"\""
+                       <<" from inv=\""<<from_inv.dump()<<"\""
                        <<" list=\""<<from_list<<"\""
                        <<" i="<<from_i
-                       <<" to inv=\""<<to_inv<<"\""
+                       <<" to inv=\""<<to_inv.dump()<<"\""
                        <<" list=\""<<to_list<<"\""
                        <<" i="<<to_i
                        <<std::endl;
@@ -252,7 +264,8 @@ IDropAction::IDropAction(std::istream &is)
        std::getline(is, ts, ' ');
        count = stoi(ts);
 
-       std::getline(is, from_inv, ' ');
+       std::getline(is, ts, ' ');
+       from_inv.deSerialize(ts);
 
        std::getline(is, from_list, ' ');
 
@@ -260,27 +273,13 @@ IDropAction::IDropAction(std::istream &is)
        from_i = stoi(ts);
 }
 
-void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
-               ServerEnvironment *env)
+void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player)
 {
-       if(c->current_player == NULL){
-               infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl;
-               return;
-       }
-
-       // Do NOT cast directly to ServerActiveObject*, it breaks
-       // because of multiple inheritance.
-       ServerActiveObject *dropper =
-               static_cast<ServerActiveObject*>(
-               static_cast<ServerRemotePlayer*>(
-                       c->current_player
-               ));
-
-       Inventory *inv_from = mgr->getInventory(c, from_inv);
+       Inventory *inv_from = mgr->getInventory(from_inv);
        
        if(!inv_from){
                infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
-                               <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
+                               <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
                return;
        }
 
@@ -291,254 +290,35 @@ void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
        */
        if(!list_from){
                infostream<<"IDropAction::apply(): FAIL: source list not found: "
-                               <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
-                               <<", from_list=\""<<from_list<<"\""<<std::endl;
+                               <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
                return;
        }
-       InventoryItem *item = list_from->getItem(from_i);
-       if(item == NULL)
+       if(list_from->getItem(from_i).empty())
        {
                infostream<<"IDropAction::apply(): FAIL: source item not found: "
-                               <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
+                               <<"from_inv=\""<<from_inv.dump()<<"\""
                                <<", from_list=\""<<from_list<<"\""
                                <<" from_i="<<from_i<<std::endl;
                return;
        }
 
-       v3f pos = dropper->getBasePosition();
-       pos.Y += 0.5*BS;
-
-       s16 count2 = count;
-       if(count2 == 0)
-               count2 = -1;
-
        /*
                Drop the item
        */
-       bool remove = item->dropOrPlace(env, dropper, pos, false, count2);
-       if(remove)
-               list_from->deleteItem(from_i);
-
-       mgr->inventoryModified(c, from_inv);
+       ItemStack item = list_from->getItem(from_i);
+       if(scriptapi_item_on_drop(player->getEnv()->getLua(), item, player,
+                               player->getBasePosition() + v3f(0,1,0)))
+       {
+               // Apply returned ItemStack
+               if(g_settings->getBool("creative_mode") == false
+                               || from_inv.type != InventoryLocation::PLAYER)
+                       list_from->changeItem(from_i, item);
+               mgr->setInventoryModified(from_inv);
+       }
 
        infostream<<"IDropAction::apply(): dropped "
-                       <<"["<<describeC(c)<<"]"
-                       <<" from inv=\""<<from_inv<<"\""
+                       <<" from inv=\""<<from_inv.dump()<<"\""
                        <<" list=\""<<from_list<<"\""
                        <<" i="<<from_i
                        <<std::endl;
 }
-
-/*
-       Craft checking system
-*/
-
-bool ItemSpec::checkItem(const InventoryItem *item) const
-{
-       if(type == ITEM_NONE)
-       {
-               // Has to be no item
-               if(item != NULL)
-                       return false;
-               return true;
-       }
-       
-       // There should be an item
-       if(item == NULL)
-               return false;
-
-       std::string itemname = item->getName();
-
-       if(type == ITEM_MATERIAL)
-       {
-               if(itemname != "MaterialItem")
-                       return false;
-               MaterialItem *mitem = (MaterialItem*)item;
-               if(num != 65535){
-                       if(mitem->getMaterial() != num)
-                               return false;
-               } else {
-                       if(mitem->getNodeName() != name)
-                               return false;
-               }
-       }
-       else if(type == ITEM_CRAFT)
-       {
-               if(itemname != "CraftItem")
-                       return false;
-               CraftItem *mitem = (CraftItem*)item;
-               if(mitem->getSubName() != name)
-                       return false;
-       }
-       else if(type == ITEM_TOOL)
-       {
-               // Not supported yet
-               assert(0);
-       }
-       else if(type == ITEM_MBO)
-       {
-               // Not supported yet
-               assert(0);
-       }
-       else
-       {
-               // Not supported yet
-               assert(0);
-       }
-       return true;
-}
-
-bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
-{
-       u16 items_min_x = 100;
-       u16 items_max_x = 100;
-       u16 items_min_y = 100;
-       u16 items_max_y = 100;
-       for(u16 y=0; y<3; y++)
-       for(u16 x=0; x<3; x++)
-       {
-               if(items[y*3 + x] == NULL)
-                       continue;
-               if(items_min_x == 100 || x < items_min_x)
-                       items_min_x = x;
-               if(items_min_y == 100 || y < items_min_y)
-                       items_min_y = y;
-               if(items_max_x == 100 || x > items_max_x)
-                       items_max_x = x;
-               if(items_max_y == 100 || y > items_max_y)
-                       items_max_y = y;
-       }
-       // No items at all, just return false
-       if(items_min_x == 100)
-               return false;
-       
-       u16 items_w = items_max_x - items_min_x + 1;
-       u16 items_h = items_max_y - items_min_y + 1;
-
-       u16 specs_min_x = 100;
-       u16 specs_max_x = 100;
-       u16 specs_min_y = 100;
-       u16 specs_max_y = 100;
-       for(u16 y=0; y<3; y++)
-       for(u16 x=0; x<3; x++)
-       {
-               if(specs[y*3 + x].type == ITEM_NONE)
-                       continue;
-               if(specs_min_x == 100 || x < specs_min_x)
-                       specs_min_x = x;
-               if(specs_min_y == 100 || y < specs_min_y)
-                       specs_min_y = y;
-               if(specs_max_x == 100 || x > specs_max_x)
-                       specs_max_x = x;
-               if(specs_max_y == 100 || y > specs_max_y)
-                       specs_max_y = y;
-       }
-       // No specs at all, just return false
-       if(specs_min_x == 100)
-               return false;
-
-       u16 specs_w = specs_max_x - specs_min_x + 1;
-       u16 specs_h = specs_max_y - specs_min_y + 1;
-
-       // Different sizes
-       if(items_w != specs_w || items_h != specs_h)
-               return false;
-
-       for(u16 y=0; y<specs_h; y++)
-       for(u16 x=0; x<specs_w; x++)
-       {
-               u16 items_x = items_min_x + x;
-               u16 items_y = items_min_y + y;
-               u16 specs_x = specs_min_x + x;
-               u16 specs_y = specs_min_y + y;
-               const InventoryItem *item = items[items_y * 3 + items_x];
-               const ItemSpec &spec = specs[specs_y * 3 + specs_x];
-
-               if(spec.checkItem(item) == false)
-                       return false;
-       }
-
-       return true;
-}
-
-bool checkItemCombination(const InventoryItem * const * items,
-               const InventoryItem * const * specs)
-{
-       u16 items_min_x = 100;
-       u16 items_max_x = 100;
-       u16 items_min_y = 100;
-       u16 items_max_y = 100;
-       for(u16 y=0; y<3; y++)
-       for(u16 x=0; x<3; x++)
-       {
-               if(items[y*3 + x] == NULL)
-                       continue;
-               if(items_min_x == 100 || x < items_min_x)
-                       items_min_x = x;
-               if(items_min_y == 100 || y < items_min_y)
-                       items_min_y = y;
-               if(items_max_x == 100 || x > items_max_x)
-                       items_max_x = x;
-               if(items_max_y == 100 || y > items_max_y)
-                       items_max_y = y;
-       }
-       // No items at all, just return false
-       if(items_min_x == 100)
-               return false;
-       
-       u16 items_w = items_max_x - items_min_x + 1;
-       u16 items_h = items_max_y - items_min_y + 1;
-
-       u16 specs_min_x = 100;
-       u16 specs_max_x = 100;
-       u16 specs_min_y = 100;
-       u16 specs_max_y = 100;
-       for(u16 y=0; y<3; y++)
-       for(u16 x=0; x<3; x++)
-       {
-               if(specs[y*3 + x] == NULL)
-                       continue;
-               if(specs_min_x == 100 || x < specs_min_x)
-                       specs_min_x = x;
-               if(specs_min_y == 100 || y < specs_min_y)
-                       specs_min_y = y;
-               if(specs_max_x == 100 || x > specs_max_x)
-                       specs_max_x = x;
-               if(specs_max_y == 100 || y > specs_max_y)
-                       specs_max_y = y;
-       }
-       // No specs at all, just return false
-       if(specs_min_x == 100)
-               return false;
-
-       u16 specs_w = specs_max_x - specs_min_x + 1;
-       u16 specs_h = specs_max_y - specs_min_y + 1;
-
-       // Different sizes
-       if(items_w != specs_w || items_h != specs_h)
-               return false;
-
-       for(u16 y=0; y<specs_h; y++)
-       for(u16 x=0; x<specs_w; x++)
-       {
-               u16 items_x = items_min_x + x;
-               u16 items_y = items_min_y + y;
-               u16 specs_x = specs_min_x + x;
-               u16 specs_y = specs_min_y + y;
-               const InventoryItem *item = items[items_y * 3 + items_x];
-               const InventoryItem *spec = specs[specs_y * 3 + specs_x];
-               
-               if(item == NULL && spec == NULL)
-                       continue;
-               if(item == NULL && spec != NULL)
-                       return false;
-               if(item != NULL && spec == NULL)
-                       return false;
-               if(!spec->isSubsetOf(item))
-                       return false;
-       }
-
-       return true;
-}
-
-
index 4abe5e73d029b0537e5d4f115668b1bf12a22110..f6ae4febacb758f116df85acc2f08d243411d064 100644 (file)
@@ -21,19 +21,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define INVENTORYMANAGER_HEADER
 
 #include "inventory.h"
+#include <iostream>
+#include <string>
+class ServerActiveObject;
 
-// Should probably somehow replace InventoryContext over time
 struct InventoryLocation
 {
        enum Type{
                UNDEFINED,
+               CURRENT_PLAYER,
                PLAYER,
-               NODEMETA
+               NODEMETA,
        } type;
 
        std::string name; // PLAYER
        v3s16 p; // NODEMETA
 
+       InventoryLocation()
+       {
+               setUndefined();
+       }
+       void setUndefined()
+       {
+               type = UNDEFINED;
+       }
+       void setCurrentPlayer()
+       {
+               type = CURRENT_PLAYER;
+       }
        void setPlayer(const std::string &name_)
        {
                type = PLAYER;
@@ -44,17 +59,17 @@ struct InventoryLocation
                type = NODEMETA;
                p = p_;
        }
-};
 
-class Player;
+       void applyCurrentPlayer(const std::string &name_)
+       {
+               if(type == CURRENT_PLAYER)
+                       setPlayer(name_);
+       }
 
-struct InventoryContext
-{
-       Player *current_player;
-       
-       InventoryContext():
-               current_player(NULL)
-       {}
+       std::string dump() const;
+       void serialize(std::ostream &os) const;
+       void deSerialize(std::istream &is);
+       void deSerialize(std::string s);
 };
 
 struct InventoryAction;
@@ -68,18 +83,11 @@ public:
        // Get an inventory or set it modified (so it will be updated over
        // network or so)
        virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;}
+       virtual std::string getInventoryOwner(const InventoryLocation &loc){return "";}
        virtual void setInventoryModified(const InventoryLocation &loc){}
 
        // Used on the client to send an action to the server
        virtual void inventoryAction(InventoryAction *a){}
-
-       // (Deprecated; these wrap to the latter ones)
-       // Get a pointer to an inventory specified by id. id can be:
-       // - "current_player"
-       // - "nodemeta:X,Y,Z"
-       Inventory* getInventory(InventoryContext *c, std::string id);
-       // Used on the server by InventoryAction::apply and other stuff
-       void inventoryModified(InventoryContext *c, std::string id);
 };
 
 #define IACTION_MOVE 0
@@ -91,18 +99,17 @@ struct InventoryAction
        
        virtual u16 getType() const = 0;
        virtual void serialize(std::ostream &os) const = 0;
-       virtual void apply(InventoryContext *c, InventoryManager *mgr,
-                       ServerEnvironment *env) = 0;
+       virtual void apply(InventoryManager *mgr, ServerActiveObject *player) = 0;
 };
 
 struct IMoveAction : public InventoryAction
 {
        // count=0 means "everything"
        u16 count;
-       std::string from_inv;
+       InventoryLocation from_inv;
        std::string from_list;
        s16 from_i;
-       std::string to_inv;
+       InventoryLocation to_inv;
        std::string to_list;
        s16 to_i;
        
@@ -124,23 +131,22 @@ struct IMoveAction : public InventoryAction
        {
                os<<"Move ";
                os<<count<<" ";
-               os<<from_inv<<" ";
+               os<<from_inv.dump()<<" ";
                os<<from_list<<" ";
                os<<from_i<<" ";
-               os<<to_inv<<" ";
+               os<<to_inv.dump()<<" ";
                os<<to_list<<" ";
                os<<to_i;
        }
 
-       void apply(InventoryContext *c, InventoryManager *mgr,
-                       ServerEnvironment *env);
+       void apply(InventoryManager *mgr, ServerActiveObject *player);
 };
 
 struct IDropAction : public InventoryAction
 {
        // count=0 means "everything"
        u16 count;
-       std::string from_inv;
+       InventoryLocation from_inv;
        std::string from_list;
        s16 from_i;
        
@@ -161,68 +167,13 @@ struct IDropAction : public InventoryAction
        {
                os<<"Drop ";
                os<<count<<" ";
-               os<<from_inv<<" ";
+               os<<from_inv.dump()<<" ";
                os<<from_list<<" ";
                os<<from_i;
        }
 
-       void apply(InventoryContext *c, InventoryManager *mgr,
-                       ServerEnvironment *env);
-};
-
-/*
-       Craft checking system
-*/
-
-enum ItemSpecType
-{
-       ITEM_NONE,
-       ITEM_MATERIAL,
-       ITEM_CRAFT,
-       ITEM_TOOL,
-       ITEM_MBO
-};
-
-struct ItemSpec
-{
-       enum ItemSpecType type;
-       // Only other one of these is used
-       std::string name;
-       u16 num;
-
-       ItemSpec():
-               type(ITEM_NONE)
-       {
-       }
-       ItemSpec(enum ItemSpecType a_type, std::string a_name):
-               type(a_type),
-               name(a_name),
-               num(65535)
-       {
-       }
-       ItemSpec(enum ItemSpecType a_type, u16 a_num):
-               type(a_type),
-               name(""),
-               num(a_num)
-       {
-       }
-
-       bool checkItem(const InventoryItem *item) const;
+       void apply(InventoryManager *mgr, ServerActiveObject *player);
 };
 
-/*
-       items: a pointer to an array of 9 pointers to items
-       specs: a pointer to an array of 9 ItemSpecs
-*/
-bool checkItemCombination(const InventoryItem * const*items, const ItemSpec *specs);
-
-/*
-       items: a pointer to an array of 9 pointers to items
-       specs: a pointer to an array of 9 pointers to items
-*/
-bool checkItemCombination(const InventoryItem * const * items,
-               const InventoryItem * const * specs);
-
-
 #endif
 
index bc17694fc44903a2280e89fdd4d7cdb847e7b0cf..67c96a653ca70f592bebea7aecc10ed2831336ef 100644 (file)
@@ -39,6 +39,8 @@ typedef core::vector2d<s32> v2s32;
 typedef core::vector2d<u32> v2u32;
 typedef core::vector2d<f32> v2f32;
 
+typedef core::aabbox3d<f32> aabb3f;
+
 #ifdef _MSC_VER
        // Windows
        typedef unsigned long long u64;
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
new file mode 100644 (file)
index 0000000..aa888bb
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2011 Kahrl <kahrl@gmx.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 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 General Public License for more details.
+
+You should have received a copy of the GNU 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.
+*/
+
+#include "itemdef.h"
+
+#include "gamedef.h"
+#include "nodedef.h"
+#include "materials.h"
+#include "inventory.h"
+#ifndef SERVER
+#include "mapblock_mesh.h"
+#include "mesh.h"
+#include "tile.h"
+#endif
+#include "log.h"
+#include "utility.h"
+#include <map>
+#include <set>
+
+/*
+       ItemDefinition
+*/
+ItemDefinition::ItemDefinition()
+{
+       resetInitial();
+}
+
+ItemDefinition::ItemDefinition(const ItemDefinition &def)
+{
+       resetInitial();
+       *this = def;
+}
+
+ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
+{
+       if(this == &def)
+               return *this;
+
+       reset();
+
+       type = def.type;
+       name = def.name;
+       description = def.description;
+       inventory_image = def.inventory_image;
+       wield_image = def.wield_image;
+       wield_scale = def.wield_scale;
+       stack_max = def.stack_max;
+       usable = def.usable;
+       liquids_pointable = def.liquids_pointable;
+       if(def.tool_digging_properties)
+       {
+               tool_digging_properties = new ToolDiggingProperties(
+                               *def.tool_digging_properties);
+       }
+#ifndef SERVER
+       inventory_texture = def.inventory_texture;
+       if(def.wield_mesh)
+       {
+               wield_mesh = def.wield_mesh;
+               wield_mesh->grab();
+       }
+#endif
+       return *this;
+}
+
+ItemDefinition::~ItemDefinition()
+{
+       reset();
+}
+
+void ItemDefinition::resetInitial()
+{
+       // Initialize pointers to NULL so reset() does not delete undefined pointers
+       tool_digging_properties = NULL;
+#ifndef SERVER
+       inventory_texture = NULL;
+       wield_mesh = NULL;
+#endif
+       reset();
+}
+
+void ItemDefinition::reset()
+{
+       type = ITEM_NONE;
+       name = "";
+       description = "";
+       inventory_image = "";
+       wield_image = "";
+       wield_scale = v3f(1.0, 1.0, 1.0);
+       stack_max = 99;
+       usable = false;
+       liquids_pointable = false;
+       if(tool_digging_properties)
+       {
+               delete tool_digging_properties;
+               tool_digging_properties = NULL;
+       }
+
+#ifndef SERVER
+       inventory_texture = NULL;
+       if(wield_mesh)
+       {
+               wield_mesh->drop();
+               wield_mesh = NULL;
+       }
+#endif
+}
+
+void ItemDefinition::serialize(std::ostream &os) const
+{
+       writeU8(os, 0); // version
+       writeU8(os, type);
+       os<<serializeString(name);
+       os<<serializeString(description);
+       os<<serializeString(inventory_image);
+       os<<serializeString(wield_image);
+       writeV3F1000(os, wield_scale);
+       writeS16(os, stack_max);
+       writeU8(os, usable);
+       writeU8(os, liquids_pointable);
+       std::string tool_digging_properties_s = "";
+       if(tool_digging_properties)
+       {
+               std::ostringstream tmp_os(std::ios::binary);
+               tool_digging_properties->serialize(tmp_os);
+               tool_digging_properties_s = tmp_os.str();
+       }
+       os<<serializeString(tool_digging_properties_s);
+}
+
+void ItemDefinition::deSerialize(std::istream &is)
+{
+       // Reset everything
+       reset();
+
+       // Deserialize
+       int version = readU8(is);
+       if(version != 0)
+               throw SerializationError("unsupported ItemDefinition version");
+       type = (enum ItemType)readU8(is);
+       name = deSerializeString(is);
+       description = deSerializeString(is);
+       inventory_image = deSerializeString(is);
+       wield_image = deSerializeString(is);
+       wield_scale = readV3F1000(is);
+       stack_max = readS16(is);
+       usable = readU8(is);
+       liquids_pointable = readU8(is);
+       std::string tool_digging_properties_s = deSerializeString(is);
+       if(!tool_digging_properties_s.empty())
+       {
+               std::istringstream tmp_is(tool_digging_properties_s, std::ios::binary);
+               tool_digging_properties = new ToolDiggingProperties;
+               tool_digging_properties->deSerialize(tmp_is);
+       }
+}
+
+/*
+       CItemDefManager
+*/
+
+// SUGG: Support chains of aliases?
+
+class CItemDefManager: public IWritableItemDefManager
+{
+public:
+       CItemDefManager()
+       {
+               clear();
+       }
+       virtual ~CItemDefManager()
+       {
+       }
+       virtual const ItemDefinition& get(const std::string &name_) const
+       {
+               // Convert name according to possible alias
+               std::string name = getAlias(name_);
+               // Get the definition
+               std::map<std::string, ItemDefinition*>::const_iterator i;
+               i = m_item_definitions.find(name);
+               if(i == m_item_definitions.end())
+                       i = m_item_definitions.find("unknown");
+               assert(i != m_item_definitions.end());
+               return *(i->second);
+       }
+       virtual std::string getAlias(const std::string &name) const
+       {
+               std::map<std::string, std::string>::const_iterator i;
+               i = m_aliases.find(name);
+               if(i != m_aliases.end())
+                       return i->second;
+               return name;
+       }
+       virtual std::set<std::string> getAll() const
+       {
+               std::set<std::string> result;
+               for(std::map<std::string, ItemDefinition*>::const_iterator
+                               i = m_item_definitions.begin();
+                               i != m_item_definitions.end(); i++)
+               {
+                       result.insert(i->first);
+               }
+               for(std::map<std::string, std::string>::const_iterator
+                               i = m_aliases.begin();
+                               i != m_aliases.end(); i++)
+               {
+                       result.insert(i->first);
+               }
+               return result;
+       }
+       virtual bool isKnown(const std::string &name_) const
+       {
+               // Convert name according to possible alias
+               std::string name = getAlias(name_);
+               // Get the definition
+               std::map<std::string, ItemDefinition*>::const_iterator i;
+               return m_item_definitions.find(name) != m_item_definitions.end();
+       }
+       void clear()
+       {
+               for(std::map<std::string, ItemDefinition*>::const_iterator
+                               i = m_item_definitions.begin();
+                               i != m_item_definitions.end(); i++)
+               {
+                       delete i->second;
+               }
+               m_item_definitions.clear();
+               m_aliases.clear();
+
+               // Add the four builtin items:
+               //   "" is the hand
+               //   "unknown" is returned whenever an undefined item is accessed
+               //   "air" is the air node
+               //   "ignore" is the ignore node
+
+               ItemDefinition* hand_def = new ItemDefinition;
+               hand_def->name = "";
+               hand_def->wield_image = "wieldhand.png";
+               hand_def->tool_digging_properties = new ToolDiggingProperties;
+               m_item_definitions.insert(std::make_pair("", hand_def));
+
+               ItemDefinition* unknown_def = new ItemDefinition;
+               unknown_def->name = "unknown";
+               m_item_definitions.insert(std::make_pair("unknown", unknown_def));
+
+               ItemDefinition* air_def = new ItemDefinition;
+               air_def->type = ITEM_NODE;
+               air_def->name = "air";
+               m_item_definitions.insert(std::make_pair("air", air_def));
+
+               ItemDefinition* ignore_def = new ItemDefinition;
+               ignore_def->type = ITEM_NODE;
+               ignore_def->name = "ignore";
+               m_item_definitions.insert(std::make_pair("ignore", ignore_def));
+       }
+       virtual void registerItem(const ItemDefinition &def)
+       {
+               infostream<<"ItemDefManager: registering \""<<def.name<<"\""<<std::endl;
+               // Ensure that the "" item (the hand) always has ToolDiggingProperties
+               if(def.name == "")
+                       assert(def.tool_digging_properties != NULL);
+
+               m_item_definitions[def.name] = new ItemDefinition(def);
+
+               // Remove conflicting alias if it exists
+               bool alias_removed = (m_aliases.erase(def.name) != 0);
+               if(alias_removed)
+                       infostream<<"ItemDefManager: erased alias "<<def.name
+                                       <<" because item was defined"<<std::endl;
+       }
+       virtual void registerAlias(const std::string &name,
+                       const std::string &convert_to)
+       {
+               if(m_item_definitions.find(name) == m_item_definitions.end())
+               {
+                       infostream<<"ItemDefManager: setting alias "<<name
+                               <<" -> "<<convert_to<<std::endl;
+                       m_aliases[name] = convert_to;
+               }
+       }
+
+       virtual void updateTexturesAndMeshes(IGameDef *gamedef)
+       {
+#ifndef SERVER
+               infostream<<"ItemDefManager::updateTexturesAndMeshes(): Updating "
+                               <<"textures and meshes in item definitions"<<std::endl;
+
+               ITextureSource *tsrc = gamedef->getTextureSource();
+               INodeDefManager *nodedef = gamedef->getNodeDefManager();
+               IrrlichtDevice *device = tsrc->getDevice();
+               video::IVideoDriver *driver = device->getVideoDriver();
+
+               for(std::map<std::string, ItemDefinition*>::iterator
+                               i = m_item_definitions.begin();
+                               i != m_item_definitions.end(); i++)
+               {
+                       ItemDefinition *def = i->second;
+
+                       bool need_node_mesh = false;
+
+                       // Create an inventory texture
+                       def->inventory_texture = NULL;
+                       if(def->inventory_image != "")
+                       {
+                               def->inventory_texture = tsrc->getTextureRaw(def->inventory_image);
+                       }
+                       else if(def->type == ITEM_NODE)
+                       {
+                               need_node_mesh = true;
+                       }
+
+                       // Create a wield mesh
+                       if(def->wield_mesh != NULL)
+                       {
+                               def->wield_mesh->drop();
+                               def->wield_mesh = NULL;
+                       }
+                       if(def->type == ITEM_NODE && def->wield_image == "")
+                       {
+                               need_node_mesh = true;
+                       }
+                       else if(def->wield_image != "" || def->inventory_image != "")
+                       {
+                               // Extrude the wield image into a mesh
+
+                               std::string imagename;
+                               if(def->wield_image != "")
+                                       imagename = def->wield_image;
+                               else
+                                       imagename = def->inventory_image;
+
+                               def->wield_mesh = createExtrudedMesh(
+                                               tsrc->getTextureRaw(imagename),
+                                               driver,
+                                               def->wield_scale * v3f(40.0, 40.0, 4.0));
+                               if(def->wield_mesh == NULL)
+                               {
+                                       infostream<<"ItemDefManager: WARNING: "
+                                               <<"updateTexturesAndMeshes(): "
+                                               <<"Unable to create extruded mesh for item "
+                                               <<def->name<<std::endl;
+                               }
+                       }
+
+                       if(need_node_mesh)
+                       {
+                               /*
+                                       Get node properties
+                               */
+                               content_t id = nodedef->getId(def->name);
+                               const ContentFeatures &f = nodedef->get(id);
+
+                               /*
+                                       Make a mesh from the node
+                               */
+                               MeshMakeData mesh_make_data;
+                               MapNode mesh_make_node(
+                                       id,
+                                       (f.param_type == CPT_LIGHT) ? 0xee : 0,
+                                       0);
+                               mesh_make_data.fillSingleNode(1000, &mesh_make_node);
+                               scene::IMesh *node_mesh =
+                                       makeMapBlockMesh(&mesh_make_data, gamedef);
+                               setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
+
+                               /*
+                                       Scale and translate the mesh so it's a unit cube
+                                       centered on the origin
+                               */
+                               scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS));
+                               translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0));
+
+                               /*
+                                       Draw node mesh into a render target texture
+                               */
+                               if(def->inventory_texture == NULL && node_mesh != NULL)
+                               {
+                                       core::dimension2d<u32> dim(64,64);
+                                       std::string rtt_texture_name = "INVENTORY_"
+                                               + def->name + "_RTT";
+                                       v3f camera_position(0, 1.0, -1.5);
+                                       camera_position.rotateXZBy(45);
+                                       v3f camera_lookat(0, 0, 0);
+                                       core::CMatrix4<f32> camera_projection_matrix;
+                                       // Set orthogonal projection
+                                       camera_projection_matrix.buildProjectionMatrixOrthoLH(
+                                                       1.65, 1.65, 0, 100);
+
+                                       video::SColorf ambient_light(0.2,0.2,0.2);
+                                       v3f light_position(10, 100, -50);
+                                       video::SColorf light_color(0.5,0.5,0.5);
+                                       f32 light_radius = 1000;
+
+                                       def->inventory_texture = generateTextureFromMesh(
+                                               node_mesh, device, dim, rtt_texture_name,
+                                               camera_position,
+                                               camera_lookat,
+                                               camera_projection_matrix,
+                                               ambient_light,
+                                               light_position,
+                                               light_color,
+                                               light_radius);
+                                       // Note: might have returned NULL
+                               }
+
+                               /*
+                                       Use the node mesh as the wield mesh
+                               */
+                               if(def->wield_mesh == NULL && node_mesh != NULL)
+                               {
+                                       // Scale to proper wield mesh proportions
+                                       scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
+                                                       * def->wield_scale);
+                                       def->wield_mesh = node_mesh;
+                                       def->wield_mesh->grab();
+                               }
+
+
+                               if(node_mesh != NULL)
+                                       node_mesh->drop();
+                       }
+               }
+#endif
+       }
+       void serialize(std::ostream &os)
+       {
+               writeU8(os, 0); // version
+               u16 count = m_item_definitions.size();
+               writeU16(os, count);
+               for(std::map<std::string, ItemDefinition*>::const_iterator
+                               i = m_item_definitions.begin();
+                               i != m_item_definitions.end(); i++)
+               {
+                       ItemDefinition *def = i->second;
+                       // Serialize ItemDefinition and write wrapped in a string
+                       std::ostringstream tmp_os(std::ios::binary);
+                       def->serialize(tmp_os);
+                       os<<serializeString(tmp_os.str());
+               }
+               writeU16(os, m_aliases.size());
+               for(std::map<std::string, std::string>::const_iterator
+                       i = m_aliases.begin(); i != m_aliases.end(); i++)
+               {
+                       os<<serializeString(i->first);
+                       os<<serializeString(i->second);
+               }
+       }
+       void deSerialize(std::istream &is)
+       {
+               // Clear everything
+               clear();
+               // Deserialize
+               int version = readU8(is);
+               if(version != 0)
+                       throw SerializationError("unsupported ItemDefManager version");
+               u16 count = readU16(is);
+               for(u16 i=0; i<count; i++)
+               {
+                       // Deserialize a string and grab an ItemDefinition from it
+                       std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
+                       ItemDefinition def;
+                       def.deSerialize(tmp_is);
+                       // Register
+                       registerItem(def);
+               }
+               u16 num_aliases = readU16(is);
+               for(u16 i=0; i<num_aliases; i++)
+               {
+                       std::string name = deSerializeString(is);
+                       std::string convert_to = deSerializeString(is);
+                       registerAlias(name, convert_to);
+               }
+       }
+private:
+       // Key is name
+       std::map<std::string, ItemDefinition*> m_item_definitions;
+       // Aliases
+       std::map<std::string, std::string> m_aliases;
+};
+
+IWritableItemDefManager* createItemDefManager()
+{
+       return new CItemDefManager();
+}
+
diff --git a/src/itemdef.h b/src/itemdef.h
new file mode 100644 (file)
index 0000000..5a43259
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+Minetest-c55
+Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2011 Kahrl <kahrl@gmx.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 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 General Public License for more details.
+
+You should have received a copy of the GNU 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.
+*/
+
+#ifndef ITEMDEF_HEADER
+#define ITEMDEF_HEADER
+
+#include "common_irrlicht.h"
+#include <string>
+#include <iostream>
+#include <set>
+class IGameDef;
+struct ToolDiggingProperties;
+
+/*
+       Base item definition
+*/
+
+enum ItemType
+{
+       ITEM_NONE,
+       ITEM_NODE,
+       ITEM_CRAFT,
+       ITEM_TOOL,
+};
+
+struct ItemDefinition
+{
+       /*
+               Basic item properties
+       */
+       ItemType type;
+       std::string name; // "" = hand
+       std::string description; // Shown in tooltip.
+
+       /*
+               Visual properties
+       */
+       std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems
+       std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used
+       v3f wield_scale;
+
+       /*
+               Item stack and interaction properties
+       */
+       s16 stack_max;
+       bool usable;
+       bool liquids_pointable;
+       // May be NULL. If non-NULL, deleted by destructor
+       ToolDiggingProperties *tool_digging_properties;
+
+       /*
+               Cached stuff
+       */
+#ifndef SERVER
+       video::ITexture *inventory_texture;
+       scene::IMesh *wield_mesh;
+#endif
+
+       /*
+               Some helpful methods
+       */
+       ItemDefinition();
+       ItemDefinition(const ItemDefinition &def);
+       ItemDefinition& operator=(const ItemDefinition &def);
+       ~ItemDefinition();
+       void reset();
+       void serialize(std::ostream &os) const;
+       void deSerialize(std::istream &is);
+private:
+       void resetInitial();
+};
+
+class IItemDefManager
+{
+public:
+       IItemDefManager(){}
+       virtual ~IItemDefManager(){}
+
+       // Get item definition
+       virtual const ItemDefinition& get(const std::string &name) const=0;
+       // Get alias definition
+       virtual std::string getAlias(const std::string &name) const=0;
+       // Get set of all defined item names and aliases
+       virtual std::set<std::string> getAll() const=0;
+       // Check if item is known
+       virtual bool isKnown(const std::string &name) const=0;
+
+       virtual void serialize(std::ostream &os)=0;
+};
+
+class IWritableItemDefManager : public IItemDefManager
+{
+public:
+       IWritableItemDefManager(){}
+       virtual ~IWritableItemDefManager(){}
+
+       // Get item definition
+       virtual const ItemDefinition& get(const std::string &name) const=0;
+       // Get alias definition
+       virtual std::string getAlias(const std::string &name) const=0;
+       // Get set of all defined item names and aliases
+       virtual std::set<std::string> getAll() const=0;
+       // Check if item is known
+       virtual bool isKnown(const std::string &name) const=0;
+
+       // Remove all registered item and node definitions and aliases
+       // Then re-add the builtin item definitions
+       virtual void clear()=0;
+       // Register item definition
+       virtual void registerItem(const ItemDefinition &def)=0;
+       // Set an alias so that items named <name> will load as <convert_to>.
+       // Alias is not set if <name> has already been defined.
+       // Alias will be removed if <name> is defined at a later point of time.
+       virtual void registerAlias(const std::string &name,
+                       const std::string &convert_to)=0;
+
+       /*
+               Update inventory textures and wield meshes to latest
+               return values of ITextureSource and INodeDefManager.
+               Call after updating the texture atlas of a texture source.
+       */
+       virtual void updateTexturesAndMeshes(IGameDef *gamedef)=0;
+
+       virtual void serialize(std::ostream &os)=0;
+       virtual void deSerialize(std::istream &is)=0;
+};
+
+IWritableItemDefManager* createItemDefManager();
+
+#endif
index 016cb222e0325c0fb86e7abf9028efbc7ab65801..73c13caca258d11572e53819b84c430688b31b46 100644 (file)
@@ -30,6 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "nameidmapping.h"
 #include "content_mapnode.h" // For legacy name-id mapping
+#ifndef SERVER
+#include "mapblock_mesh.h"
+#endif
 
 /*
        MapBlock
index aa5aa43f735e82168d186c986e9e75d83ae6c306..f2d94753c9fba586f5f8bff26d6fe20220acfc4f 100644 (file)
@@ -32,9 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "voxel.h"
 #include "staticobject.h"
 #include "mapblock_nodemod.h"
-#ifndef SERVER
-       #include "mapblock_mesh.h"
-#endif
 #include "modifiedstate.h"
 
 class Map;
index c051dda5046b816e630aeca69d1124db45a254cb..db9057e193ecf217618bde45d1dd72c4a5173ed9 100644 (file)
@@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "profiler.h"
 #include "nodedef.h"
-#include "tile.h"
 #include "gamedef.h"
 #include "content_mapblock.h"
 #include "mineral.h" // For mineral_block_texture
@@ -83,6 +82,39 @@ void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
        }
 }
 
+void MeshMakeData::fillSingleNode(u32 daynight_ratio, MapNode *node)
+{
+       m_daynight_ratio = daynight_ratio;
+       m_blockpos = v3s16(0,0,0);
+       m_temp_mods.clear();
+       
+       v3s16 blockpos_nodes = v3s16(0,0,0);
+       VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
+                       blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
+       s32 volume = area.getVolume();
+       s32 our_node_index = area.index(1,1,1);
+
+       // Allocate this block + neighbors
+       m_vmanip.clear();
+       m_vmanip.addArea(area);
+
+       // Fill in data
+       MapNode *data = new MapNode[volume];
+       for(s32 i = 0; i < volume; i++)
+       {
+               if(i == our_node_index)
+               {
+                       data[i] = *node;
+               }
+               else
+               {
+                       data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
+               }
+       }
+       m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
+       delete[] data;
+}
+
 /*
        vertex_dirs: v3s16[4]
 */
@@ -207,8 +239,8 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
        else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
        else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
 
-       v3f zerovector = v3f(0,0,0);
-       
+       v3f normal(dir.X, dir.Y, dir.Z);
+
        u8 alpha = tile.alpha;
        /*u8 alpha = 255;
        if(tile.id == TILE_WATER)
@@ -230,16 +262,16 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
        face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
                        core::vector2d<f32>(x0+w*abs_scale, y0));*/
 
-       face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
+       face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
                        MapBlock_LightColor(alpha, li0),
                        core::vector2d<f32>(x0+w*abs_scale, y0+h));
-       face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
+       face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
                        MapBlock_LightColor(alpha, li1),
                        core::vector2d<f32>(x0, y0+h));
-       face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
+       face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
                        MapBlock_LightColor(alpha, li2),
                        core::vector2d<f32>(x0, y0));
-       face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
+       face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
                        MapBlock_LightColor(alpha, li3),
                        core::vector2d<f32>(x0+w*abs_scale, y0));
 
@@ -309,8 +341,8 @@ static TileSpec getTile(const MapNode &node, v3s16 dir,
        Gets node tile from any place relative to block.
        Returns TILE_NODE if doesn't exist or should not be drawn.
 */
-static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
-               NodeModMap &temp_mods, ITextureSource *tsrc, INodeDefManager *ndef)
+TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+               NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef)
 {
        TileSpec spec;
        spec = getTile(mn, face_dir, tsrc, ndef);
@@ -325,13 +357,15 @@ static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
        {
                struct NodeMod mod = n->getValue();*/
        NodeMod mod;
-       if(temp_mods.get(p, &mod))
+       if(temp_mods && temp_mods->get(p, &mod))
        {
+               #if 0  // NODEMOD_CHANGECONTENT isn't used at the moment
                if(mod.type == NODEMOD_CHANGECONTENT)
                {
                        MapNode mn2(mod.param);
                        spec = getTile(mn2, face_dir, tsrc, ndef);
                }
+               #endif
                if(mod.type == NODEMOD_CRACK)
                {
                        /*
@@ -361,19 +395,14 @@ static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
        return spec;
 }
 
-static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
+static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap *temp_mods)
 {
        /*
                Check temporary modifications on this node
        */
-       /*core::map<v3s16, NodeMod>::Node *n;
-       n = m_temp_mods.find(p);
-       // If modified
-       if(n != NULL)
-       {
-               struct NodeMod mod = n->getValue();*/
+       #if 0  // NODEMOD_CHANGECONTENT isn't used at the moment
        NodeMod mod;
-       if(temp_mods.get(p, &mod))
+       if(temp_mods && temp_mods->get(p, &mod))
        {
                if(mod.type == NODEMOD_CHANGECONTENT)
                {
@@ -395,6 +424,7 @@ static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
                        */
                }
        }
+       #endif
 
        return mn.getContent();
 }
@@ -469,7 +499,7 @@ static void getTileInfo(
                v3s16 face_dir,
                u32 daynight_ratio,
                VoxelManipulator &vmanip,
-               NodeModMap &temp_mods,
+               NodeModMap *temp_mods,
                bool smooth_lighting,
                IGameDef *gamedef,
                // Output:
@@ -553,7 +583,7 @@ static void updateFastFaceRow(
                v3s16 face_dir,
                v3f face_dir_f,
                core::array<FastFace> &dest,
-               NodeModMap &temp_mods,
+               NodeModMap *temp_mods,
                VoxelManipulator &vmanip,
                v3s16 blockpos_nodes,
                bool smooth_lighting,
@@ -749,7 +779,7 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
                                                v3s16(0,1,0), //face dir
                                                v3f  (0,1,0),
                                                fastfaces_new,
-                                               data->m_temp_mods,
+                                               &data->m_temp_mods,
                                                data->m_vmanip,
                                                blockpos_nodes,
                                                smooth_lighting,
@@ -768,7 +798,7 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
                                                v3s16(1,0,0),
                                                v3f  (1,0,0),
                                                fastfaces_new,
-                                               data->m_temp_mods,
+                                               &data->m_temp_mods,
                                                data->m_vmanip,
                                                blockpos_nodes,
                                                smooth_lighting,
@@ -787,7 +817,7 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
                                                v3s16(0,0,1),
                                                v3f  (0,0,1),
                                                fastfaces_new,
-                                               data->m_temp_mods,
+                                               &data->m_temp_mods,
                                                data->m_vmanip,
                                                blockpos_nodes,
                                                smooth_lighting,
index 36cc9be24d271c6fc6222aa0df9139f9af08e8d3..4d3e7d29d1d73232bab8a3b5324a6241e6c95e72 100644 (file)
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "common_irrlicht.h"
 #include "mapblock_nodemod.h"
+#include "tile.h"
 #include "voxel.h"
 
 class IGameDef;
@@ -125,6 +126,8 @@ private:
 
 // Helper functions
 video::SColor MapBlock_LightColor(u8 alpha, u8 light);
+TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+               NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef);
 
 class MapBlock;
 
@@ -140,6 +143,11 @@ struct MeshMakeData
                parent of block.
        */
        void fill(u32 daynight_ratio, MapBlock *block);
+
+       /*
+               Set up with only a single node at (1,1,1)
+       */
+       void fillSingleNode(u32 daynight_ratio, MapNode *node);
 };
 
 // This is the highest-level function in here
index d2d37ebf46ceddb10e99bde1c8b8e7272871d45f..146209d5881cd5489d4c9b487a21ef31f04815e6 100644 (file)
@@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "materials.h"
 #include "mapnode.h"
 #include "nodedef.h"
-#include "tooldef.h"
 #include "utility.h"
 
 void MaterialProperties::serialize(std::ostream &os)
@@ -49,6 +48,56 @@ void MaterialProperties::deSerialize(std::istream &is)
        flammability = readF1000(is);
 }
 
+ToolDiggingProperties::ToolDiggingProperties(float full_punch_interval_,
+               float a, float b, float c, float d, float e,
+               float f, float g, float h, float i, float j):
+       full_punch_interval(full_punch_interval_),
+       basetime(a),
+       dt_weight(b),
+       dt_crackiness(c),
+       dt_crumbliness(d),
+       dt_cuttability(e),
+       basedurability(f),
+       dd_weight(g),
+       dd_crackiness(h),
+       dd_crumbliness(i),
+       dd_cuttability(j)
+{}
+
+void ToolDiggingProperties::serialize(std::ostream &os)
+{
+       writeU8(os, 0); // version
+       writeF1000(os, full_punch_interval);
+       writeF1000(os, basetime);
+       writeF1000(os, dt_weight);
+       writeF1000(os, dt_crackiness);
+       writeF1000(os, dt_crumbliness);
+       writeF1000(os, dt_cuttability);
+       writeF1000(os, basedurability);
+       writeF1000(os, dd_weight);
+       writeF1000(os, dd_crackiness);
+       writeF1000(os, dd_crumbliness);
+       writeF1000(os, dd_cuttability);
+}
+
+void ToolDiggingProperties::deSerialize(std::istream &is)
+{
+       int version = readU8(is);
+       if(version != 0) throw SerializationError(
+                       "unsupported ToolDiggingProperties version");
+       full_punch_interval = readF1000(is);
+       basetime = readF1000(is);
+       dt_weight = readF1000(is);
+       dt_crackiness = readF1000(is);
+       dt_crumbliness = readF1000(is);
+       dt_cuttability = readF1000(is);
+       basedurability = readF1000(is);
+       dd_weight = readF1000(is);
+       dd_crackiness = readF1000(is);
+       dd_crumbliness = readF1000(is);
+       dd_cuttability = readF1000(is);
+}
+
 DiggingProperties getDiggingProperties(const MaterialProperties *mp,
                const ToolDiggingProperties *tp, float time_from_last_punch)
 {
index b25d047beb9831060268d46a7e6a78d7d9a34d70..face7d5df7277784bc6337a4b66147cd45424c91 100644 (file)
@@ -72,6 +72,29 @@ struct MaterialProperties
        void deSerialize(std::istream &is);
 };
 
+struct ToolDiggingProperties
+{
+       // time = basetime + sum(feature here * feature in MaterialProperties)
+       float full_punch_interval;
+       float basetime;
+       float dt_weight;
+       float dt_crackiness;
+       float dt_crumbliness;
+       float dt_cuttability;
+       float basedurability;
+       float dd_weight;
+       float dd_crackiness;
+       float dd_crumbliness;
+       float dd_cuttability;
+
+       ToolDiggingProperties(float full_punch_interval_=2.0,
+                       float a=0.75, float b=0, float c=0, float d=0, float e=0,
+                       float f=50, float g=0, float h=0, float i=0, float j=0);
+
+       void serialize(std::ostream &os);
+       void deSerialize(std::istream &is);
+};
+
 struct DiggingProperties
 {
        bool diggable;
@@ -87,7 +110,6 @@ struct DiggingProperties
        {}
 };
 
-struct ToolDiggingProperties;
 class INodeDefManager;
 
 DiggingProperties getDiggingProperties(const MaterialProperties *mp,
index 1d347a09f7678cc153c786b92fdcebca7096aac6..5afb4af592ba7306831212290895eb5523a15a85 100644 (file)
@@ -18,8 +18,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "mesh.h"
+#include "log.h"
+#include <cassert>
+#include <iostream>
 #include <IAnimatedMesh.h>
 #include <SAnimatedMesh.h>
+#include <ICameraSceneNode.h>
 
 // In Irrlicht 1.8 the signature of ITexture::lock was changed from
 // (bool, u32) to (E_TEXTURE_LOCK_MODE, u32).
@@ -73,9 +77,15 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale)
        {
                scene::IMeshBuffer *buf = new scene::SMeshBuffer();
                buf->append(vertices + 4 * i, 4, indices, 6);
+               // Set default material
+               buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
+               buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
+               buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+               // Add mesh buffer to mesh
                mesh->addMeshBuffer(buf);
                buf->drop();
        }
+
        scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh);
        mesh->drop();
        scaleMesh(anim_mesh, scale);  // also recalculates bounding box
@@ -280,6 +290,13 @@ scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture,
                }
                img1->drop();
        }
+
+       // Set default material
+       mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture);
+       mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_LIGHTING, false);
+       mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
+       mesh->getMeshBuffer(0)->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
        scaleMesh(mesh, scale);  // also recalculates bounding box
        return mesh;
 }
@@ -313,6 +330,35 @@ void scaleMesh(scene::IMesh *mesh, v3f scale)
        mesh->setBoundingBox(bbox);
 }
 
+void translateMesh(scene::IMesh *mesh, v3f vec)
+{
+       if(mesh == NULL)
+               return;
+
+       core::aabbox3d<f32> bbox;
+       bbox.reset(0,0,0);
+
+       u16 mc = mesh->getMeshBufferCount();
+       for(u16 j=0; j<mc; j++)
+       {
+               scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
+               video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
+               u16 vc = buf->getVertexCount();
+               for(u16 i=0; i<vc; i++)
+               {
+                       vertices[i].Pos += vec;
+               }
+               buf->recalculateBoundingBox();
+
+               // calculate total bounding box
+               if(j == 0)
+                       bbox = buf->getBoundingBox();
+               else
+                       bbox.addInternalBox(buf->getBoundingBox());
+       }
+       mesh->setBoundingBox(bbox);
+}
+
 void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
 {
        if(mesh == NULL)
@@ -360,3 +406,74 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh,
                }
        }
 }
+
+video::ITexture *generateTextureFromMesh(scene::IMesh *mesh,
+               IrrlichtDevice *device,
+               core::dimension2d<u32> dim,
+               std::string texture_name,
+               v3f camera_position,
+               v3f camera_lookat,
+               core::CMatrix4<f32> camera_projection_matrix,
+               video::SColorf ambient_light,
+               v3f light_position,
+               video::SColorf light_color,
+               f32 light_radius)
+{
+       video::IVideoDriver *driver = device->getVideoDriver();
+       if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
+       {
+               errorstream<<"generateTextureFromMesh(): EVDF_RENDER_TO_TARGET"
+                               " not supported."<<std::endl;
+               return NULL;
+       }
+
+       // Create render target texture
+       video::ITexture *rtt = driver->addRenderTargetTexture(
+                       dim, texture_name.c_str(), video::ECF_A8R8G8B8);
+       if(rtt == NULL)
+       {
+               errorstream<<"generateTextureFromMesh(): addRenderTargetTexture"
+                               " returned NULL."<<std::endl;
+               return NULL;
+       }
+
+       // Set render target
+       driver->setRenderTarget(rtt, true, true, video::SColor(0,0,0,0));
+
+       // Get a scene manager
+       scene::ISceneManager *smgr_main = device->getSceneManager();
+       assert(smgr_main);
+       scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
+       assert(smgr);
+
+       scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
+       meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
+       meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
+       meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
+
+       scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
+                       camera_position, camera_lookat);
+       // second parameter of setProjectionMatrix (isOrthogonal) is ignored
+       camera->setProjectionMatrix(camera_projection_matrix, false);
+
+       smgr->setAmbientLight(ambient_light);
+       smgr->addLightSceneNode(0, light_position, light_color, light_radius);
+
+       // Render scene
+       driver->beginScene(true, true, video::SColor(0,0,0,0));
+       smgr->drawAll();
+       driver->endScene();
+
+       // NOTE: The scene nodes should not be dropped, otherwise
+       //       smgr->drop() segfaults
+       /*cube->drop();
+       camera->drop();
+       light->drop();*/
+       // Drop scene manager
+       smgr->drop();
+
+       // Unset render target
+       driver->setRenderTarget(0, true, true, 0);
+
+       return rtt;
+}
index 33e072927fd1850780202d23938a25df5dceb125..57166235ebf834391bc1cd252a38117d776df812 100644 (file)
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define MESH_HEADER
 
 #include "common_irrlicht.h"
+#include <string>
 
 /*
        Create a new cube mesh.
@@ -47,6 +48,11 @@ scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture,
 */
 void scaleMesh(scene::IMesh *mesh, v3f scale);
 
+/*
+       Translate each vertex coordinate by the specified vector.
+*/
+void translateMesh(scene::IMesh *mesh, v3f vec);
+
 /*
        Set a constant color for all vertices in the mesh
 */
@@ -63,4 +69,20 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh,
                const video::SColor &colorY,
                const video::SColor &colorZ);
 
+/*
+       Render a mesh to a texture.
+       Returns NULL if render-to-texture failed.
+*/
+video::ITexture *generateTextureFromMesh(scene::IMesh *mesh,
+               IrrlichtDevice *device,
+               core::dimension2d<u32> dim,
+               std::string texture_name,
+               v3f camera_position,
+               v3f camera_lookat,
+               core::CMatrix4<f32> camera_projection_matrix,
+               video::SColorf ambient_light,
+               v3f light_position,
+               video::SColorf light_color,
+               f32 light_radius);
+
 #endif
index 038251fa3a452f0936359cae5c78daadb89970ad..4e495fce63dbf4cd45109e4f8cd25882a4e374a4 100644 (file)
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "mineral.h"
+#include "gamedef.h"
 
 
 const char *mineral_filenames[MINERAL_COUNT] =
@@ -47,5 +48,15 @@ std::string mineral_block_texture(u8 mineral)
        return mineral_textures[mineral];
 }
 
+ItemStack getDiggedMineralItem(u8 mineral, IGameDef *gamedef)
+{
+       if(mineral == MINERAL_COAL)
+               return ItemStack("default:coal_lump", 1, 0, "", gamedef->idef());
+       else if(mineral == MINERAL_IRON)
+               return ItemStack("default:iron_lump", 1, 0, "", gamedef->idef());
+       else
+               return ItemStack();
+}
+
 
 
index 4949fe07ec42cbc6ecd5a15de21ffb06f6e7b1d8..a945d0e0242073dd811532c074af3da74334740e 100644 (file)
@@ -38,19 +38,10 @@ void init_mineral();
 
 #define MINERAL_COUNT 3
 
-std::string mineral_block_texture(u8 mineral);
-
 class IGameDef;
 
-inline CraftItem * getDiggedMineralItem(u8 mineral, IGameDef *gamedef)
-{
-       if(mineral == MINERAL_COAL)
-               return new CraftItem(gamedef, "lump_of_coal", 1);
-       else if(mineral == MINERAL_IRON)
-               return new CraftItem(gamedef, "lump_of_iron", 1);
-
-       return NULL;
-}
+std::string mineral_block_texture(u8 mineral);
+ItemStack getDiggedMineralItem(u8 mineral, IGameDef *gamedef);
 
 #endif
 
index d7769700b90762139ba4de75f4b3e729e5893e36..87469bfb52726dc87524c2bd4b3e166a3cf3980a 100644 (file)
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodedef.h"
 
 #include "main.h" // For g_settings
-#include "nodemetadata.h"
+#include "itemdef.h"
 #ifndef SERVER
 #include "tile.h"
 #endif
@@ -103,8 +103,6 @@ void ContentFeatures::reset()
                Cached stuff
        */
 #ifndef SERVER
-       inventory_texture = NULL;
-       
        for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
                special_materials[j] = NULL;
                special_aps[j] = NULL;
@@ -113,7 +111,6 @@ void ContentFeatures::reset()
        visual_solidness = 0;
        backface_culling = true;
 #endif
-       used_texturenames.clear();
        /*
                Actual data
                
@@ -127,7 +124,6 @@ void ContentFeatures::reset()
                tname_tiles[i] = "";
        for(u16 j=0; j<CF_SPECIAL_COUNT; j++)
                mspec_special[j] = MaterialSpec();
-       tname_inventory = "";
        alpha = 255;
        post_effect_color = video::SColor(0, 0, 0, 0);
        param_type = CPT_NONE;
@@ -140,7 +136,6 @@ void ContentFeatures::reset()
        climbable = false;
        buildable_to = false;
        wall_mounted = false;
-       often_contains_mineral = false;
        dug_item = "";
        extra_dug_item = "";
        extra_dug_item_rarity = 2;
@@ -153,21 +148,20 @@ void ContentFeatures::reset()
        damage_per_second = 0;
        selection_box = NodeBox();
        material = MaterialProperties();
-       cookresult_item = ""; // Cannot be cooked
-       furnace_cooktime = 3.0;
-       furnace_burntime = -1.0; // Cannot be burned
+       // Make unknown blocks diggable
+       material.diggability = DIGGABLE_CONSTANT;
+       material.constant_time = 0.5;
 }
 
 void ContentFeatures::serialize(std::ostream &os)
 {
-       writeU8(os, 0); // version
+       writeU8(os, 1); // version
        os<<serializeString(name);
        writeU8(os, drawtype);
        writeF1000(os, visual_scale);
        writeU8(os, 6);
        for(u32 i=0; i<6; i++)
                os<<serializeString(tname_tiles[i]);
-       os<<serializeString(tname_inventory);
        writeU8(os, CF_SPECIAL_COUNT);
        for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
                mspec_special[i].serialize(os);
@@ -187,7 +181,6 @@ void ContentFeatures::serialize(std::ostream &os)
        writeU8(os, climbable);
        writeU8(os, buildable_to);
        writeU8(os, wall_mounted);
-       writeU8(os, often_contains_mineral);
        os<<serializeString(dug_item);
        os<<serializeString(extra_dug_item);
        writeS32(os, extra_dug_item_rarity);
@@ -200,15 +193,12 @@ void ContentFeatures::serialize(std::ostream &os)
        writeU32(os, damage_per_second);
        selection_box.serialize(os);
        material.serialize(os);
-       os<<serializeString(cookresult_item);
-       writeF1000(os, furnace_cooktime);
-       writeF1000(os, furnace_burntime);
 }
 
-void ContentFeatures::deSerialize(std::istream &is, IGameDef *gamedef)
+void ContentFeatures::deSerialize(std::istream &is)
 {
        int version = readU8(is);
-       if(version != 0)
+       if(version != 1)
                throw SerializationError("unsupported ContentFeatures version");
        name = deSerializeString(is);
        drawtype = (enum NodeDrawType)readU8(is);
@@ -216,9 +206,7 @@ void ContentFeatures::deSerialize(std::istream &is, IGameDef *gamedef)
        if(readU8(is) != 6)
                throw SerializationError("unsupported tile count");
        for(u32 i=0; i<6; i++)
-               setTexture(i, deSerializeString(is));
-               //tname_tiles[i] = deSerializeString(is);
-       tname_inventory = deSerializeString(is);
+               tname_tiles[i] = deSerializeString(is);
        if(readU8(is) != CF_SPECIAL_COUNT)
                throw SerializationError("unsupported CF_SPECIAL_COUNT");
        for(u32 i=0; i<CF_SPECIAL_COUNT; i++){
@@ -239,7 +227,6 @@ void ContentFeatures::deSerialize(std::istream &is, IGameDef *gamedef)
        climbable = readU8(is);
        buildable_to = readU8(is);
        wall_mounted = readU8(is);
-       often_contains_mineral = readU8(is);
        dug_item = deSerializeString(is);
        extra_dug_item = deSerializeString(is);
        extra_dug_item_rarity = readS32(is);
@@ -252,53 +239,6 @@ void ContentFeatures::deSerialize(std::istream &is, IGameDef *gamedef)
        damage_per_second = readU32(is);
        selection_box.deSerialize(is);
        material.deSerialize(is);
-       cookresult_item = deSerializeString(is);
-       furnace_cooktime = readF1000(is);
-       furnace_burntime = readF1000(is);
-}
-
-void ContentFeatures::setTexture(u16 i, std::string name)
-{
-       used_texturenames.insert(name);
-       tname_tiles[i] = name;
-       if(tname_inventory == "")
-               tname_inventory = name;
-}
-
-void ContentFeatures::setAllTextures(std::string name)
-{
-       for(u16 i=0; i<6; i++)
-               setTexture(i, name);
-       // Force inventory texture too
-       setInventoryTexture(name);
-}
-
-void ContentFeatures::setSpecialMaterial(u16 i, const MaterialSpec &mspec)
-{
-       assert(i < CF_SPECIAL_COUNT);
-       mspec_special[i] = mspec;
-}
-
-void ContentFeatures::setInventoryTexture(std::string imgname)
-{
-       tname_inventory = imgname;
-}
-
-void ContentFeatures::setInventoryTextureCube(std::string top,
-               std::string left, std::string right)
-{
-       str_replace_char(top, '^', '&');
-       str_replace_char(left, '^', '&');
-       str_replace_char(right, '^', '&');
-
-       std::string imgname_full;
-       imgname_full += "[inventorycube{";
-       imgname_full += top;
-       imgname_full += "{";
-       imgname_full += left;
-       imgname_full += "{";
-       imgname_full += right;
-       tname_inventory = imgname_full;
 }
 
 /*
@@ -311,14 +251,12 @@ public:
        void clear()
        {
                m_name_id_mapping.clear();
+               m_name_id_mapping_with_aliases.clear();
 
-               m_aliases.clear();
-               
                for(u16 i=0; i<=MAX_CONTENT; i++)
                {
                        ContentFeatures &f = m_content_features[i];
                        f.reset(); // Reset to defaults
-                       f.setAllTextures("unknown_block.png");
                }
                
                // Set CONTENT_AIR
@@ -336,7 +274,7 @@ public:
                        // Insert directly into containers
                        content_t c = CONTENT_AIR;
                        m_content_features[c] = f;
-                       m_name_id_mapping.set(c, f.name);
+                       addNameIdMapping(c, f.name);
                }
                // Set CONTENT_IGNORE
                {
@@ -354,7 +292,7 @@ public:
                        // Insert directly into containers
                        content_t c = CONTENT_IGNORE;
                        m_content_features[c] = f;
-                       m_name_id_mapping.set(c, f.name);
+                       addNameIdMapping(c, f.name);
                }
        }
        // CONTENT_IGNORE = not found
@@ -401,12 +339,14 @@ public:
        {
                return get(n.getContent());
        }
-       virtual bool getId(const std::string &name_, content_t &result) const
+       virtual bool getId(const std::string &name, content_t &result) const
        {
-               // Convert name according to possible alias
-               std::string name = getAlias(name_);
-               // Get id
-               return m_name_id_mapping.getId(name, result);
+               std::map<std::string, content_t>::const_iterator
+                       i = m_name_id_mapping_with_aliases.find(name);
+               if(i == m_name_id_mapping_with_aliases.end())
+                       return false;
+               result = i->second;
+               return true;
        }
        virtual content_t getId(const std::string &name) const
        {
@@ -420,14 +360,6 @@ public:
                getId(name, id);
                return get(id);
        }
-       virtual std::string getAlias(const std::string &name) const
-       {
-               std::map<std::string, std::string>::const_iterator i;
-               i = m_aliases.find(name);
-               if(i != m_aliases.end())
-                       return i->second;
-               return name;
-       }
        // IWritableNodeDefManager
        virtual void set(content_t c, const ContentFeatures &def)
        {
@@ -451,20 +383,14 @@ public:
                }
                m_content_features[c] = def;
                if(def.name != "")
-                       m_name_id_mapping.set(c, def.name);
-
-               // Remove conflicting alias if it exists
-               bool alias_removed = (m_aliases.erase(def.name) != 0);
-               if(alias_removed)
-                       infostream<<"ndef: erased alias "<<def.name
-                                       <<" because node was defined"<<std::endl;
+                       addNameIdMapping(c, def.name);
        }
        virtual content_t set(const std::string &name,
                        const ContentFeatures &def)
        {
                assert(name == def.name);
                u16 id = CONTENT_IGNORE;
-               bool found = m_name_id_mapping.getId(name, id);
+               bool found = m_name_id_mapping.getId(name, id);  // ignore aliases
                if(!found){
                        // Determine if full param2 is required
                        bool require_full_param2 = (
@@ -481,7 +407,7 @@ public:
                        if(id == CONTENT_IGNORE)
                                return CONTENT_IGNORE;
                        if(name != "")
-                               m_name_id_mapping.set(id, name);
+                               addNameIdMapping(id, name);
                }
                set(id, def);
                return id;
@@ -491,23 +417,27 @@ public:
                assert(name != "");
                ContentFeatures f;
                f.name = name;
-               f.setAllTextures("unknown_block.png");
                // Make unknown blocks diggable
-               f.material.diggability = DIGGABLE_NORMAL;
+               f.material.diggability = DIGGABLE_CONSTANT;
+               f.material.constant_time = 0.5;
                return set(name, f);
        }
-       virtual void setAlias(const std::string &name,
-                       const std::string &convert_to)
+       virtual void updateAliases(IItemDefManager *idef)
        {
-               content_t id;
-               if(getId(name, id)){
-                       infostream<<"ndef: not setting alias "<<name<<" -> "<<convert_to
-                                       <<": "<<name<<" is already defined"<<std::endl;
-                       return;
+               std::set<std::string> all = idef->getAll();
+               m_name_id_mapping_with_aliases.clear();
+               for(std::set<std::string>::iterator
+                               i = all.begin(); i != all.end(); i++)
+               {
+                       std::string name = *i;
+                       std::string convert_to = idef->getAlias(name);
+                       content_t id;
+                       if(m_name_id_mapping.getId(convert_to, id))
+                       {
+                               m_name_id_mapping_with_aliases.insert(
+                                               std::make_pair(name, id));
+                       }
                }
-               infostream<<"ndef: setting alias "<<name<<" -> "<<convert_to
-                               <<std::endl;
-               m_aliases[name] = convert_to;
        }
        virtual void updateTextures(ITextureSource *tsrc)
        {
@@ -523,6 +453,14 @@ public:
                {
                        ContentFeatures *f = &m_content_features[i];
 
+                       std::string tname_tiles[6];
+                       for(u32 j=0; j<6; j++)
+                       {
+                               tname_tiles[j] = f->tname_tiles[j];
+                               if(tname_tiles[j] == "")
+                                       tname_tiles[j] = "unknown_block.png";
+                        }
+
                        switch(f->drawtype){
                        default:
                        case NDT_NORMAL:
@@ -567,8 +505,7 @@ public:
                                        f->drawtype = NDT_NORMAL;
                                        f->solidness = 2;
                                        for(u32 i=0; i<6; i++){
-                                               f->setTexture(i, f->tname_tiles[i]
-                                                               + std::string("^[noalpha"));
+                                               tname_tiles[i] += std::string("^[noalpha");
                                        }
                                }
                                break;
@@ -581,16 +518,9 @@ public:
                                break;
                        }
 
-                       // Inventory texture
-                       if(f->tname_inventory != "")
-                               f->inventory_texture = tsrc->getTextureRaw(f->tname_inventory);
-                       else
-                               f->inventory_texture = NULL;
                        // Tile textures
                        for(u16 j=0; j<6; j++){
-                               if(f->tname_tiles[j] == "")
-                                       continue;
-                               f->tiles[j].texture = tsrc->getTexture(f->tname_tiles[j]);
+                               f->tiles[j].texture = tsrc->getTexture(tname_tiles[j]);
                                f->tiles[j].alpha = f->alpha;
                                if(f->alpha == 255)
                                        f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
@@ -649,16 +579,8 @@ public:
                }
                writeU16(os, count);
                os<<serializeLongString(tmp_os.str());
-
-               writeU16(os, m_aliases.size());
-               for(std::map<std::string, std::string>::const_iterator
-                               i = m_aliases.begin(); i != m_aliases.end(); i++)
-               {
-                       os<<serializeString(i->first);
-                       os<<serializeString(i->second);
-               }
        }
-       void deSerialize(std::istream &is, IGameDef *gamedef)
+       void deSerialize(std::istream &is)
        {
                clear();
                u16 count = readU16(is);
@@ -674,27 +596,26 @@ public:
                        if(i == CONTENT_IGNORE || i == CONTENT_AIR)
                                continue;*/
                        ContentFeatures *f = &m_content_features[i];
-                       f->deSerialize(tmp_is, gamedef);
+                       f->deSerialize(tmp_is);
                        if(f->name != "")
-                               m_name_id_mapping.set(i, f->name);
-               }
-
-               u16 num_aliases = readU16(is);
-               if(!is.eof()){
-                       for(u16 i=0; i<num_aliases; i++){
-                               std::string name = deSerializeString(is);
-                               std::string convert_to = deSerializeString(is);
-                               m_aliases[name] = convert_to;
-                       }
+                               addNameIdMapping(i, f->name);
                }
        }
+private:
+       void addNameIdMapping(content_t i, std::string name)
+       {
+               m_name_id_mapping.set(i, name);
+               m_name_id_mapping_with_aliases.insert(std::make_pair(name, i));
+       }
 private:
        // Features indexed by id
        ContentFeatures m_content_features[MAX_CONTENT+1];
        // A mapping for fast converting back and forth between names and ids
        NameIdMapping m_name_id_mapping;
-       // Aliases
-       std::map<std::string, std::string> m_aliases;
+       // Like m_name_id_mapping, but only from names to ids, and includes
+       // item aliases too. Updated by updateAliases()
+       // Note: Not serialized.
+       std::map<std::string, content_t> m_name_id_mapping_with_aliases;
 };
 
 IWritableNodeDefManager* createNodeDefManager()
index fdf2f8c45b6e1d1525b4f8f81e88e2aa250d0e13..598ad7fb391555eeac7f122189caf479e0fc89b4 100644 (file)
@@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "tile.h"
 #endif
 #include "materials.h" // MaterialProperties
+class IItemDefManager;
 class ITextureSource;
 class IGameDef;
 
@@ -124,7 +125,6 @@ struct ContentFeatures
        // 0     1     2     3     4     5
        // up    down  right left  back  front 
        TileSpec tiles[6];
-       video::ITexture *inventory_texture;
        // Special material/texture
        // - Currently used for flowing liquids
        video::SMaterial *special_materials[CF_SPECIAL_COUNT];
@@ -133,11 +133,7 @@ struct ContentFeatures
        u8 visual_solidness; // When solidness=0, this tells how it looks like
        bool backface_culling;
 #endif
-       
-       // List of textures that are used and are wanted to be included in
-       // the texture atlas
-       std::set<std::string> used_texturenames;
-       
+
        /*
                Actual data
        */
@@ -148,7 +144,6 @@ struct ContentFeatures
        enum NodeDrawType drawtype;
        float visual_scale; // Misc. scale parameter
        std::string tname_tiles[6];
-       std::string tname_inventory;
        MaterialSpec mspec_special[CF_SPECIAL_COUNT]; // Use setter methods
        u8 alpha;
 
@@ -174,10 +169,6 @@ struct ContentFeatures
        // If true, param2 is set to direction when placed. Used for torches.
        // NOTE: the direction format is quite inefficient and should be changed
        bool wall_mounted;
-       // Whether this content type often contains mineral.
-       // Used for texture atlas creation.
-       // Currently only enabled for CONTENT_STONE.
-       bool often_contains_mineral;
        // Inventory item string as which the node appears in inventory when dug.
        // Mineral overrides this.
        std::string dug_item;
@@ -202,9 +193,6 @@ struct ContentFeatures
        u32 damage_per_second;
        NodeBox selection_box;
        MaterialProperties material;
-       std::string cookresult_item;
-       float furnace_cooktime;
-       float furnace_burntime;
 
        /*
                Methods
@@ -214,22 +202,8 @@ struct ContentFeatures
        ~ContentFeatures();
        void reset();
        void serialize(std::ostream &os);
-       void deSerialize(std::istream &is, IGameDef *gamedef);
+       void deSerialize(std::istream &is);
 
-       /*
-               Texture setters.
-               
-       */
-       
-       // Texture setters. They also add stuff to used_texturenames.
-       void setTexture(u16 i, std::string name);
-       void setAllTextures(std::string name);
-       void setSpecialMaterial(u16 i, const MaterialSpec &mspec);
-
-       void setInventoryTexture(std::string imgname);
-       void setInventoryTextureCube(std::string top,
-                       std::string left, std::string right);
-       
        /*
                Some handy methods
        */
@@ -253,7 +227,6 @@ public:
        virtual bool getId(const std::string &name, content_t &result) const=0;
        virtual content_t getId(const std::string &name) const=0;
        virtual const ContentFeatures& get(const std::string &name) const=0;
-       virtual std::string getAlias(const std::string &name) const =0;
        
        virtual void serialize(std::ostream &os)=0;
 };
@@ -271,8 +244,7 @@ public:
        virtual content_t getId(const std::string &name) const=0;
        // If not found, returns the features of CONTENT_IGNORE
        virtual const ContentFeatures& get(const std::string &name) const=0;
-       virtual std::string getAlias(const std::string &name) const =0;
-               
+
        // Register node definition
        virtual void set(content_t c, const ContentFeatures &def)=0;
        // Register node definition by name (allocate an id)
@@ -281,11 +253,12 @@ public:
                        const ContentFeatures &def)=0;
        // If returns CONTENT_IGNORE, could not allocate id
        virtual content_t allocateDummy(const std::string &name)=0;
-       // Set an alias so that nodes named <name> will load as <convert_to>.
-       // Alias is not set if <name> has already been defined.
-       // Alias will be removed if <name> is defined at a later point of time.
-       virtual void setAlias(const std::string &name,
-                       const std::string &convert_to)=0;
+
+       /*
+               Update item alias mapping.
+               Call after updating item definitions.
+       */
+       virtual void updateAliases(IItemDefManager *idef)=0;
 
        /*
                Update tile textures to latest return values of TextueSource.
@@ -294,7 +267,7 @@ public:
        virtual void updateTextures(ITextureSource *tsrc)=0;
 
        virtual void serialize(std::ostream &os)=0;
-       virtual void deSerialize(std::istream &is, IGameDef *gamedef)=0;
+       virtual void deSerialize(std::istream &is)=0;
 };
 
 IWritableNodeDefManager* createNodeDefManager();
index 963f67c282542265e63ea816a22d3986816225ea..b072746c6241da064845ad72ed7c8b8ab83a8b2e 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "main.h" // For g_settings
 #include "settings.h"
 #include "nodedef.h"
+#include "collision.h"
 #include "environment.h"
 #include "gamedef.h"
 
@@ -37,13 +38,13 @@ Player::Player(IGameDef *gamedef):
        in_water_stable(false),
        is_climbing(false),
        swimming_up(false),
+       inventory(gamedef->idef()),
        inventory_backup(NULL),
        craftresult_is_preview(true),
        hp(20),
        peer_id(PEER_ID_INEXISTENT),
 // protected
        m_gamedef(gamedef),
-       m_selected_item(0),
        m_pitch(0),
        m_yaw(0),
        m_speed(0,0,0),
@@ -58,11 +59,6 @@ Player::~Player()
        delete inventory_backup;
 }
 
-void Player::wieldItem(u16 item)
-{
-       m_selected_item = item;
-}
-
 void Player::resetInventory()
 {
        inventory.clear();
@@ -172,7 +168,7 @@ void Player::deSerialize(std::istream &is)
                hp = 20;
        }
 
-       inventory.deSerialize(is, m_gamedef);
+       inventory.deSerialize(is);
 }
 
 #ifndef SERVER
index 1c9dde7e0aab7c369f58164a74f71577de08f048..43f6ee5ec162f678c59bb3ea58a882d27033f4ff 100644 (file)
@@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "common_irrlicht.h"
 #include "inventory.h"
-#include "collision.h"
 
 #define PLAYERNAME_SIZE 20
 
@@ -31,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class Map;
 class IGameDef;
+class CollisionInfo;
 
 class Player
 {
@@ -117,16 +117,7 @@ public:
                snprintf(m_name, PLAYERNAME_SIZE, "%s", name);
        }
 
-       virtual void wieldItem(u16 item);
-       virtual const InventoryItem *getWieldItem() const
-       {
-               const InventoryList *list = inventory.getList("main");
-               if (list)
-                       return list->getItem(m_selected_item);
-               return NULL;
-       }
-
-       const char * getName()
+       const char * getName() const
        {
                return m_name;
        }
@@ -174,7 +165,6 @@ protected:
        IGameDef *m_gamedef;
 
        char m_name[PLAYERNAME_SIZE];
-       u16 m_selected_item;
        f32 m_pitch;
        f32 m_yaw;
        v3f m_speed;
index b617626a0bc3a2fb05359992bd40de5719ae4f0a..187d1a894146cf3b8a0f5aabbef63c86d61f9180 100644 (file)
@@ -36,10 +36,9 @@ extern "C" {
 //#include "luna.h"
 #include "luaentity_common.h"
 #include "content_sao.h" // For LuaEntitySAO
-#include "tooldef.h"
+#include "itemdef.h"
 #include "nodedef.h"
 #include "craftdef.h"
-#include "craftitemdef.h"
 #include "main.h" // For g_settings
 #include "settings.h" // For accessing g_settings
 #include "nodemetadata.h"
@@ -127,243 +126,55 @@ public:
        }
 };
 
-std::string get_current_modname(lua_State *L)
-{
-       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
-       std::string modname = "";
-       if(lua_type(L, -1) == LUA_TSTRING)
-               modname = lua_tostring(L, -1);
-       lua_pop(L, 1);
-       return modname;
-}
-
-void check_modname_prefix(lua_State *L, std::string &name)
-{
-       if(name.size() == 0)
-               throw LuaError(L, std::string("Name is empty"));
-       
-       if(name[0] == ':'){
-               name = name.substr(1);
-               return;
-       }
-       
-       std::string modname = get_current_modname(L);
-       assert(modname != "");
-       
-       // For __builtin, anything goes
-       if(modname == "__builtin")
-               return;
-       
-       if(name.substr(0, modname.size()+1) != modname + ":")
-               throw LuaError(L, std::string("Name \"")+name
-                               +"\" does not follow naming conventions: "
-                               +"\"modname:\" or \":\" prefix required)");
-       
-       std::string subname = name.substr(modname.size()+1);
-       if(!string_allowed(subname, "abcdefghijklmnopqrstuvwxyz"
-                       "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
-               throw LuaError(L, std::string("Name \"")+name
-                               +"\" does not follow naming conventions: "
-                               +"\"contains unallowed characters");
-}
-
-static void push_v3f(lua_State *L, v3f p)
-{
-       lua_newtable(L);
-       lua_pushnumber(L, p.X);
-       lua_setfield(L, -2, "x");
-       lua_pushnumber(L, p.Y);
-       lua_setfield(L, -2, "y");
-       lua_pushnumber(L, p.Z);
-       lua_setfield(L, -2, "z");
-}
-
-static v2s16 read_v2s16(lua_State *L, int index)
-{
-       v2s16 p;
-       luaL_checktype(L, index, LUA_TTABLE);
-       lua_getfield(L, index, "x");
-       p.X = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       lua_getfield(L, index, "y");
-       p.Y = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       return p;
-}
-
-static v2f read_v2f(lua_State *L, int index)
-{
-       v2f p;
-       luaL_checktype(L, index, LUA_TTABLE);
-       lua_getfield(L, index, "x");
-       p.X = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       lua_getfield(L, index, "y");
-       p.Y = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       return p;
-}
+/*
+       Getters for stuff in main tables
+*/
 
 static Server* get_server(lua_State *L)
 {
        // Get server from registry
        lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
-       return (Server*)lua_touserdata(L, -1);
+       Server *server = (Server*)lua_touserdata(L, -1);
+       lua_pop(L, 1);
+       return server;
 }
 
 static ServerEnvironment* get_env(lua_State *L)
 {
        // Get environment from registry
        lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env");
-       return (ServerEnvironment*)lua_touserdata(L, -1);
-}
-
-static v3f read_v3f(lua_State *L, int index)
-{
-       v3f pos;
-       luaL_checktype(L, index, LUA_TTABLE);
-       lua_getfield(L, index, "x");
-       pos.X = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       lua_getfield(L, index, "y");
-       pos.Y = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       lua_getfield(L, index, "z");
-       pos.Z = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       return pos;
-}
-
-static v3f check_v3f(lua_State *L, int index)
-{
-       v3f pos;
-       luaL_checktype(L, index, LUA_TTABLE);
-       lua_getfield(L, index, "x");
-       pos.X = luaL_checknumber(L, -1);
-       lua_pop(L, 1);
-       lua_getfield(L, index, "y");
-       pos.Y = luaL_checknumber(L, -1);
+       ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
        lua_pop(L, 1);
-       lua_getfield(L, index, "z");
-       pos.Z = luaL_checknumber(L, -1);
-       lua_pop(L, 1);
-       return pos;
-}
-
-static void pushFloatPos(lua_State *L, v3f p)
-{
-       p /= BS;
-       push_v3f(L, p);
-}
-
-static v3f checkFloatPos(lua_State *L, int index)
-{
-       return check_v3f(L, index) * BS;
-}
-
-static void push_v3s16(lua_State *L, v3s16 p)
-{
-       lua_newtable(L);
-       lua_pushnumber(L, p.X);
-       lua_setfield(L, -2, "x");
-       lua_pushnumber(L, p.Y);
-       lua_setfield(L, -2, "y");
-       lua_pushnumber(L, p.Z);
-       lua_setfield(L, -2, "z");
-}
-
-static v3s16 read_v3s16(lua_State *L, int index)
-{
-       // Correct rounding at <0
-       v3f pf = read_v3f(L, index);
-       return floatToInt(pf, 1.0);
-}
-
-static v3s16 check_v3s16(lua_State *L, int index)
-{
-       // Correct rounding at <0
-       v3f pf = check_v3f(L, index);
-       return floatToInt(pf, 1.0);
-}
-
-static void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef)
-{
-       lua_newtable(L);
-       lua_pushstring(L, ndef->get(n).name.c_str());
-       lua_setfield(L, -2, "name");
-       lua_pushnumber(L, n.getParam1());
-       lua_setfield(L, -2, "param1");
-       lua_pushnumber(L, n.getParam2());
-       lua_setfield(L, -2, "param2");
+       return env;
 }
 
-static MapNode readnode(lua_State *L, int index, INodeDefManager *ndef)
+static void objectref_get(lua_State *L, u16 id)
 {
-       lua_getfield(L, index, "name");
-       const char *name = luaL_checkstring(L, -1);
-       lua_pop(L, 1);
-       u8 param1;
-       lua_getfield(L, index, "param1");
-       if(lua_isnil(L, -1))
-               param1 = 0;
-       else
-               param1 = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       u8 param2;
-       lua_getfield(L, index, "param2");
-       if(lua_isnil(L, -1))
-               param2 = 0;
-       else
-               param2 = lua_tonumber(L, -1);
-       lua_pop(L, 1);
-       return MapNode(ndef, name, param1, param2);
+       // Get minetest.object_refs[i]
+       lua_getglobal(L, "minetest");
+       lua_getfield(L, -1, "object_refs");
+       luaL_checktype(L, -1, LUA_TTABLE);
+       lua_pushnumber(L, id);
+       lua_gettable(L, -2);
+       lua_remove(L, -2); // object_refs
+       lua_remove(L, -2); // minetest
 }
 
-static video::SColor readARGB8(lua_State *L, int index)
+static void luaentity_get(lua_State *L, u16 id)
 {
-       video::SColor color;
-       luaL_checktype(L, index, LUA_TTABLE);
-       lua_getfield(L, index, "a");
-       if(lua_isnumber(L, -1))
-               color.setAlpha(lua_tonumber(L, -1));
-       lua_pop(L, 1);
-       lua_getfield(L, index, "r");
-       color.setRed(lua_tonumber(L, -1));
-       lua_pop(L, 1);
-       lua_getfield(L, index, "g");
-       color.setGreen(lua_tonumber(L, -1));
-       lua_pop(L, 1);
-       lua_getfield(L, index, "b");
-       color.setBlue(lua_tonumber(L, -1));
-       lua_pop(L, 1);
-       return color;
+       // Get minetest.luaentities[i]
+       lua_getglobal(L, "minetest");
+       lua_getfield(L, -1, "luaentities");
+       luaL_checktype(L, -1, LUA_TTABLE);
+       lua_pushnumber(L, id);
+       lua_gettable(L, -2);
+       lua_remove(L, -2); // luaentities
+       lua_remove(L, -2); // minetest
 }
 
-static core::aabbox3d<f32> read_aabbox3df32(lua_State *L, int index, f32 scale)
-{
-       core::aabbox3d<f32> box;
-       if(lua_istable(L, -1)){
-               lua_rawgeti(L, -1, 1);
-               box.MinEdge.X = lua_tonumber(L, -1) * scale;
-               lua_pop(L, 1);
-               lua_rawgeti(L, -1, 2);
-               box.MinEdge.Y = lua_tonumber(L, -1) * scale;
-               lua_pop(L, 1);
-               lua_rawgeti(L, -1, 3);
-               box.MinEdge.Z = lua_tonumber(L, -1) * scale;
-               lua_pop(L, 1);
-               lua_rawgeti(L, -1, 4);
-               box.MaxEdge.X = lua_tonumber(L, -1) * scale;
-               lua_pop(L, 1);
-               lua_rawgeti(L, -1, 5);
-               box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
-               lua_pop(L, 1);
-               lua_rawgeti(L, -1, 6);
-               box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
-               lua_pop(L, 1);
-       }
-       return box;
-}
+/*
+       Table field getters
+*/
 
 static bool getstringfield(lua_State *L, int table,
                const char *fieldname, std::string &result)
@@ -371,7 +182,9 @@ static bool getstringfield(lua_State *L, int table,
        lua_getfield(L, table, fieldname);
        bool got = false;
        if(lua_isstring(L, -1)){
-               result = lua_tostring(L, -1);
+               size_t len = 0;
+               const char *ptr = lua_tolstring(L, -1, &len);
+               result.assign(ptr, len);
                got = true;
        }
        lua_pop(L, 1);
@@ -442,13 +255,13 @@ static int getintfield_default(lua_State *L, int table,
        return result;
 }
 
-/*static float getfloatfield_default(lua_State *L, int table,
+static float getfloatfield_default(lua_State *L, int table,
                const char *fieldname, float default_)
 {
        float result = default_;
        getfloatfield(L, table, fieldname, result);
        return result;
-}*/
+}
 
 static bool getboolfield_default(lua_State *L, int table,
                const char *fieldname, bool default_)
@@ -523,216 +336,19 @@ static void warn_if_field_exists(lua_State *L, int table,
 }
 
 /*
-       Inventory stuff
+       EnumString definitions
 */
 
-static void inventory_set_list_from_lua(Inventory *inv, const char *name,
-               lua_State *L, int tableindex, IGameDef *gamedef, int forcesize=-1)
+struct EnumString es_ItemType[] =
 {
-       if(tableindex < 0)
-               tableindex = lua_gettop(L) + 1 + tableindex;
-       // If nil, delete list
-       if(lua_isnil(L, tableindex)){
-               inv->deleteList(name);
-               return;
-       }
-       // Otherwise set list
-       std::list<std::string> items;
-       luaL_checktype(L, tableindex, LUA_TTABLE);
-       int table = tableindex;
-       lua_pushnil(L);
-       while(lua_next(L, table) != 0){
-               // key at index -2 and value at index -1
-               luaL_checktype(L, -1, LUA_TSTRING);
-               std::string itemstring = luaL_checkstring(L, -1);
-               items.push_back(itemstring);
-               // removes value, keeps key for next iteration
-               lua_pop(L, 1);
-       }
-       int listsize = (forcesize != -1) ? forcesize : items.size();
-       InventoryList *invlist = inv->addList(name, listsize);
-       int index = 0;
-       for(std::list<std::string>::const_iterator
-                       i = items.begin(); i != items.end(); i++){
-               if(forcesize != -1 && index == forcesize)
-                       break;
-               const std::string &itemstring = *i;
-               InventoryItem *newitem = NULL;
-               if(itemstring != "")
-                       newitem = InventoryItem::deSerialize(itemstring,
-                                       gamedef);
-               InventoryItem *olditem = invlist->changeItem(index, newitem);
-               delete olditem;
-               index++;
-       }
-       while(forcesize != -1 && index < forcesize){
-               InventoryItem *olditem = invlist->changeItem(index, NULL);
-               delete olditem;
-               index++;
-       }
-}
+       {ITEM_NONE, "none"},
+       {ITEM_NODE, "node"},
+       {ITEM_CRAFT, "craft"},
+       {ITEM_TOOL, "tool"},
+       {0, NULL},
+};
 
-static void inventory_get_list_to_lua(Inventory *inv, const char *name,
-               lua_State *L)
-{
-       InventoryList *invlist = inv->getList(name);
-       if(invlist == NULL){
-               lua_pushnil(L);
-               return;
-       }
-       // Get the table insert function
-       lua_getglobal(L, "table");
-       lua_getfield(L, -1, "insert");
-       int table_insert = lua_gettop(L);
-       // Create and fill table
-       lua_newtable(L);
-       int table = lua_gettop(L);
-       for(u32 i=0; i<invlist->getSize(); i++){
-               InventoryItem *item = invlist->getItem(i);
-               lua_pushvalue(L, table_insert);
-               lua_pushvalue(L, table);
-               if(item == NULL){
-                       lua_pushstring(L, "");
-               } else {
-                       lua_pushstring(L, item->getItemString().c_str());
-               }
-               if(lua_pcall(L, 2, 0, 0))
-                       script_error(L, "error: %s", lua_tostring(L, -1));
-       }
-}
-
-static void push_stack_item(lua_State *L, InventoryItem *item0)
-{
-       if(item0 == NULL){
-               lua_pushnil(L);
-       }
-       else if(std::string("MaterialItem") == item0->getName()){
-               MaterialItem *item = (MaterialItem*)item0;
-               lua_newtable(L);
-               lua_pushstring(L, "node");
-               lua_setfield(L, -2, "type");
-               lua_pushstring(L, item->getNodeName().c_str());
-               lua_setfield(L, -2, "name");
-       }
-       else if(std::string("CraftItem") == item0->getName()){
-               CraftItem *item = (CraftItem*)item0;
-               lua_newtable(L);
-               lua_pushstring(L, "craft");
-               lua_setfield(L, -2, "type");
-               lua_pushstring(L, item->getSubName().c_str());
-               lua_setfield(L, -2, "name");
-       }
-       else if(std::string("ToolItem") == item0->getName()){
-               ToolItem *item = (ToolItem*)item0;
-               lua_newtable(L);
-               lua_pushstring(L, "tool");
-               lua_setfield(L, -2, "type");
-               lua_pushstring(L, item->getToolName().c_str());
-               lua_setfield(L, -2, "name");
-               lua_pushstring(L, itos(item->getWear()).c_str());
-               lua_setfield(L, -2, "wear");
-       }
-       else{
-               errorstream<<"push_stack_item: Unknown item name: \""
-                               <<item0->getName()<<"\""<<std::endl;
-               lua_pushnil(L);
-       }
-}
-
-static InventoryItem* check_stack_item(lua_State *L, int index)
-{
-       if(index < 0)
-               index = lua_gettop(L) + 1 + index;
-       if(lua_isnil(L, index))
-               return NULL;
-       if(!lua_istable(L, index))
-               throw LuaError(L, "check_stack_item: Item not nil or table");
-       // A very crappy implementation for now
-       // Will be replaced when unified namespace for items is made
-       std::string type = getstringfield_default(L, index, "type", "");
-       std::string name = getstringfield_default(L, index, "name", "");
-       std::string num = getstringfield_default(L, index, "wear", "_");
-       if(num == "_")
-               num = 1;
-       std::string itemstring = type + " \"" + name + "\" " + num;
-       try{
-               return InventoryItem::deSerialize(itemstring, get_server(L));
-       }catch(SerializationError &e){
-               throw LuaError(L, std::string("check_stack_item: "
-                               "internal error (itemstring=\"")+itemstring+"\")");
-       }
-}
-
-/*
-       ToolDiggingProperties
-*/
-
-static ToolDiggingProperties read_tool_digging_properties(
-               lua_State *L, int table)
-{
-       ToolDiggingProperties prop;
-       getfloatfield(L, table, "full_punch_interval", prop.full_punch_interval);
-       getfloatfield(L, table, "basetime", prop.basetime);
-       getfloatfield(L, table, "dt_weight", prop.dt_weight);
-       getfloatfield(L, table, "dt_crackiness", prop.dt_crackiness);
-       getfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness);
-       getfloatfield(L, table, "dt_cuttability", prop.dt_cuttability);
-       getfloatfield(L, table, "basedurability", prop.basedurability);
-       getfloatfield(L, table, "dd_weight", prop.dd_weight);
-       getfloatfield(L, table, "dd_crackiness", prop.dd_crackiness);
-       getfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness);
-       getfloatfield(L, table, "dd_cuttability", prop.dd_cuttability);
-       return prop;
-}
-
-static void set_tool_digging_properties(lua_State *L, int table,
-               const ToolDiggingProperties &prop)
-{
-       setfloatfield(L, table, "full_punch_interval", prop.full_punch_interval);
-       setfloatfield(L, table, "basetime", prop.basetime);
-       setfloatfield(L, table, "dt_weight", prop.dt_weight);
-       setfloatfield(L, table, "dt_crackiness", prop.dt_crackiness);
-       setfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness);
-       setfloatfield(L, table, "dt_cuttability", prop.dt_cuttability);
-       setfloatfield(L, table, "basedurability", prop.basedurability);
-       setfloatfield(L, table, "dd_weight", prop.dd_weight);
-       setfloatfield(L, table, "dd_crackiness", prop.dd_crackiness);
-       setfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness);
-       setfloatfield(L, table, "dd_cuttability", prop.dd_cuttability);
-}
-
-static void push_tool_digging_properties(lua_State *L,
-               const ToolDiggingProperties &prop)
-{
-       lua_newtable(L);
-       set_tool_digging_properties(L, -1, prop);
-}
-
-/*
-       ToolDefinition
-*/
-
-static ToolDefinition read_tool_definition(lua_State *L, int table)
-{
-       ToolDefinition def;
-       getstringfield(L, table, "image", def.imagename);
-       def.properties = read_tool_digging_properties(L, table);
-       return def;
-}
-
-static void push_tool_definition(lua_State *L, const ToolDefinition &def)
-{
-       lua_newtable(L);
-       lua_pushstring(L, def.imagename.c_str());
-       lua_setfield(L, -2, "image");
-       set_tool_digging_properties(L, -1, def.properties);
-}
-
-/*
-       EnumString definitions
-*/
-
-struct EnumString es_DrawType[] =
+struct EnumString es_DrawType[] =
 {
        {NDT_NORMAL, "normal"},
        {NDT_AIRLIKE, "airlike"},
@@ -783,180 +399,869 @@ struct EnumString es_Diggability[] =
 };
 
 /*
-       Getters for stuff in main tables
+       C struct <-> Lua table converter functions
 */
 
-static void objectref_get(lua_State *L, u16 id)
+static void push_v3f(lua_State *L, v3f p)
 {
-       // Get minetest.object_refs[i]
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "object_refs");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       lua_pushnumber(L, id);
-       lua_gettable(L, -2);
-       lua_remove(L, -2); // object_refs
-       lua_remove(L, -2); // minetest
+       lua_newtable(L);
+       lua_pushnumber(L, p.X);
+       lua_setfield(L, -2, "x");
+       lua_pushnumber(L, p.Y);
+       lua_setfield(L, -2, "y");
+       lua_pushnumber(L, p.Z);
+       lua_setfield(L, -2, "z");
 }
 
-static void luaentity_get(lua_State *L, u16 id)
+static v2s16 read_v2s16(lua_State *L, int index)
 {
-       // Get minetest.luaentities[i]
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "luaentities");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       lua_pushnumber(L, id);
-       lua_gettable(L, -2);
-       lua_remove(L, -2); // luaentities
-       lua_remove(L, -2); // minetest
+       v2s16 p;
+       luaL_checktype(L, index, LUA_TTABLE);
+       lua_getfield(L, index, "x");
+       p.X = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "y");
+       p.Y = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       return p;
 }
 
-/*
-       Object wrappers
-*/
+static v2f read_v2f(lua_State *L, int index)
+{
+       v2f p;
+       luaL_checktype(L, index, LUA_TTABLE);
+       lua_getfield(L, index, "x");
+       p.X = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "y");
+       p.Y = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       return p;
+}
 
-#define method(class, name) {#name, class::l_##name}
+static v3f read_v3f(lua_State *L, int index)
+{
+       v3f pos;
+       luaL_checktype(L, index, LUA_TTABLE);
+       lua_getfield(L, index, "x");
+       pos.X = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "y");
+       pos.Y = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "z");
+       pos.Z = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       return pos;
+}
 
-/*
-       ItemStack
-*/
+static v3f check_v3f(lua_State *L, int index)
+{
+       v3f pos;
+       luaL_checktype(L, index, LUA_TTABLE);
+       lua_getfield(L, index, "x");
+       pos.X = luaL_checknumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "y");
+       pos.Y = luaL_checknumber(L, -1);
+       lua_pop(L, 1);
+       lua_getfield(L, index, "z");
+       pos.Z = luaL_checknumber(L, -1);
+       lua_pop(L, 1);
+       return pos;
+}
+
+static void pushFloatPos(lua_State *L, v3f p)
+{
+       p /= BS;
+       push_v3f(L, p);
+}
 
-class ItemStack
+static v3f checkFloatPos(lua_State *L, int index)
 {
-private:
-       InventoryItem *m_stack;
+       return check_v3f(L, index) * BS;
+}
 
-       static const char className[];
-       static const luaL_reg methods[];
+static void push_v3s16(lua_State *L, v3s16 p)
+{
+       lua_newtable(L);
+       lua_pushnumber(L, p.X);
+       lua_setfield(L, -2, "x");
+       lua_pushnumber(L, p.Y);
+       lua_setfield(L, -2, "y");
+       lua_pushnumber(L, p.Z);
+       lua_setfield(L, -2, "z");
+}
 
-       // Exported functions
-       
-       // garbage collector
-       static int gc_object(lua_State *L) {
-               ItemStack *o = *(ItemStack **)(lua_touserdata(L, 1));
-               delete o;
-               return 0;
+static v3s16 read_v3s16(lua_State *L, int index)
+{
+       // Correct rounding at <0
+       v3f pf = read_v3f(L, index);
+       return floatToInt(pf, 1.0);
+}
+
+static v3s16 check_v3s16(lua_State *L, int index)
+{
+       // Correct rounding at <0
+       v3f pf = check_v3f(L, index);
+       return floatToInt(pf, 1.0);
+}
+
+static void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef)
+{
+       lua_newtable(L);
+       lua_pushstring(L, ndef->get(n).name.c_str());
+       lua_setfield(L, -2, "name");
+       lua_pushnumber(L, n.getParam1());
+       lua_setfield(L, -2, "param1");
+       lua_pushnumber(L, n.getParam2());
+       lua_setfield(L, -2, "param2");
+}
+
+static MapNode readnode(lua_State *L, int index, INodeDefManager *ndef)
+{
+       lua_getfield(L, index, "name");
+       const char *name = luaL_checkstring(L, -1);
+       lua_pop(L, 1);
+       u8 param1;
+       lua_getfield(L, index, "param1");
+       if(lua_isnil(L, -1))
+               param1 = 0;
+       else
+               param1 = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       u8 param2;
+       lua_getfield(L, index, "param2");
+       if(lua_isnil(L, -1))
+               param2 = 0;
+       else
+               param2 = lua_tonumber(L, -1);
+       lua_pop(L, 1);
+       return MapNode(ndef, name, param1, param2);
+}
+
+static video::SColor readARGB8(lua_State *L, int index)
+{
+       video::SColor color;
+       luaL_checktype(L, index, LUA_TTABLE);
+       lua_getfield(L, index, "a");
+       if(lua_isnumber(L, -1))
+               color.setAlpha(lua_tonumber(L, -1));
+       lua_pop(L, 1);
+       lua_getfield(L, index, "r");
+       color.setRed(lua_tonumber(L, -1));
+       lua_pop(L, 1);
+       lua_getfield(L, index, "g");
+       color.setGreen(lua_tonumber(L, -1));
+       lua_pop(L, 1);
+       lua_getfield(L, index, "b");
+       color.setBlue(lua_tonumber(L, -1));
+       lua_pop(L, 1);
+       return color;
+}
+
+static core::aabbox3d<f32> read_aabbox3df32(lua_State *L, int index, f32 scale)
+{
+       core::aabbox3d<f32> box;
+       if(lua_istable(L, -1)){
+               lua_rawgeti(L, -1, 1);
+               box.MinEdge.X = lua_tonumber(L, -1) * scale;
+               lua_pop(L, 1);
+               lua_rawgeti(L, -1, 2);
+               box.MinEdge.Y = lua_tonumber(L, -1) * scale;
+               lua_pop(L, 1);
+               lua_rawgeti(L, -1, 3);
+               box.MinEdge.Z = lua_tonumber(L, -1) * scale;
+               lua_pop(L, 1);
+               lua_rawgeti(L, -1, 4);
+               box.MaxEdge.X = lua_tonumber(L, -1) * scale;
+               lua_pop(L, 1);
+               lua_rawgeti(L, -1, 5);
+               box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
+               lua_pop(L, 1);
+               lua_rawgeti(L, -1, 6);
+               box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
+               lua_pop(L, 1);
+       }
+       return box;
+}
+
+/*
+       ToolDiggingProperties
+*/
+
+static ToolDiggingProperties read_tool_digging_properties(
+               lua_State *L, int table)
+{
+       ToolDiggingProperties prop;
+       getfloatfield(L, table, "full_punch_interval", prop.full_punch_interval);
+       getfloatfield(L, table, "basetime", prop.basetime);
+       getfloatfield(L, table, "dt_weight", prop.dt_weight);
+       getfloatfield(L, table, "dt_crackiness", prop.dt_crackiness);
+       getfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness);
+       getfloatfield(L, table, "dt_cuttability", prop.dt_cuttability);
+       getfloatfield(L, table, "basedurability", prop.basedurability);
+       getfloatfield(L, table, "dd_weight", prop.dd_weight);
+       getfloatfield(L, table, "dd_crackiness", prop.dd_crackiness);
+       getfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness);
+       getfloatfield(L, table, "dd_cuttability", prop.dd_cuttability);
+       return prop;
+}
+
+static void set_tool_digging_properties(lua_State *L, int table,
+               const ToolDiggingProperties &prop)
+{
+       setfloatfield(L, table, "full_punch_interval", prop.full_punch_interval);
+       setfloatfield(L, table, "basetime", prop.basetime);
+       setfloatfield(L, table, "dt_weight", prop.dt_weight);
+       setfloatfield(L, table, "dt_crackiness", prop.dt_crackiness);
+       setfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness);
+       setfloatfield(L, table, "dt_cuttability", prop.dt_cuttability);
+       setfloatfield(L, table, "basedurability", prop.basedurability);
+       setfloatfield(L, table, "dd_weight", prop.dd_weight);
+       setfloatfield(L, table, "dd_crackiness", prop.dd_crackiness);
+       setfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness);
+       setfloatfield(L, table, "dd_cuttability", prop.dd_cuttability);
+}
+
+static void push_tool_digging_properties(lua_State *L,
+               const ToolDiggingProperties &prop)
+{
+       lua_newtable(L);
+       set_tool_digging_properties(L, -1, prop);
+}
+
+/*
+       PointedThing
+*/
+
+static void push_pointed_thing(lua_State *L, const PointedThing& pointed)
+{
+       lua_newtable(L);
+       if(pointed.type == POINTEDTHING_NODE)
+       {
+               lua_pushstring(L, "node");
+               lua_setfield(L, -2, "type");
+               push_v3s16(L, pointed.node_undersurface);
+               lua_setfield(L, -2, "under");
+               push_v3s16(L, pointed.node_abovesurface);
+               lua_setfield(L, -2, "above");
+       }
+       else if(pointed.type == POINTEDTHING_OBJECT)
+       {
+               lua_pushstring(L, "object");
+               lua_setfield(L, -2, "type");
+               objectref_get(L, pointed.object_id);
+               lua_setfield(L, -2, "ref");
+       }
+       else
+       {
+               lua_pushstring(L, "nothing");
+               lua_setfield(L, -2, "type");
+       }
+}
+
+/*
+       ItemDefinition
+*/
+
+static ItemDefinition read_item_definition(lua_State *L, int index)
+{
+       if(index < 0)
+               index = lua_gettop(L) + 1 + index;
+
+       // Read the item definition
+       ItemDefinition def;
+
+       def.type = (ItemType)getenumfield(L, index, "type",
+                       es_ItemType, ITEM_NONE);
+       getstringfield(L, index, "name", def.name);
+       getstringfield(L, index, "description", def.description);
+       getstringfield(L, index, "inventory_image", def.inventory_image);
+       getstringfield(L, index, "wield_image", def.wield_image);
+
+       lua_getfield(L, index, "wield_scale");
+       if(lua_istable(L, -1)){
+               def.wield_scale = check_v3f(L, -1);
+       }
+       lua_pop(L, 1);
+
+       def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max);
+       if(def.stack_max == 0)
+               def.stack_max = 1;
+
+       lua_getfield(L, index, "on_use");
+       def.usable = lua_isfunction(L, -1);
+       lua_pop(L, 1);
+
+       getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
+
+       lua_getfield(L, index, "tool_digging_properties");
+       if(lua_istable(L, -1)){
+               def.tool_digging_properties = new ToolDiggingProperties(
+                               read_tool_digging_properties(L, -1));
+       }
+       lua_pop(L, 1);
+
+       // If name is "" (hand), ensure there are ToolDiggingProperties
+       // because it will be looked up there whenever any other item has
+       // no ToolDiggingProperties
+       if(def.name == "" && def.tool_digging_properties == NULL){
+               def.tool_digging_properties = new ToolDiggingProperties();
+       }
+
+       return def;
+}
+
+/*
+       ContentFeatures
+*/
+
+static ContentFeatures read_content_features(lua_State *L, int index)
+{
+       if(index < 0)
+               index = lua_gettop(L) + 1 + index;
+
+       ContentFeatures f;
+       getstringfield(L, index, "name", f.name);
+
+       /* Visual definition */
+
+       f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType,
+                       NDT_NORMAL);
+       getfloatfield(L, index, "visual_scale", f.visual_scale);
+
+       lua_getfield(L, index, "tile_images");
+       if(lua_istable(L, -1)){
+               int table = lua_gettop(L);
+               lua_pushnil(L);
+               int i = 0;
+               while(lua_next(L, table) != 0){
+                       // key at index -2 and value at index -1
+                       if(lua_isstring(L, -1))
+                               f.tname_tiles[i] = lua_tostring(L, -1);
+                       else
+                               f.tname_tiles[i] = "";
+                       // removes value, keeps key for next iteration
+                       lua_pop(L, 1);
+                       i++;
+                       if(i==6){
+                               lua_pop(L, 1);
+                               break;
+                       }
+               }
+               // Copy last value to all remaining textures
+               if(i >= 1){
+                       std::string lastname = f.tname_tiles[i-1];
+                       while(i < 6){
+                               f.tname_tiles[i] = lastname;
+                               i++;
+                       }
+               }
+       }
+       lua_pop(L, 1);
+
+       lua_getfield(L, index, "special_materials");
+       if(lua_istable(L, -1)){
+               int table = lua_gettop(L);
+               lua_pushnil(L);
+               int i = 0;
+               while(lua_next(L, table) != 0){
+                       // key at index -2 and value at index -1
+                       int smtable = lua_gettop(L);
+                       std::string tname = getstringfield_default(
+                                       L, smtable, "image", "");
+                       bool backface_culling = getboolfield_default(
+                                       L, smtable, "backface_culling", true);
+                       MaterialSpec mspec(tname, backface_culling);
+                       f.mspec_special[i] = mspec;
+                       // removes value, keeps key for next iteration
+                       lua_pop(L, 1);
+                       i++;
+                       if(i==6){
+                               lua_pop(L, 1);
+                               break;
+                       }
+               }
+       }
+       lua_pop(L, 1);
+
+       f.alpha = getintfield_default(L, index, "alpha", 255);
+
+       /* Other stuff */
+       
+       lua_getfield(L, index, "post_effect_color");
+       if(!lua_isnil(L, -1))
+               f.post_effect_color = readARGB8(L, -1);
+       lua_pop(L, 1);
+
+       f.param_type = (ContentParamType)getenumfield(L, index, "paramtype",
+                       es_ContentParamType, CPT_NONE);
+       
+       // True for all ground-like things like stone and mud, false for eg. trees
+       getboolfield(L, index, "is_ground_content", f.is_ground_content);
+       f.light_propagates = (f.param_type == CPT_LIGHT);
+       warn_if_field_exists(L, index, "light_propagates",
+                       "deprecated: determined from paramtype");
+       getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates);
+       // This is used for collision detection.
+       // Also for general solidness queries.
+       getboolfield(L, index, "walkable", f.walkable);
+       // Player can point to these
+       getboolfield(L, index, "pointable", f.pointable);
+       // Player can dig these
+       getboolfield(L, index, "diggable", f.diggable);
+       // Player can climb these
+       getboolfield(L, index, "climbable", f.climbable);
+       // Player can build on these
+       getboolfield(L, index, "buildable_to", f.buildable_to);
+       // If true, param2 is set to direction when placed. Used for torches.
+       // NOTE: the direction format is quite inefficient and should be changed
+       getboolfield(L, index, "wall_mounted", f.wall_mounted);
+       // Inventory item string as which the node appears in inventory when dug.
+       // Mineral overrides this.
+       getstringfield(L, index, "dug_item", f.dug_item);
+       // Extra dug item and its rarity
+       getstringfield(L, index, "extra_dug_item", f.extra_dug_item);
+       // Usual get interval for extra dug item
+       getintfield(L, index, "extra_dug_item_rarity", f.extra_dug_item_rarity);
+       // Metadata name of node (eg. "furnace")
+       getstringfield(L, index, "metadata_name", f.metadata_name);
+       // Whether the node is non-liquid, source liquid or flowing liquid
+       f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype",
+                       es_LiquidType, LIQUID_NONE);
+       // If the content is liquid, this is the flowing version of the liquid.
+       getstringfield(L, index, "liquid_alternative_flowing",
+                       f.liquid_alternative_flowing);
+       // If the content is liquid, this is the source version of the liquid.
+       getstringfield(L, index, "liquid_alternative_source",
+                       f.liquid_alternative_source);
+       // Viscosity for fluid flow, ranging from 1 to 7, with
+       // 1 giving almost instantaneous propagation and 7 being
+       // the slowest possible
+       f.liquid_viscosity = getintfield_default(L, index,
+                       "liquid_viscosity", f.liquid_viscosity);
+       // Amount of light the node emits
+       f.light_source = getintfield_default(L, index,
+                       "light_source", f.light_source);
+       f.damage_per_second = getintfield_default(L, index,
+                       "damage_per_second", f.damage_per_second);
+       
+       lua_getfield(L, index, "selection_box");
+       if(lua_istable(L, -1)){
+               f.selection_box.type = (NodeBoxType)getenumfield(L, -1, "type",
+                               es_NodeBoxType, NODEBOX_REGULAR);
+
+               lua_getfield(L, -1, "fixed");
+               if(lua_istable(L, -1))
+                       f.selection_box.fixed = read_aabbox3df32(L, -1, BS);
+               lua_pop(L, 1);
+
+               lua_getfield(L, -1, "wall_top");
+               if(lua_istable(L, -1))
+                       f.selection_box.wall_top = read_aabbox3df32(L, -1, BS);
+               lua_pop(L, 1);
+
+               lua_getfield(L, -1, "wall_bottom");
+               if(lua_istable(L, -1))
+                       f.selection_box.wall_bottom = read_aabbox3df32(L, -1, BS);
+               lua_pop(L, 1);
+
+               lua_getfield(L, -1, "wall_side");
+               if(lua_istable(L, -1))
+                       f.selection_box.wall_side = read_aabbox3df32(L, -1, BS);
+               lua_pop(L, 1);
+       }
+       lua_pop(L, 1);
+
+       lua_getfield(L, index, "material");
+       if(lua_istable(L, -1)){
+               f.material.diggability = (Diggability)getenumfield(L, -1, "diggability",
+                               es_Diggability, DIGGABLE_NORMAL);
+               
+               getfloatfield(L, -1, "constant_time", f.material.constant_time);
+               getfloatfield(L, -1, "weight", f.material.weight);
+               getfloatfield(L, -1, "crackiness", f.material.crackiness);
+               getfloatfield(L, -1, "crumbliness", f.material.crumbliness);
+               getfloatfield(L, -1, "cuttability", f.material.cuttability);
+               getfloatfield(L, -1, "flammability", f.material.flammability);
+       }
+       lua_pop(L, 1);
+
+       return f;
+}
+
+/*
+       Inventory stuff
+*/
+
+static ItemStack read_item(lua_State *L, int index);
+
+static void inventory_set_list_from_lua(Inventory *inv, const char *name,
+               lua_State *L, int tableindex, int forcesize=-1)
+{
+       dstream<<"inventory_set_list_from_lua\n";
+       if(tableindex < 0)
+               tableindex = lua_gettop(L) + 1 + tableindex;
+       // If nil, delete list
+       if(lua_isnil(L, tableindex)){
+               inv->deleteList(name);
+               return;
+       }
+       // Otherwise set list
+       std::vector<ItemStack> items;
+       luaL_checktype(L, tableindex, LUA_TTABLE);
+       lua_pushnil(L);
+       while(lua_next(L, tableindex) != 0){
+               // key at index -2 and value at index -1
+               items.push_back(read_item(L, -1));
+               // removes value, keeps key for next iteration
+               lua_pop(L, 1);
+       }
+       int listsize = (forcesize != -1) ? forcesize : items.size();
+       InventoryList *invlist = inv->addList(name, listsize);
+       int index = 0;
+       for(std::vector<ItemStack>::const_iterator
+                       i = items.begin(); i != items.end(); i++){
+               if(forcesize != -1 && index == forcesize)
+                       break;
+               invlist->changeItem(index, *i);
+               index++;
+       }
+       while(forcesize != -1 && index < forcesize){
+               invlist->deleteItem(index);
+               index++;
+       }
+       dstream<<"inventory_set_list_from_lua done\n";
+}
+
+static void inventory_get_list_to_lua(Inventory *inv, const char *name,
+               lua_State *L)
+{
+       InventoryList *invlist = inv->getList(name);
+       if(invlist == NULL){
+               lua_pushnil(L);
+               return;
+       }
+       // Get the table insert function
+       lua_getglobal(L, "table");
+       lua_getfield(L, -1, "insert");
+       int table_insert = lua_gettop(L);
+       // Create and fill table
+       lua_newtable(L);
+       int table = lua_gettop(L);
+       for(u32 i=0; i<invlist->getSize(); i++){
+               ItemStack item = invlist->getItem(i);
+               lua_pushvalue(L, table_insert);
+               lua_pushvalue(L, table);
+               lua_pushstring(L, item.getItemString().c_str());
+               if(lua_pcall(L, 2, 0, 0))
+                       script_error(L, "error: %s", lua_tostring(L, -1));
+       }
+}
+
+/*
+       Helpful macros for userdata classes
+*/
+
+#define method(class, name) {#name, class::l_##name}
+
+/*
+       LuaItemStack
+*/
+
+class LuaItemStack
+{
+private:
+       ItemStack m_stack;
+
+       static const char className[];
+       static const luaL_reg methods[];
+
+       // Exported functions
+       
+       // garbage collector
+       static int gc_object(lua_State *L)
+       {
+               LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1));
+               delete o;
+               return 0;
+       }
+
+       // is_empty(self) -> true/false
+       static int l_is_empty(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               lua_pushboolean(L, item.empty());
+               return 1;
+       }
+
+       // get_name(self) -> string
+       static int l_get_name(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               lua_pushstring(L, item.name.c_str());
+               return 1;
+       }
+
+       // get_count(self) -> number
+       static int l_get_count(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               lua_pushinteger(L, item.count);
+               return 1;
+       }
+
+       // get_wear(self) -> number
+       static int l_get_wear(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               lua_pushinteger(L, item.wear);
+               return 1;
+       }
+
+       // get_metadata(self) -> string
+       static int l_get_metadata(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
+               return 1;
+       }
+
+       // clear(self) -> true
+       static int l_clear(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               o->m_stack.clear();
+               lua_pushboolean(L, true);
+               return 1;
+       }
+
+       // replace(self, itemstack or itemstring or table or nil) -> true
+       static int l_replace(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               o->m_stack = read_item(L, 2);
+               lua_pushboolean(L, true);
+               return 1;
+       }
+
+       // to_string(self) -> string
+       static int l_to_string(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               std::string itemstring = o->m_stack.getItemString();
+               lua_pushstring(L, itemstring.c_str());
+               return 1;
+       }
+
+       // to_table(self) -> table or nil
+       static int l_to_table(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               const ItemStack &item = o->m_stack;
+               if(item.empty())
+               {
+                       lua_pushnil(L);
+               }
+               else
+               {
+                       lua_newtable(L);
+                       lua_pushstring(L, item.name.c_str());
+                       lua_setfield(L, -2, "name");
+                       lua_pushinteger(L, item.count);
+                       lua_setfield(L, -2, "count");
+                       lua_pushinteger(L, item.wear);
+                       lua_setfield(L, -2, "wear");
+                       lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
+                       lua_setfield(L, -2, "metadata");
+               }
+               return 1;
+       }
+
+       // get_stack_max(self) -> number
+       static int l_get_stack_max(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               lua_pushinteger(L, item.getStackMax(get_server(L)->idef()));
+               return 1;
+       }
+
+       // get_free_space(self) -> number
+       static int l_get_free_space(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               lua_pushinteger(L, item.freeSpace(get_server(L)->idef()));
+               return 1;
+       }
+
+       // is_known(self) -> true/false
+       // Checks if the item is defined.
+       static int l_is_known(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               bool is_known = item.isKnown(get_server(L)->idef());
+               lua_pushboolean(L, is_known);
+               return 1;
+       }
+
+       // get_definition(self) -> table
+       // Returns the item definition table from minetest.registered_items,
+       // or a fallback one (name="unknown")
+       static int l_get_definition(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+
+               // Get minetest.registered_items[name]
+               lua_getglobal(L, "minetest");
+               lua_getfield(L, -1, "registered_items");
+               luaL_checktype(L, -1, LUA_TTABLE);
+               lua_getfield(L, -1, item.name.c_str());
+               if(lua_isnil(L, -1))
+               {
+                       lua_pop(L, 1);
+                       lua_getfield(L, -1, "unknown");
+               }
+               return 1;
        }
 
-       // peek_item(self)
-       static int l_peek_item(lua_State *L)
+       // get_tool_digging_properties(self) -> table
+       // Returns the effective tool digging properties.
+       // Returns those of the hand ("") if this item has none associated.
+       static int l_get_tool_digging_properties(lua_State *L)
        {
-               ItemStack *o = checkobject(L, 1);
-               push_stack_item(L, o->m_stack);
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               const ToolDiggingProperties &prop =
+                       item.getToolDiggingProperties(get_server(L)->idef());
+               push_tool_digging_properties(L, prop);
                return 1;
        }
 
-       // take_item(self)
-       static int l_take_item(lua_State *L)
+       // add_wear(self, amount) -> true/false
+       // The range for "amount" is [0,65535]. Wear is only added if the item
+       // is a tool. Adding wear might destroy the item.
+       // Returns true if the item is (or was) a tool.
+       static int l_add_wear(lua_State *L)
        {
-               ItemStack *o = checkobject(L, 1);
-               push_stack_item(L, o->m_stack);
-               if(o->m_stack->getCount() <= 1){
-                       delete o->m_stack;
-                       o->m_stack = NULL;
-               } else {
-                       o->m_stack->remove(1);
-               }
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               int amount = lua_tointeger(L, 2);
+               bool result = item.addWear(amount, get_server(L)->idef());
+               lua_pushboolean(L, result);
                return 1;
        }
 
-       // put_item(self, item) -> true/false
-       static int l_put_item(lua_State *L)
+       // add_item(self, itemstack or itemstring or table or nil) -> itemstack
+       // Returns leftover item stack
+       static int l_add_item(lua_State *L)
        {
-               ItemStack *o = checkobject(L, 1);
-               InventoryItem *item = check_stack_item(L, 2);
-               if(!item){ // nil can always be inserted
-                       lua_pushboolean(L, true);
-                       return 1;
-               }
-               if(!item->addableTo(o->m_stack)){
-                       lua_pushboolean(L, false);
-                       return 1;
-               }
-               o->m_stack->add(1);
-               delete item;
-               lua_pushboolean(L, true);
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               ItemStack newitem = read_item(L, 2);
+               ItemStack leftover = item.addItem(newitem, get_server(L)->idef());
+               create(L, leftover);
                return 1;
        }
 
-       // put_stackstring(self, stackstring) -> true/false
-       static int l_put_stackstring(lua_State *L)
+       // item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack
+       // First return value is true iff the new item fits fully into the stack
+       // Second return value is the would-be-left-over item stack
+       static int l_item_fits(lua_State *L)
        {
-               ItemStack *o = checkobject(L, 1);
-               std::string stackstring = luaL_checkstring(L, 2);
-               try{
-                       InventoryItem *item = InventoryItem::deSerialize(stackstring,
-                                       get_server(L));
-                       if(!item->addableTo(o->m_stack)){
-                               lua_pushboolean(L, false);
-                               return 1;
-                       }
-                       o->m_stack->add(1);
-                       delete item;
-                       lua_pushboolean(L, true);
-                       return 1;
-               }
-               catch(SerializationError &e){
-                       lua_pushboolean(L, false);
-                       return 1;
-               }
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               ItemStack newitem = read_item(L, 2);
+               ItemStack restitem;
+               bool fits = item.itemFits(newitem, &restitem, get_server(L)->idef());
+               lua_pushboolean(L, fits);  // first return value
+               create(L, restitem);       // second return value
+               return 2;
+       }
+
+       // take_item(self, takecount=1) -> itemstack
+       static int l_take_item(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               u32 takecount = 1;
+               if(!lua_isnone(L, 2))
+                       takecount = lua_tointeger(L, 2);
+               ItemStack taken = item.takeItem(takecount);
+               create(L, taken);
+               return 1;
+       }
+
+       // peek_item(self, peekcount=1) -> itemstack
+       static int l_peek_item(lua_State *L)
+       {
+               LuaItemStack *o = checkobject(L, 1);
+               ItemStack &item = o->m_stack;
+               u32 peekcount = 1;
+               if(!lua_isnone(L, 2))
+                       peekcount = lua_tointeger(L, 2);
+               ItemStack peekaboo = item.peekItem(peekcount);
+               create(L, peekaboo);
+               return 1;
        }
 
 public:
-       ItemStack(InventoryItem *item=NULL):
+       LuaItemStack(const ItemStack &item):
                m_stack(item)
        {
        }
 
-       ~ItemStack()
+       ~LuaItemStack()
        {
-               delete m_stack;
        }
 
-       static ItemStack* checkobject(lua_State *L, int narg)
+       const ItemStack& getItem() const
        {
-               luaL_checktype(L, narg, LUA_TUSERDATA);
-               void *ud = luaL_checkudata(L, narg, className);
-               if(!ud) luaL_typerror(L, narg, className);
-               return *(ItemStack**)ud;  // unbox pointer
+               return m_stack;
        }
-
-       InventoryItem* getItemCopy()
+       ItemStack& getItem()
        {
-               if(!m_stack)
-                       return NULL;
-               return m_stack->clone();
+               return m_stack;
        }
        
-       // Creates an ItemStack and leaves it on top of stack
+       // LuaItemStack(itemstack or itemstring or table or nil)
+       // Creates an LuaItemStack and leaves it on top of stack
        static int create_object(lua_State *L)
        {
-               InventoryItem *item = NULL;
-               if(lua_isstring(L, 1)){
-                       std::string itemstring = lua_tostring(L, 1);
-                       if(itemstring != ""){
-                               try{
-                                       IGameDef *gdef = get_server(L);
-                                       item = InventoryItem::deSerialize(itemstring, gdef);
-                               }catch(SerializationError &e){
-                               }
-                       }
-               }
-               ItemStack *o = new ItemStack(item);
+               ItemStack item = read_item(L, 1);
+               LuaItemStack *o = new LuaItemStack(item);
                *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
                luaL_getmetatable(L, className);
                lua_setmetatable(L, -2);
                return 1;
        }
        // Not callable from Lua
-       static int create(lua_State *L, InventoryItem *item)
+       static int create(lua_State *L, const ItemStack &item)
        {
-               ItemStack *o = new ItemStack(item);
+               LuaItemStack *o = new LuaItemStack(item);
                *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
                luaL_getmetatable(L, className);
                lua_setmetatable(L, -2);
                return 1;
        }
 
+       static LuaItemStack* checkobject(lua_State *L, int narg)
+       {
+               luaL_checktype(L, narg, LUA_TUSERDATA);
+               void *ud = luaL_checkudata(L, narg, className);
+               if(!ud) luaL_typerror(L, narg, className);
+               return *(LuaItemStack**)ud;  // unbox pointer
+       }
+
        static void Register(lua_State *L)
        {
                lua_newtable(L);
@@ -981,19 +1286,83 @@ public:
                luaL_openlib(L, 0, methods, 0);  // fill methodtable
                lua_pop(L, 1);  // drop methodtable
 
-               // Can be created from Lua (ItemStack::create(itemstring))
+               // Can be created from Lua (LuaItemStack(itemstack or itemstring or table or nil))
                lua_register(L, className, create_object);
        }
 };
-const char ItemStack::className[] = "ItemStack";
-const luaL_reg ItemStack::methods[] = {
-       method(ItemStack, peek_item),
-       method(ItemStack, take_item),
-       method(ItemStack, put_item),
-       method(ItemStack, put_stackstring),
+const char LuaItemStack::className[] = "ItemStack";
+const luaL_reg LuaItemStack::methods[] = {
+       method(LuaItemStack, is_empty),
+       method(LuaItemStack, get_name),
+       method(LuaItemStack, get_count),
+       method(LuaItemStack, get_wear),
+       method(LuaItemStack, get_metadata),
+       method(LuaItemStack, clear),
+       method(LuaItemStack, replace),
+       method(LuaItemStack, to_string),
+       method(LuaItemStack, to_table),
+       method(LuaItemStack, get_stack_max),
+       method(LuaItemStack, get_free_space),
+       method(LuaItemStack, is_known),
+       method(LuaItemStack, get_definition),
+       method(LuaItemStack, get_tool_digging_properties),
+       method(LuaItemStack, add_wear),
+       method(LuaItemStack, add_item),
+       method(LuaItemStack, item_fits),
+       method(LuaItemStack, take_item),
+       method(LuaItemStack, peek_item),
        {0,0}
 };
 
+static ItemStack read_item(lua_State *L, int index)
+{
+       if(index < 0)
+               index = lua_gettop(L) + 1 + index;
+
+       if(lua_isnil(L, index))
+       {
+               return ItemStack();
+       }
+       else if(lua_isuserdata(L, index))
+       {
+               // Convert from LuaItemStack
+               LuaItemStack *o = LuaItemStack::checkobject(L, index);
+               return o->getItem();
+       }
+       else if(lua_isstring(L, index))
+       {
+               // Convert from itemstring
+               std::string itemstring = lua_tostring(L, index);
+               IItemDefManager *idef = get_server(L)->idef();
+               try
+               {
+                       ItemStack item;
+                       item.deSerialize(itemstring, idef);
+                       return item;
+               }
+               catch(SerializationError &e)
+               {
+                       infostream<<"WARNING: unable to create item from itemstring"
+                                       <<": "<<itemstring<<std::endl;
+                       return ItemStack();
+               }
+       }
+       else if(lua_istable(L, index))
+       {
+               // Convert from table
+               IItemDefManager *idef = get_server(L)->idef();
+               std::string name = getstringfield_default(L, index, "name", "");
+               int count = getintfield_default(L, index, "count", 1);
+               int wear = getintfield_default(L, index, "wear", 0);
+               std::string metadata = getstringfield_default(L, index, "metadata", "");
+               return ItemStack(name, count, wear, metadata, idef);
+       }
+       else
+       {
+               throw LuaError(L, "Expecting itemstack, itemstring, table or nil");
+       }
+}
+
 /*
        InvRef
 */
@@ -1028,15 +1397,6 @@ private:
                return inv->getList(listname);
        }
 
-       static InventoryItem* getitem(lua_State *L, InvRef *ref,
-                       const char *listname, int i)
-       {
-               InventoryList *list = getlist(L, ref, listname);
-               if(!list)
-                       return NULL;
-               return list->getItem(i);
-       }
-
        static void reportInventoryChange(lua_State *L, InvRef *ref)
        {
                // Inform other things that the inventory has changed
@@ -1088,39 +1448,35 @@ private:
                return 0;
        }
 
-       // get_stack(self, listname, i)
+       // get_stack(self, listname, i) -> itemstack
        static int l_get_stack(lua_State *L)
        {
                InvRef *ref = checkobject(L, 1);
                const char *listname = luaL_checkstring(L, 2);
                int i = luaL_checknumber(L, 3) - 1;
-               InventoryItem *item = getitem(L, ref, listname, i);
-               if(!item){
-                       ItemStack::create(L, NULL);
-                       return 1;
-               }
-               ItemStack::create(L, item->clone());
+               InventoryList *list = getlist(L, ref, listname);
+               ItemStack item;
+               if(list != NULL && i >= 0 && i < (int) list->getSize())
+                       item = list->getItem(i);
+               LuaItemStack::create(L, item);
                return 1;
        }
 
-       // set_stack(self, listname, i, stack)
+       // set_stack(self, listname, i, stack) -> true/false
        static int l_set_stack(lua_State *L)
        {
                InvRef *ref = checkobject(L, 1);
                const char *listname = luaL_checkstring(L, 2);
                int i = luaL_checknumber(L, 3) - 1;
-               ItemStack *stack = ItemStack::checkobject(L, 4);
+               ItemStack newitem = read_item(L, 4);
                InventoryList *list = getlist(L, ref, listname);
-               if(!list){
+               if(list != NULL && i >= 0 && i < (int) list->getSize()){
+                       list->changeItem(i, newitem);
+                       reportInventoryChange(L, ref);
+                       lua_pushboolean(L, true);
+               } else {
                        lua_pushboolean(L, false);
-                       return 1;
                }
-               InventoryItem *newitem = stack->getItemCopy();
-               InventoryItem *olditem = list->changeItem(i, newitem);
-               bool success = (olditem != newitem);
-               delete olditem;
-               lua_pushboolean(L, success);
-               reportInventoryChange(L, ref);
                return 1;
        }
 
@@ -1143,57 +1499,79 @@ private:
                InventoryList *list = inv->getList(listname);
                if(list)
                        inventory_set_list_from_lua(inv, listname, L, 3,
-                                       get_server(L), list->getSize());
+                                       list->getSize());
                else
-                       inventory_set_list_from_lua(inv, listname, L, 3,
-                                       get_server(L));
+                       inventory_set_list_from_lua(inv, listname, L, 3);
                reportInventoryChange(L, ref);
                return 0;
        }
 
-       // autoinsert_stack(self, listname, stack)
-       static int l_autoinsert_stack(lua_State *L)
+       // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
+       // Returns the leftover stack
+       static int l_add_item(lua_State *L)
        {
                InvRef *ref = checkobject(L, 1);
                const char *listname = luaL_checkstring(L, 2);
-               ItemStack *stack = ItemStack::checkobject(L, 3);
+               ItemStack item = read_item(L, 3);
                InventoryList *list = getlist(L, ref, listname);
-               if(!list){
-                       lua_pushboolean(L, false);
-                       return 1;
+               if(list){
+                       ItemStack leftover = list->addItem(item);
+                       if(leftover.count != item.count)
+                               reportInventoryChange(L, ref);
+                       LuaItemStack::create(L, leftover);
+               } else {
+                       LuaItemStack::create(L, item);
                }
-               InventoryItem *item = stack->getItemCopy();
-               if(list->roomForItem(item)){
-                       delete list->addItem(item);
-                       lua_pushboolean(L, true);
-                       reportInventoryChange(L, ref);
+               return 1;
+       }
+
+       // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
+       // Returns true if the item completely fits into the list
+       static int l_room_for_item(lua_State *L)
+       {
+               InvRef *ref = checkobject(L, 1);
+               const char *listname = luaL_checkstring(L, 2);
+               ItemStack item = read_item(L, 3);
+               InventoryList *list = getlist(L, ref, listname);
+               if(list){
+                       lua_pushboolean(L, list->roomForItem(item));
                } else {
-                       delete item;
                        lua_pushboolean(L, false);
                }
                return 1;
        }
 
-       // autoinsert_stackstring(self, listname, stackstring)
-       static int l_autoinsert_stackstring(lua_State *L)
+       // contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
+       // Returns true if the list contains the given count of the given item name
+       static int l_contains_item(lua_State *L)
        {
                InvRef *ref = checkobject(L, 1);
                const char *listname = luaL_checkstring(L, 2);
-               const char *stackstring = luaL_checkstring(L, 3);
+               ItemStack item = read_item(L, 3);
                InventoryList *list = getlist(L, ref, listname);
-               if(!list){
+               if(list){
+                       lua_pushboolean(L, list->containsItem(item));
+               } else {
                        lua_pushboolean(L, false);
-                       return 1;
                }
-               InventoryItem *item = InventoryItem::deSerialize(stackstring,
-                               get_server(L));
-               if(list->roomForItem(item)){
-                       delete list->addItem(item);
-                       lua_pushboolean(L, true);
-                       reportInventoryChange(L, ref);
+               return 1;
+       }
+
+       // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
+       // Returns the items that were actually removed
+       static int l_remove_item(lua_State *L)
+       {
+               InvRef *ref = checkobject(L, 1);
+               const char *listname = luaL_checkstring(L, 2);
+               ItemStack item = read_item(L, 3);
+               InventoryList *list = getlist(L, ref, listname);
+               if(list){
+                       ItemStack removed = list->removeItem(item);
+                       if(!removed.empty())
+                               reportInventoryChange(L, ref);
+                       LuaItemStack::create(L, removed);
                } else {
-                       delete item;
-                       lua_pushboolean(L, false);
+                       LuaItemStack::create(L, ItemStack());
                }
                return 1;
        }
@@ -1266,8 +1644,10 @@ const luaL_reg InvRef::methods[] = {
        method(InvRef, set_stack),
        method(InvRef, get_list),
        method(InvRef, set_list),
-       method(InvRef, autoinsert_stack),
-       method(InvRef, autoinsert_stackstring),
+       method(InvRef, add_item),
+       method(InvRef, room_for_item),
+       method(InvRef, contains_item),
+       method(InvRef, remove_item),
        {0,0}
 };
 
@@ -1420,36 +1800,6 @@ private:
                return 1;
        }
 
-       // deprecated: inventory_set_list(self, name, {item1, item2, ...})
-       static int l_inventory_set_list(lua_State *L)
-       {
-               infostream<<"Deprecated: inventory_set_list"<<std::endl;
-               NodeMetaRef *ref = checkobject(L, 1);
-               NodeMetadata *meta = getmeta(ref);
-               if(meta == NULL) return 0;
-               // Do it
-               Inventory *inv = meta->getInventory();
-               const char *name = luaL_checkstring(L, 2);
-               inventory_set_list_from_lua(inv, name, L, 3,
-                               ref->m_env->getGameDef());
-               reportMetadataChange(ref);
-               return 0;
-       }
-
-       // deprecated: inventory_get_list(self, name)
-       static int l_inventory_get_list(lua_State *L)
-       {
-               infostream<<"Deprecated: inventory_get_list"<<std::endl;
-               NodeMetaRef *ref = checkobject(L, 1);
-               NodeMetadata *meta = getmeta(ref);
-               if(meta == NULL) return 0;
-               // Do it
-               Inventory *inv = meta->getInventory();
-               const char *name = luaL_checkstring(L, 2);
-               inventory_get_list_to_lua(inv, name, L);
-               return 1;
-       }
-
        // set_inventory_draw_spec(self, text)
        static int l_set_inventory_draw_spec(lua_State *L)
        {
@@ -1636,8 +1986,6 @@ const luaL_reg NodeMetaRef::methods[] = {
        method(NodeMetaRef, get_owner),
        method(NodeMetaRef, set_infotext),
        method(NodeMetaRef, get_inventory),
-       method(NodeMetaRef, inventory_set_list), // deprecated
-       method(NodeMetaRef, inventory_get_list), // deprecated
        method(NodeMetaRef, set_inventory_draw_spec),
        method(NodeMetaRef, set_allow_text_input),
        method(NodeMetaRef, set_allow_removal),
@@ -1798,122 +2146,98 @@ private:
                return 0;
        }
 
-       // get_wield_digging_properties(self)
-       static int l_get_wield_digging_properties(lua_State *L)
+       // set_hp(self, hp)
+       // hp = number of hitpoints (2 * number of hearts)
+       // returns: nil
+       static int l_set_hp(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
+               luaL_checknumber(L, 2);
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
+               int hp = lua_tonumber(L, 2);
+               infostream<<"ObjectRef::l_set_hp(): id="<<co->getId()
+                               <<" hp="<<hp<<std::endl;
                // Do it
-               ToolDiggingProperties prop;
-               co->getWieldDiggingProperties(&prop);
-               push_tool_digging_properties(L, prop);
+               co->setHP(hp);
+               // Return
+               return 0;
+       }
+
+       // get_hp(self)
+       // returns: number of hitpoints (2 * number of hearts)
+       // 0 if not applicable to this type of object
+       static int l_get_hp(lua_State *L)
+       {
+               ObjectRef *ref = checkobject(L, 1);
+               ServerActiveObject *co = getobject(ref);
+               if(co == NULL) return 0;
+               int hp = co->getHP();
+               infostream<<"ObjectRef::l_get_hp(): id="<<co->getId()
+                               <<" hp="<<hp<<std::endl;
+               // Return
+               lua_pushnumber(L, hp);
                return 1;
        }
 
-       // damage_wielded_item(self, amount)
-       static int l_damage_wielded_item(lua_State *L)
+       // get_inventory(self)
+       static int l_get_inventory(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
                // Do it
-               int amount = lua_tonumber(L, 2);
-               co->damageWieldedItem(amount);
-               return 0;
+               InventoryLocation loc = co->getInventoryLocation();
+               if(get_server(L)->getInventory(loc) != NULL)
+                       InvRef::create(L, loc);
+               else
+                       lua_pushnil(L);
+               return 1;
        }
 
-       // add_to_inventory(self, itemstring)
-       // returns: true if item was added, (false, "reason") otherwise
-       static int l_add_to_inventory(lua_State *L)
+       // get_wield_list(self)
+       static int l_get_wield_list(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
-               luaL_checkstring(L, 2);
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
-               // itemstring
-               const char *itemstring = luaL_checkstring(L, 2);
-               infostream<<"ObjectRef::l_add_to_inventory(): id="<<co->getId()
-                               <<" itemstring=\""<<itemstring<<"\""<<std::endl;
                // Do it
-               std::istringstream is(itemstring, std::ios::binary);
-               ServerEnvironment *env = co->getEnv();
-               assert(env);
-               IGameDef *gamedef = env->getGameDef();
-               try{
-                       InventoryItem *item = InventoryItem::deSerialize(is, gamedef);
-                       if(item->getCount() == 0)
-                               item->setCount(1);
-                       bool added = co->addToInventory(item);
-                       // Return
-                       lua_pushboolean(L, added);
-                       if(!added)
-                               lua_pushstring(L, "failed to add item");
-                       return 2;
-               } catch(SerializationError &e){
-                       // Return
-                       lua_pushboolean(L, false);
-                       lua_pushstring(L, (std::string("Invalid item: ")
-                                       + e.what()).c_str());
-                       return 2;
-               }
+               lua_pushstring(L, co->getWieldList().c_str());
+               return 1;
        }
 
-       // add_to_inventory_later(self, itemstring)
-       // returns: nil
-       static int l_add_to_inventory_later(lua_State *L)
+       // get_wield_index(self)
+       static int l_get_wield_index(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
-               luaL_checkstring(L, 2);
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
-               // itemstring
-               const char *itemstring = luaL_checkstring(L, 2);
-               infostream<<"ObjectRef::l_add_to_inventory_later(): id="<<co->getId()
-                               <<" itemstring=\""<<itemstring<<"\""<<std::endl;
                // Do it
-               std::istringstream is(itemstring, std::ios::binary);
-               ServerEnvironment *env = co->getEnv();
-               assert(env);
-               IGameDef *gamedef = env->getGameDef();
-               InventoryItem *item = InventoryItem::deSerialize(is, gamedef);
-               infostream<<"item="<<env<<std::endl;
-               co->addToInventoryLater(item);
-               // Return
-               return 0;
+               lua_pushinteger(L, co->getWieldIndex() + 1);
+               return 1;
        }
 
-       // set_hp(self, hp)
-       // hp = number of hitpoints (2 * number of hearts)
-       // returns: nil
-       static int l_set_hp(lua_State *L)
+       // get_wielded_item(self)
+       static int l_get_wielded_item(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
-               luaL_checknumber(L, 2);
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
-               int hp = lua_tonumber(L, 2);
-               infostream<<"ObjectRef::l_set_hp(): id="<<co->getId()
-                               <<" hp="<<hp<<std::endl;
                // Do it
-               co->setHP(hp);
-               // Return
-               return 0;
+               LuaItemStack::create(L, co->getWieldedItem());
+               return 1;
        }
 
-       // get_hp(self)
-       // returns: number of hitpoints (2 * number of hearts)
-       // 0 if not applicable to this type of object
-       static int l_get_hp(lua_State *L)
+       // set_wielded_item(self, itemstack or itemstring or table or nil)
+       static int l_set_wielded_item(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
                ServerActiveObject *co = getobject(ref);
                if(co == NULL) return 0;
-               int hp = co->getHP();
-               infostream<<"ObjectRef::l_get_hp(): id="<<co->getId()
-                               <<" hp="<<hp<<std::endl;
-               // Return
-               lua_pushnumber(L, hp);
+               // Do it
+               ItemStack item = read_item(L, 2);
+               bool success = co->setWieldedItem(item);
+               lua_pushboolean(L, success);
                return 1;
        }
 
@@ -2031,112 +2355,45 @@ private:
        }
 
        // DEPRECATED
-       // get_entity_name(self)
-       static int l_get_entity_name(lua_State *L)
-       {
-               ObjectRef *ref = checkobject(L, 1);
-               LuaEntitySAO *co = getluaobject(ref);
-               if(co == NULL) return 0;
-               // Do it
-               std::string name = co->getName();
-               lua_pushstring(L, name.c_str());
-               return 1;
-       }
-       
-       // get_luaentity(self)
-       static int l_get_luaentity(lua_State *L)
-       {
-               ObjectRef *ref = checkobject(L, 1);
-               LuaEntitySAO *co = getluaobject(ref);
-               if(co == NULL) return 0;
-               // Do it
-               luaentity_get(L, co->getId());
-               return 1;
-       }
-       
-       /* Player-only */
-       
-       // get_player_name(self)
-       static int l_get_player_name(lua_State *L)
-       {
-               ObjectRef *ref = checkobject(L, 1);
-               ServerRemotePlayer *player = getplayer(ref);
-               if(player == NULL){
-                       lua_pushnil(L);
-                       return 1;
-               }
-               // Do it
-               lua_pushstring(L, player->getName());
-               return 1;
-       }
-       
-       // get_inventory(self)
-       static int l_get_inventory(lua_State *L)
-       {
-               ObjectRef *ref = checkobject(L, 1);
-               ServerRemotePlayer *player = getplayer(ref);
-               if(player == NULL) return 0;
-               // Do it
-               InvRef::createPlayer(L, player);
-               return 1;
-       }
-
-       // deprecated: inventory_set_list(self, name, {item1, item2, ...})
-       static int l_inventory_set_list(lua_State *L)
-       {
-               infostream<<"Deprecated: inventory_set_list"<<std::endl;
-               ObjectRef *ref = checkobject(L, 1);
-               ServerRemotePlayer *player = getplayer(ref);
-               if(player == NULL) return 0;
-               const char *name = luaL_checkstring(L, 2);
-               // Do it
-               inventory_set_list_from_lua(&player->inventory, name, L, 3,
-                               player->getEnv()->getGameDef(), PLAYER_INVENTORY_SIZE);
-               player->m_inventory_not_sent = true;
-               return 0;
-       }
-
-       // deprecated: inventory_get_list(self, name)
-       static int l_inventory_get_list(lua_State *L)
+       // get_entity_name(self)
+       static int l_get_entity_name(lua_State *L)
        {
-               infostream<<"Deprecated: inventory_get_list"<<std::endl;
                ObjectRef *ref = checkobject(L, 1);
-               ServerRemotePlayer *player = getplayer(ref);
-               if(player == NULL) return 0;
-               const char *name = luaL_checkstring(L, 2);
+               LuaEntitySAO *co = getluaobject(ref);
+               if(co == NULL) return 0;
                // Do it
-               inventory_get_list_to_lua(&player->inventory, name, L);
+               std::string name = co->getName();
+               lua_pushstring(L, name.c_str());
                return 1;
        }
-
-       // get_wielded_itemstring(self)
-       static int l_get_wielded_itemstring(lua_State *L)
+       
+       // get_luaentity(self)
+       static int l_get_luaentity(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
-               ServerRemotePlayer *player = getplayer(ref);
-               if(player == NULL) return 0;
+               LuaEntitySAO *co = getluaobject(ref);
+               if(co == NULL) return 0;
                // Do it
-               InventoryItem *item = player->getWieldedItem();
-               if(item == NULL){
-                       lua_pushnil(L);
-                       return 1;
-               }
-               lua_pushstring(L, item->getItemString().c_str());
+               luaentity_get(L, co->getId());
                return 1;
        }
-
-       // get_wielded_item(self)
-       static int l_get_wielded_item(lua_State *L)
+       
+       /* Player-only */
+       
+       // get_player_name(self)
+       static int l_get_player_name(lua_State *L)
        {
                ObjectRef *ref = checkobject(L, 1);
                ServerRemotePlayer *player = getplayer(ref);
-               if(player == NULL) return 0;
+               if(player == NULL){
+                       lua_pushnil(L);
+                       return 1;
+               }
                // Do it
-               InventoryItem *item0 = player->getWieldedItem();
-               push_stack_item(L, item0);
+               lua_pushstring(L, player->getName());
                return 1;
        }
-
+       
        // get_look_dir(self)
        static int l_get_look_dir(lua_State *L)
        {
@@ -2243,12 +2500,13 @@ const luaL_reg ObjectRef::methods[] = {
        method(ObjectRef, moveto),
        method(ObjectRef, punch),
        method(ObjectRef, right_click),
-       method(ObjectRef, get_wield_digging_properties),
-       method(ObjectRef, damage_wielded_item),
-       method(ObjectRef, add_to_inventory),
-       method(ObjectRef, add_to_inventory_later),
        method(ObjectRef, set_hp),
        method(ObjectRef, get_hp),
+       method(ObjectRef, get_inventory),
+       method(ObjectRef, get_wield_list),
+       method(ObjectRef, get_wield_index),
+       method(ObjectRef, get_wielded_item),
+       method(ObjectRef, set_wielded_item),
        // LuaEntitySAO-only
        method(ObjectRef, setvelocity),
        method(ObjectRef, getvelocity),
@@ -2262,11 +2520,6 @@ const luaL_reg ObjectRef::methods[] = {
        method(ObjectRef, get_luaentity),
        // Player-only
        method(ObjectRef, get_player_name),
-       method(ObjectRef, get_inventory),
-       method(ObjectRef, inventory_set_list), // deprecated
-       method(ObjectRef, inventory_get_list), // deprecated
-       method(ObjectRef, get_wielded_itemstring),
-       method(ObjectRef, get_wielded_item),
        method(ObjectRef, get_look_dir),
        method(ObjectRef, get_look_pitch),
        method(ObjectRef, get_look_yaw),
@@ -2408,7 +2661,7 @@ private:
                }
        }
 
-       // EnvRef:add_entity(pos, entityname)
+       // EnvRef:add_entity(pos, entityname) -> ObjectRef or nil
        // pos = {x=num, y=num, z=num}
        static int l_add_entity(lua_State *L)
        {
@@ -2431,22 +2684,29 @@ private:
                return 1;
        }
 
-       // EnvRef:add_item(pos, inventorystring)
+       // EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
        // pos = {x=num, y=num, z=num}
        static int l_add_item(lua_State *L)
        {
-               infostream<<"EnvRef::l_add_item()"<<std::endl;
+               //infostream<<"EnvRef::l_add_item()"<<std::endl;
                EnvRef *o = checkobject(L, 1);
                ServerEnvironment *env = o->m_env;
                if(env == NULL) return 0;
                // pos
                v3f pos = checkFloatPos(L, 2);
-               // inventorystring
-               const char *inventorystring = luaL_checkstring(L, 3);
+               // item
+               ItemStack item = read_item(L, 3);
+               if(item.empty() || !item.isKnown(get_server(L)->idef()))
+                       return 0;
                // Do it
-               ServerActiveObject *obj = new ItemSAO(env, pos, inventorystring);
-               env->addActiveObject(obj);
-               return 0;
+               ServerActiveObject *obj = new ItemSAO(env, pos, item.getItemString());
+               int objectid = env->addActiveObject(obj);
+               // If failed to add, return nothing (reads as nil)
+               if(objectid == 0)
+                       return 0;
+               // Return ObjectRef
+               objectref_get_or_create(L, obj);
+               return 1;
        }
 
        // EnvRef:add_rat(pos)
@@ -2627,55 +2887,6 @@ const luaL_reg EnvRef::methods[] = {
        Global functions
 */
 
-static int l_register_nodedef_defaults(lua_State *L)
-{
-       luaL_checktype(L, 1, LUA_TTABLE);
-
-       lua_pushvalue(L, 1); // Explicitly put parameter 1 on top of stack
-       lua_setfield(L, LUA_REGISTRYINDEX, "minetest_nodedef_default");
-
-       return 0;
-}
-
-// Register new object prototype
-// register_entity(name, prototype)
-static int l_register_entity(lua_State *L)
-{
-       std::string name = luaL_checkstring(L, 1);
-       check_modname_prefix(L, name);
-       //infostream<<"register_entity: "<<name<<std::endl;
-       luaL_checktype(L, 2, LUA_TTABLE);
-
-       // Get minetest.registered_entities
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_entities");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       int registered_entities = lua_gettop(L);
-       lua_pushvalue(L, 2); // Object = param 2 -> stack top
-       // registered_entities[name] = object
-       lua_setfield(L, registered_entities, name.c_str());
-       
-       // Get registered object to top of stack
-       lua_pushvalue(L, 2);
-
-       // Set name field
-       lua_pushvalue(L, 1);
-       lua_setfield(L, -2, "name");
-       
-       // Set __index to point to itself
-       lua_pushvalue(L, -1);
-       lua_setfield(L, -2, "__index");
-
-       // Set metatable.__index = metatable
-       luaL_getmetatable(L, "minetest.entity");
-       lua_pushvalue(L, -1); // duplicate metatable
-       lua_setfield(L, -2, "__index");
-       // Set object metatable
-       lua_setmetatable(L, -2);
-
-       return 0; /* number of results */
-}
-
 class LuaABM : public ActiveBlockModifier
 {
 private:
@@ -2738,465 +2949,329 @@ public:
                
                // Call action
                luaL_checktype(L, -1, LUA_TTABLE);
-               lua_getfield(L, -1, "action");
-               luaL_checktype(L, -1, LUA_TFUNCTION);
-               push_v3s16(L, p);
-               pushnode(L, n, env->getGameDef()->ndef());
-               lua_pushnumber(L, active_object_count);
-               lua_pushnumber(L, active_object_count_wider);
-               if(lua_pcall(L, 4, 0, 0))
-                       script_error(L, "error: %s", lua_tostring(L, -1));
-       }
-};
-
-// register_abm({...})
-static int l_register_abm(lua_State *L)
-{
-       //infostream<<"register_abm"<<std::endl;
-       luaL_checktype(L, 1, LUA_TTABLE);
-
-       // Get minetest.registered_abms
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_abms");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       int registered_abms = lua_gettop(L);
-
-       int id = 1;
-       // Find free id
-       for(;;){
-               lua_pushnumber(L, id);
-               lua_gettable(L, registered_abms);
-               if(lua_isnil(L, -1))
-                       break;
-               lua_pop(L, 1);
-               id++;
-       }
-       lua_pop(L, 1);
-
-       infostream<<"register_abm: id="<<id<<std::endl;
-
-       // registered_abms[id] = spec
-       lua_pushnumber(L, id);
-       lua_pushvalue(L, 1);
-       lua_settable(L, registered_abms);
-       
-       return 0; /* number of results */
-}
-
-// register_tool(name, {lots of stuff})
-static int l_register_tool(lua_State *L)
-{
-       std::string name = luaL_checkstring(L, 1);
-       check_modname_prefix(L, name);
-       //infostream<<"register_tool: "<<name<<std::endl;
-       luaL_checktype(L, 2, LUA_TTABLE);
-       int table = 2;
-
-       // Get the writable tool definition manager from the server
-       IWritableToolDefManager *tooldef =
-                       get_server(L)->getWritableToolDefManager();
-       
-       ToolDefinition def = read_tool_definition(L, table);
-
-       tooldef->registerTool(name, def);
-       return 0; /* number of results */
-}
-
-// register_craftitem(name, {lots of stuff})
-static int l_register_craftitem(lua_State *L)
-{
-       std::string name = luaL_checkstring(L, 1);
-       check_modname_prefix(L, name);
-       //infostream<<"register_craftitem: "<<name<<std::endl;
-       luaL_checktype(L, 2, LUA_TTABLE);
-       int table = 2;
-
-       // Get the writable CraftItem definition manager from the server
-       IWritableCraftItemDefManager *craftitemdef =
-                       get_server(L)->getWritableCraftItemDefManager();
-       
-       // Check if on_drop is defined
-       lua_getfield(L, table, "on_drop");
-       bool got_on_drop = !lua_isnil(L, -1);
-       lua_pop(L, 1);
-
-       // Check if on_use is defined
-       lua_getfield(L, table, "on_use");
-       bool got_on_use = !lua_isnil(L, -1);
-       lua_pop(L, 1);
-
-       CraftItemDefinition def;
-       
-       getstringfield(L, table, "image", def.imagename);
-       getstringfield(L, table, "cookresult_itemstring", def.cookresult_item);
-       getfloatfield(L, table, "furnace_cooktime", def.furnace_cooktime);
-       getfloatfield(L, table, "furnace_burntime", def.furnace_burntime);
-       def.usable = getboolfield_default(L, table, "usable", got_on_use);
-       getboolfield(L, table, "liquids_pointable", def.liquids_pointable);
-       def.dropcount = getintfield_default(L, table, "dropcount", def.dropcount);
-       def.stack_max = getintfield_default(L, table, "stack_max", def.stack_max);
-
-       // If an on_drop callback is defined, force dropcount to 1
-       if (got_on_drop)
-               def.dropcount = 1;
-
-       // Register it
-       craftitemdef->registerCraftItem(name, def);
-
-       lua_pushvalue(L, table);
-       scriptapi_add_craftitem(L, name.c_str());
-
-       return 0; /* number of results */
-}
-
-// register_node(name, {lots of stuff})
-static int l_register_node(lua_State *L)
-{
-       std::string name = luaL_checkstring(L, 1);
-       check_modname_prefix(L, name);
-       //infostream<<"register_node: "<<name<<std::endl;
-       luaL_checktype(L, 2, LUA_TTABLE);
-       int nodedef_table = 2;
-
-       // Get the writable node definition manager from the server
-       IWritableNodeDefManager *nodedef =
-                       get_server(L)->getWritableNodeDefManager();
-       
-       // Get default node definition from registry
-       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_nodedef_default");
-       int nodedef_default = lua_gettop(L);
-
-       /*
-               Add to minetest.registered_nodes with default as metatable
-       */
-       
-       // Get the node definition table given as parameter
-       lua_pushvalue(L, nodedef_table);
-
-       // Set __index to point to itself
-       lua_pushvalue(L, -1);
-       lua_setfield(L, -2, "__index");
-
-       // Set nodedef_default as metatable for the definition
-       lua_pushvalue(L, nodedef_default);
-       lua_setmetatable(L, nodedef_table);
-       
-       // minetest.registered_nodes[name] = nodedef
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_nodes");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       lua_pushstring(L, name.c_str());
-       lua_pushvalue(L, nodedef_table);
-       lua_settable(L, -3);
-
-       /*
-               Create definition
-       */
-       
-       ContentFeatures f;
-
-       // Default to getting the corresponding NodeItem when dug
-       f.dug_item = std::string("NodeItem \"")+name+"\" 1";
-       
-       // Default to unknown_block.png as all textures
-       f.setAllTextures("unknown_block.png");
-
-       /*
-               Read definiton from Lua
-       */
-
-       f.name = name;
-       
-       /* Visual definition */
-
-       f.drawtype = (NodeDrawType)getenumfield(L, nodedef_table, "drawtype", es_DrawType,
-                       NDT_NORMAL);
-       getfloatfield(L, nodedef_table, "visual_scale", f.visual_scale);
-
-       lua_getfield(L, nodedef_table, "tile_images");
-       if(lua_istable(L, -1)){
-               int table = lua_gettop(L);
-               lua_pushnil(L);
-               int i = 0;
-               while(lua_next(L, table) != 0){
-                       // key at index -2 and value at index -1
-                       if(lua_isstring(L, -1))
-                               f.tname_tiles[i] = lua_tostring(L, -1);
-                       else
-                               f.tname_tiles[i] = "";
-                       // removes value, keeps key for next iteration
-                       lua_pop(L, 1);
-                       i++;
-                       if(i==6){
-                               lua_pop(L, 1);
-                               break;
-                       }
-               }
-               // Copy last value to all remaining textures
-               if(i >= 1){
-                       std::string lastname = f.tname_tiles[i-1];
-                       while(i < 6){
-                               f.tname_tiles[i] = lastname;
-                               i++;
-                       }
-               }
-       }
-       lua_pop(L, 1);
-
-       getstringfield(L, nodedef_table, "inventory_image", f.tname_inventory);
-
-       lua_getfield(L, nodedef_table, "special_materials");
-       if(lua_istable(L, -1)){
-               int table = lua_gettop(L);
-               lua_pushnil(L);
-               int i = 0;
-               while(lua_next(L, table) != 0){
-                       // key at index -2 and value at index -1
-                       int smtable = lua_gettop(L);
-                       std::string tname = getstringfield_default(
-                                       L, smtable, "image", "");
-                       bool backface_culling = getboolfield_default(
-                                       L, smtable, "backface_culling", true);
-                       MaterialSpec mspec(tname, backface_culling);
-                       f.setSpecialMaterial(i, mspec);
-                       // removes value, keeps key for next iteration
-                       lua_pop(L, 1);
-                       i++;
-                       if(i==6){
-                               lua_pop(L, 1);
-                               break;
-                       }
-               }
-       }
-       lua_pop(L, 1);
-
-       f.alpha = getintfield_default(L, nodedef_table, "alpha", 255);
-
-       /* Other stuff */
-       
-       lua_getfield(L, nodedef_table, "post_effect_color");
-       if(!lua_isnil(L, -1))
-               f.post_effect_color = readARGB8(L, -1);
-       lua_pop(L, 1);
-
-       f.param_type = (ContentParamType)getenumfield(L, nodedef_table, "paramtype",
-                       es_ContentParamType, CPT_NONE);
-       
-       // True for all ground-like things like stone and mud, false for eg. trees
-       getboolfield(L, nodedef_table, "is_ground_content", f.is_ground_content);
-       f.light_propagates = (f.param_type == CPT_LIGHT);
-       warn_if_field_exists(L, nodedef_table, "light_propagates",
-                       "deprecated: determined from paramtype");
-       getboolfield(L, nodedef_table, "sunlight_propagates", f.sunlight_propagates);
-       // This is used for collision detection.
-       // Also for general solidness queries.
-       getboolfield(L, nodedef_table, "walkable", f.walkable);
-       // Player can point to these
-       getboolfield(L, nodedef_table, "pointable", f.pointable);
-       // Player can dig these
-       getboolfield(L, nodedef_table, "diggable", f.diggable);
-       // Player can climb these
-       getboolfield(L, nodedef_table, "climbable", f.climbable);
-       // Player can build on these
-       getboolfield(L, nodedef_table, "buildable_to", f.buildable_to);
-       // If true, param2 is set to direction when placed. Used for torches.
-       // NOTE: the direction format is quite inefficient and should be changed
-       getboolfield(L, nodedef_table, "wall_mounted", f.wall_mounted);
-       // Whether this content type often contains mineral.
-       // Used for texture atlas creation.
-       // Currently only enabled for CONTENT_STONE.
-       getboolfield(L, nodedef_table, "often_contains_mineral", f.often_contains_mineral);
-       // Inventory item string as which the node appears in inventory when dug.
-       // Mineral overrides this.
-       getstringfield(L, nodedef_table, "dug_item", f.dug_item);
-       // Extra dug item and its rarity
-       getstringfield(L, nodedef_table, "extra_dug_item", f.extra_dug_item);
-       // Usual get interval for extra dug item
-       getintfield(L, nodedef_table, "extra_dug_item_rarity", f.extra_dug_item_rarity);
-       // Metadata name of node (eg. "furnace")
-       getstringfield(L, nodedef_table, "metadata_name", f.metadata_name);
-       // Whether the node is non-liquid, source liquid or flowing liquid
-       f.liquid_type = (LiquidType)getenumfield(L, nodedef_table, "liquidtype",
-                       es_LiquidType, LIQUID_NONE);
-       // If the content is liquid, this is the flowing version of the liquid.
-       getstringfield(L, nodedef_table, "liquid_alternative_flowing",
-                       f.liquid_alternative_flowing);
-       // If the content is liquid, this is the source version of the liquid.
-       getstringfield(L, nodedef_table, "liquid_alternative_source",
-                       f.liquid_alternative_source);
-       // Viscosity for fluid flow, ranging from 1 to 7, with
-       // 1 giving almost instantaneous propagation and 7 being
-       // the slowest possible
-       f.liquid_viscosity = getintfield_default(L, nodedef_table,
-                       "liquid_viscosity", f.liquid_viscosity);
-       // Amount of light the node emits
-       f.light_source = getintfield_default(L, nodedef_table,
-                       "light_source", f.light_source);
-       f.damage_per_second = getintfield_default(L, nodedef_table,
-                       "damage_per_second", f.damage_per_second);
-       
-       lua_getfield(L, nodedef_table, "selection_box");
-       if(lua_istable(L, -1)){
-               f.selection_box.type = (NodeBoxType)getenumfield(L, -1, "type",
-                               es_NodeBoxType, NODEBOX_REGULAR);
+               lua_getfield(L, -1, "action");
+               luaL_checktype(L, -1, LUA_TFUNCTION);
+               push_v3s16(L, p);
+               pushnode(L, n, env->getGameDef()->ndef());
+               lua_pushnumber(L, active_object_count);
+               lua_pushnumber(L, active_object_count_wider);
+               if(lua_pcall(L, 4, 0, 0))
+                       script_error(L, "error: %s", lua_tostring(L, -1));
+       }
+};
 
-               lua_getfield(L, -1, "fixed");
-               if(lua_istable(L, -1))
-                       f.selection_box.fixed = read_aabbox3df32(L, -1, BS);
-               lua_pop(L, 1);
+// debug(text)
+// Writes a line to dstream
+static int l_debug(lua_State *L)
+{
+       std::string text = lua_tostring(L, 1);
+       dstream << text << std::endl;
+       return 0;
+}
 
-               lua_getfield(L, -1, "wall_top");
-               if(lua_istable(L, -1))
-                       f.selection_box.wall_top = read_aabbox3df32(L, -1, BS);
-               lua_pop(L, 1);
+// log([level,] text)
+// Writes a line to the logger.
+// The one-argument version logs to infostream.
+// The two-argument version accept a log level: error, action, info, or verbose.
+static int l_log(lua_State *L)
+{
+       std::string text;
+       LogMessageLevel level = LMT_INFO;
+       if(lua_isnone(L, 2))
+       {
+               text = lua_tostring(L, 1);
+       }
+       else
+       {
+               std::string levelname = lua_tostring(L, 1);
+               text = lua_tostring(L, 2);
+               if(levelname == "error")
+                       level = LMT_ERROR;
+               else if(levelname == "action")
+                       level = LMT_ACTION;
+               else if(levelname == "verbose")
+                       level = LMT_VERBOSE;
+       }
+       log_printline(level, text);
+       return 0;
+}
 
-               lua_getfield(L, -1, "wall_bottom");
-               if(lua_istable(L, -1))
-                       f.selection_box.wall_bottom = read_aabbox3df32(L, -1, BS);
-               lua_pop(L, 1);
+// register_item_raw({lots of stuff})
+static int l_register_item_raw(lua_State *L)
+{
+       luaL_checktype(L, 1, LUA_TTABLE);
+       int table = 1;
 
-               lua_getfield(L, -1, "wall_side");
-               if(lua_istable(L, -1))
-                       f.selection_box.wall_side = read_aabbox3df32(L, -1, BS);
-               lua_pop(L, 1);
+       // Get the writable item and node definition managers from the server
+       IWritableItemDefManager *idef =
+                       get_server(L)->getWritableItemDefManager();
+       IWritableNodeDefManager *ndef =
+                       get_server(L)->getWritableNodeDefManager();
+
+       // Check if name is defined
+       lua_getfield(L, table, "name");
+       if(lua_isstring(L, -1)){
+               std::string name = lua_tostring(L, -1);
+               infostream<<"register_item_raw: "<<name<<std::endl;
+       } else {
+               throw LuaError(L, "register_item_raw: name is not defined or not a string");
        }
-       lua_pop(L, 1);
 
-       lua_getfield(L, nodedef_table, "material");
-       if(lua_istable(L, -1)){
-               f.material.diggability = (Diggability)getenumfield(L, -1, "diggability",
-                               es_Diggability, DIGGABLE_NORMAL);
-               
-               getfloatfield(L, -1, "constant_time", f.material.constant_time);
-               getfloatfield(L, -1, "weight", f.material.weight);
-               getfloatfield(L, -1, "crackiness", f.material.crackiness);
-               getfloatfield(L, -1, "crumbliness", f.material.crumbliness);
-               getfloatfield(L, -1, "cuttability", f.material.cuttability);
-               getfloatfield(L, -1, "flammability", f.material.flammability);
+       // Check if on_use is defined
+
+       // Read the item definition and register it
+       ItemDefinition def = read_item_definition(L, table);
+       idef->registerItem(def);
+
+       // Read the node definition (content features) and register it
+       if(def.type == ITEM_NODE)
+       {
+               ContentFeatures f = read_content_features(L, table);
+               ndef->set(f.name, f);
        }
-       lua_pop(L, 1);
 
-       getstringfield(L, nodedef_table, "cookresult_itemstring", f.cookresult_item);
-       getfloatfield(L, nodedef_table, "furnace_cooktime", f.furnace_cooktime);
-       getfloatfield(L, nodedef_table, "furnace_burntime", f.furnace_burntime);
-       
-       /*
-               Register it
-       */
-       
-       nodedef->set(name, f);
-       
        return 0; /* number of results */
 }
 
-// alias_node(name, convert_to_name)
-static int l_alias_node(lua_State *L)
+// register_alias_raw(name, convert_to_name)
+static int l_register_alias_raw(lua_State *L)
 {
        std::string name = luaL_checkstring(L, 1);
        std::string convert_to = luaL_checkstring(L, 2);
 
-       // Get the writable node definition manager from the server
-       IWritableNodeDefManager *nodedef =
-                       get_server(L)->getWritableNodeDefManager();
+       // Get the writable item definition manager from the server
+       IWritableItemDefManager *idef =
+                       get_server(L)->getWritableItemDefManager();
        
-       nodedef->setAlias(name, convert_to);
+       idef->registerAlias(name, convert_to);
        
        return 0; /* number of results */
 }
 
-// alias_tool(name, convert_to_name)
-static int l_alias_tool(lua_State *L)
+// helper for register_craft
+static bool read_craft_recipe_shaped(lua_State *L, int index,
+               int &width, std::vector<std::string> &recipe)
 {
-       std::string name = luaL_checkstring(L, 1);
-       std::string convert_to = luaL_checkstring(L, 2);
+       if(index < 0)
+               index = lua_gettop(L) + 1 + index;
 
-       // Get the writable tool definition manager from the server
-       IWritableToolDefManager *tooldef =
-                       get_server(L)->getWritableToolDefManager();
-       
-       tooldef->setAlias(name, convert_to);
+       if(!lua_istable(L, index))
+               return false;
 
-       return 0; /* number of results */
+       lua_pushnil(L);
+       int rowcount = 0;
+       while(lua_next(L, index) != 0){
+               int colcount = 0;
+               // key at index -2 and value at index -1
+               if(!lua_istable(L, -1))
+                       return false;
+               int table2 = lua_gettop(L);
+               lua_pushnil(L);
+               while(lua_next(L, table2) != 0){
+                       // key at index -2 and value at index -1
+                       if(!lua_isstring(L, -1))
+                               return false;
+                       recipe.push_back(lua_tostring(L, -1));
+                       // removes value, keeps key for next iteration
+                       lua_pop(L, 1);
+                       colcount++;
+               }
+               if(rowcount == 0){
+                       width = colcount;
+               } else {
+                       if(colcount != width)
+                               return false;
+               }
+               // removes value, keeps key for next iteration
+               lua_pop(L, 1);
+               rowcount++;
+       }
+       return width != 0;
 }
 
-// alias_craftitem(name, convert_to_name)
-static int l_alias_craftitem(lua_State *L)
+// helper for register_craft
+static bool read_craft_recipe_shapeless(lua_State *L, int index,
+               std::vector<std::string> &recipe)
 {
-       std::string name = luaL_checkstring(L, 1);
-       std::string convert_to = luaL_checkstring(L, 2);
+       if(index < 0)
+               index = lua_gettop(L) + 1 + index;
 
-       // Get the writable CraftItem definition manager from the server
-       IWritableCraftItemDefManager *craftitemdef =
-                       get_server(L)->getWritableCraftItemDefManager();
-       
-       craftitemdef->setAlias(name, convert_to);
+       if(!lua_istable(L, index))
+               return false;
 
-       return 0; /* number of results */
+       lua_pushnil(L);
+       while(lua_next(L, index) != 0){
+               // key at index -2 and value at index -1
+               if(!lua_isstring(L, -1))
+                       return false;
+               recipe.push_back(lua_tostring(L, -1));
+               // removes value, keeps key for next iteration
+               lua_pop(L, 1);
+       }
+       return true;
 }
 
+// helper for register_craft
+static bool read_craft_replacements(lua_State *L, int index,
+               CraftReplacements &replacements)
+{
+       if(index < 0)
+               index = lua_gettop(L) + 1 + index;
+
+       if(!lua_istable(L, index))
+               return false;
+
+       lua_pushnil(L);
+       while(lua_next(L, index) != 0){
+               // key at index -2 and value at index -1
+               if(!lua_istable(L, -1))
+                       return false;
+               lua_rawgeti(L, -1, 1);
+               if(!lua_isstring(L, -1))
+                       return false;
+               std::string replace_from = lua_tostring(L, -1);
+               lua_pop(L, 1);
+               lua_rawgeti(L, -1, 2);
+               if(!lua_isstring(L, -1))
+                       return false;
+               std::string replace_to = lua_tostring(L, -1);
+               lua_pop(L, 1);
+               replacements.pairs.push_back(
+                               std::make_pair(replace_from, replace_to));
+               // removes value, keeps key for next iteration
+               lua_pop(L, 1);
+       }
+       return true;
+}
 // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
 static int l_register_craft(lua_State *L)
 {
        //infostream<<"register_craft"<<std::endl;
        luaL_checktype(L, 1, LUA_TTABLE);
-       int table0 = 1;
+       int table = 1;
 
        // Get the writable craft definition manager from the server
        IWritableCraftDefManager *craftdef =
                        get_server(L)->getWritableCraftDefManager();
        
-       std::string output;
-       int width = 0;
-       std::vector<std::string> input;
-
-       lua_getfield(L, table0, "output");
-       luaL_checktype(L, -1, LUA_TSTRING);
-       if(lua_isstring(L, -1))
-               output = lua_tostring(L, -1);
-       lua_pop(L, 1);
+       std::string type = getstringfield_default(L, table, "type", "shaped");
 
-       lua_getfield(L, table0, "recipe");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       if(lua_istable(L, -1)){
-               int table1 = lua_gettop(L);
-               lua_pushnil(L);
-               int rowcount = 0;
-               while(lua_next(L, table1) != 0){
-                       int colcount = 0;
-                       // key at index -2 and value at index -1
-                       luaL_checktype(L, -1, LUA_TTABLE);
-                       if(lua_istable(L, -1)){
-                               int table2 = lua_gettop(L);
-                               lua_pushnil(L);
-                               while(lua_next(L, table2) != 0){
-                                       // key at index -2 and value at index -1
-                                       luaL_checktype(L, -1, LUA_TSTRING);
-                                       input.push_back(lua_tostring(L, -1));
-                                       // removes value, keeps key for next iteration
-                                       lua_pop(L, 1);
-                                       colcount++;
-                               }
-                       }
-                       if(rowcount == 0){
-                               width = colcount;
-                       } else {
-                               if(colcount != width){
-                                       std::string error;
-                                       error += "Invalid crafting recipe (output=\""
-                                                       + output + "\")";
-                                       throw LuaError(L, error);
-                               }
-                       }
-                       // removes value, keeps key for next iteration
-                       lua_pop(L, 1);
-                       rowcount++;
+       /*
+               CraftDefinitionShaped
+       */
+       if(type == "shaped"){
+               std::string output = getstringfield_default(L, table, "output", "");
+               if(output == "")
+                       throw LuaError(L, "Crafting definition is missing an output");
+
+               int width = 0;
+               std::vector<std::string> recipe;
+               lua_getfield(L, table, "recipe");
+               if(lua_isnil(L, -1))
+                       throw LuaError(L, "Crafting definition is missing a recipe"
+                                       " (output=\"" + output + "\")");
+               if(!read_craft_recipe_shaped(L, -1, width, recipe))
+                       throw LuaError(L, "Invalid crafting recipe"
+                                       " (output=\"" + output + "\")");
+
+               CraftReplacements replacements;
+               lua_getfield(L, table, "replacements");
+               if(!lua_isnil(L, -1))
+               {
+                       if(!read_craft_replacements(L, -1, replacements))
+                               throw LuaError(L, "Invalid replacements"
+                                               " (output=\"" + output + "\")");
                }
+
+               CraftDefinition *def = new CraftDefinitionShaped(
+                               output, width, recipe, replacements);
+               craftdef->registerCraft(def);
        }
-       lua_pop(L, 1);
+       /*
+               CraftDefinitionShapeless
+       */
+       else if(type == "shapeless"){
+               std::string output = getstringfield_default(L, table, "output", "");
+               if(output == "")
+                       throw LuaError(L, "Crafting definition (shapeless)"
+                                       " is missing an output");
+
+               std::vector<std::string> recipe;
+               lua_getfield(L, table, "recipe");
+               if(lua_isnil(L, -1))
+                       throw LuaError(L, "Crafting definition (shapeless)"
+                                       " is missing a recipe"
+                                       " (output=\"" + output + "\")");
+               if(!read_craft_recipe_shapeless(L, -1, recipe))
+                       throw LuaError(L, "Invalid crafting recipe"
+                                       " (output=\"" + output + "\")");
+
+               CraftReplacements replacements;
+               lua_getfield(L, table, "replacements");
+               if(!lua_isnil(L, -1))
+               {
+                       if(!read_craft_replacements(L, -1, replacements))
+                               throw LuaError(L, "Invalid replacements"
+                                               " (output=\"" + output + "\")");
+               }
+
+               CraftDefinition *def = new CraftDefinitionShapeless(
+                               output, recipe, replacements);
+               craftdef->registerCraft(def);
+       }
+       /*
+               CraftDefinitionToolRepair
+       */
+       else if(type == "toolrepair"){
+               float additional_wear = getfloatfield_default(L, table,
+                               "additional_wear", 0.0);
+
+               CraftDefinition *def = new CraftDefinitionToolRepair(
+                               additional_wear);
+               craftdef->registerCraft(def);
+       }
+       /*
+               CraftDefinitionCooking
+       */
+       else if(type == "cooking"){
+               std::string output = getstringfield_default(L, table, "output", "");
+               if(output == "")
+                       throw LuaError(L, "Crafting definition (cooking)"
+                                       " is missing an output");
+
+               std::string recipe = getstringfield_default(L, table, "recipe", "");
+               if(recipe == "")
+                       throw LuaError(L, "Crafting definition (cooking)"
+                                       " is missing a recipe"
+                                       " (output=\"" + output + "\")");
+
+               float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
+
+               CraftDefinition *def = new CraftDefinitionCooking(
+                               output, recipe, cooktime);
+               craftdef->registerCraft(def);
+       }
+       /*
+               CraftDefinitionFuel
+       */
+       else if(type == "fuel"){
+               std::string recipe = getstringfield_default(L, table, "recipe", "");
+               if(recipe == "")
+                       throw LuaError(L, "Crafting definition (fuel)"
+                                       " is missing a recipe");
+
+               float burntime = getfloatfield_default(L, table, "burntime", 1.0);
 
-       CraftDefinition def(output, width, input);
-       craftdef->registerCraft(def);
+               CraftDefinition *def = new CraftDefinitionFuel(
+                               recipe, burntime);
+               craftdef->registerCraft(def);
+       }
+       else
+       {
+               throw LuaError(L, "Unknown crafting definition type: \"" + type + "\"");
+       }
 
+       lua_pop(L, 1);
        return 0; /* number of results */
 }
 
@@ -3294,15 +3369,19 @@ static int l_get_inventory(lua_State *L)
        return 1;
 }
 
+// get_current_modname()
+static int l_get_current_modname(lua_State *L)
+{
+       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
+       return 1;
+}
+
 // get_modpath(modname)
 static int l_get_modpath(lua_State *L)
 {
        const char *modname = luaL_checkstring(L, 1);
-       // Get server from registry
-       lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server");
-       Server *server = (Server*)lua_touserdata(L, -1);
        // Do it
-       const ModSpec *mod = server->getModSpec(modname);
+       const ModSpec *mod = get_server(L)->getModSpec(modname);
        if(!mod){
                lua_pushnil(L);
                return 1;
@@ -3312,34 +3391,22 @@ static int l_get_modpath(lua_State *L)
 }
 
 static const struct luaL_Reg minetest_f [] = {
-       {"register_nodedef_defaults", l_register_nodedef_defaults},
-       {"register_entity", l_register_entity},
-       {"register_tool", l_register_tool},
-       {"register_craftitem", l_register_craftitem},
-       {"register_node", l_register_node},
+       {"debug", l_debug},
+       {"log", l_log},
+       {"register_item_raw", l_register_item_raw},
+       {"register_alias_raw", l_register_alias_raw},
        {"register_craft", l_register_craft},
-       {"register_abm", l_register_abm},
-       {"alias_node", l_alias_node},
-       {"alias_tool", l_alias_tool},
-       {"alias_craftitem", l_alias_craftitem},
        {"setting_get", l_setting_get},
        {"setting_getbool", l_setting_getbool},
        {"chat_send_all", l_chat_send_all},
        {"chat_send_player", l_chat_send_player},
        {"get_player_privs", l_get_player_privs},
        {"get_inventory", l_get_inventory},
+       {"get_current_modname", l_get_current_modname},
        {"get_modpath", l_get_modpath},
        {NULL, NULL}
 };
 
-/*
-       LuaEntity functions
-*/
-
-static const struct luaL_Reg minetest_entity_m [] = {
-       {NULL, NULL}
-};
-
 /*
        Main export function
 */
@@ -3355,10 +3422,6 @@ void scriptapi_export(lua_State *L, Server *server)
        lua_pushlightuserdata(L, server);
        lua_setfield(L, LUA_REGISTRYINDEX, "minetest_server");
 
-       // Store nil as minetest_nodedef_defaults in registry
-       lua_pushnil(L);
-       lua_setfield(L, LUA_REGISTRYINDEX, "minetest_nodedef_default");
-       
        // Register global functions in table minetest
        lua_newtable(L);
        luaL_register(L, NULL, minetest_f);
@@ -3369,31 +3432,13 @@ void scriptapi_export(lua_State *L, Server *server)
 
        // Add tables to minetest
        
-       lua_newtable(L);
-       lua_setfield(L, -2, "registered_nodes");
-       lua_newtable(L);
-       lua_setfield(L, -2, "registered_entities");
-       lua_newtable(L);
-       lua_setfield(L, -2, "registered_craftitems");
-       lua_newtable(L);
-       lua_setfield(L, -2, "registered_abms");
-       
        lua_newtable(L);
        lua_setfield(L, -2, "object_refs");
        lua_newtable(L);
        lua_setfield(L, -2, "luaentities");
 
-       // Create entity prototype
-       luaL_newmetatable(L, "minetest.entity");
-       // metatable.__index = metatable
-       lua_pushvalue(L, -1); // Duplicate metatable
-       lua_setfield(L, -2, "__index");
-       // Put functions in metatable
-       luaL_register(L, NULL, minetest_entity_m);
-       // Put other stuff in metatable
-       
        // Register wrappers
-       ItemStack::Register(L);
+       LuaItemStack::Register(L);
        InvRef::Register(L);
        NodeMetaRef::Register(L);
        ObjectRef::Register(L);
@@ -3705,62 +3750,22 @@ void scriptapi_get_creative_inventory(lua_State *L, ServerRemotePlayer *player)
        lua_getfield(L, -1, "creative_inventory");
        luaL_checktype(L, -1, LUA_TTABLE);
        inventory_set_list_from_lua(&player->inventory, "main", L, -1,
-                       player->getEnv()->getGameDef(), PLAYER_INVENTORY_SIZE);
+                       PLAYER_INVENTORY_SIZE);
 }
 
 /*
-       craftitem
+       item callbacks
 */
 
-static void pushPointedThing(lua_State *L, const PointedThing& pointed)
-{
-       lua_newtable(L);
-       if(pointed.type == POINTEDTHING_NODE)
-       {
-               lua_pushstring(L, "node");
-               lua_setfield(L, -2, "type");
-               push_v3s16(L, pointed.node_undersurface);
-               lua_setfield(L, -2, "under");
-               push_v3s16(L, pointed.node_abovesurface);
-               lua_setfield(L, -2, "above");
-       }
-       else if(pointed.type == POINTEDTHING_OBJECT)
-       {
-               lua_pushstring(L, "object");
-               lua_setfield(L, -2, "type");
-               objectref_get(L, pointed.object_id);
-               lua_setfield(L, -2, "ref");
-       }
-       else
-       {
-               lua_pushstring(L, "nothing");
-               lua_setfield(L, -2, "type");
-       }
-}
-
-void scriptapi_add_craftitem(lua_State *L, const char *name)
-{
-       StackUnroller stack_unroller(L);
-       assert(lua_gettop(L) > 0);
-
-       // Set minetest.registered_craftitems[name] = table on top of stack
-       lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_craftitems");
-       luaL_checktype(L, -1, LUA_TTABLE);
-       lua_pushvalue(L, -3); // push another reference to the table to be registered
-       lua_setfield(L, -2, name); // set minetest.registered_craftitems[name]
-}
-
-static bool get_craftitem_callback(lua_State *L, const char *name,
-               const char *callbackname)
+// Retrieves minetest.registered_items[name][callbackname]
+// If that is nil or on error, return false and stack is unchanged
+// If that is a function, returns true and pushes the
+// function onto the stack
+static bool get_item_callback(lua_State *L,
+               const char *name, const char *callbackname)
 {
-       // Get minetest.registered_craftitems[name][callbackname]
-       // If that is nil or on error, return false and stack is unchanged
-       // If that is a function, returns true and pushes the
-       // function onto the stack
-
        lua_getglobal(L, "minetest");
-       lua_getfield(L, -1, "registered_craftitems");
+       lua_getfield(L, -1, "registered_items");
        lua_remove(L, -2);
        luaL_checktype(L, -1, LUA_TTABLE);
        lua_getfield(L, -1, name);
@@ -3768,7 +3773,7 @@ static bool get_craftitem_callback(lua_State *L, const char *name,
        // Should be a table
        if(lua_type(L, -1) != LUA_TTABLE)
        {
-               errorstream<<"CraftItem name \""<<name<<"\" not defined"<<std::endl;
+               errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
                lua_pop(L, 1);
                return false;
        }
@@ -3786,83 +3791,77 @@ static bool get_craftitem_callback(lua_State *L, const char *name,
        }
        else
        {
-               errorstream<<"CraftItem name \""<<name<<"\" callback \""
+               errorstream<<"Item \""<<name<<"\" callback \""
                        <<callbackname<<" is not a function"<<std::endl;
                lua_pop(L, 1);
                return false;
        }
 }
 
-bool scriptapi_craftitem_on_drop(lua_State *L, const char *name,
-               ServerActiveObject *dropper, v3f pos,
-               bool &callback_exists)
+bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
+               ServerActiveObject *dropper, v3f pos)
 {
        realitycheck(L);
        assert(lua_checkstack(L, 20));
-       //infostream<<"scriptapi_craftitem_on_drop"<<std::endl;
        StackUnroller stack_unroller(L);
 
-       bool result = false;
-       callback_exists = get_craftitem_callback(L, name, "on_drop");
-       if(callback_exists)
-       {
-               // Call function
-               lua_pushstring(L, name);
-               objectref_get_or_create(L, dropper);
-               pushFloatPos(L, pos);
-               if(lua_pcall(L, 3, 1, 0))
-                       script_error(L, "error: %s", lua_tostring(L, -1));
-               result = lua_toboolean(L, -1);
-       }
-       return result;
+       // Push callback function on stack
+       if(!get_item_callback(L, item.name.c_str(), "on_drop"))
+               return false;
+
+       // Call function
+       LuaItemStack::create(L, item);
+       objectref_get_or_create(L, dropper);
+       pushFloatPos(L, pos);
+       if(lua_pcall(L, 3, 1, 0))
+               script_error(L, "error: %s", lua_tostring(L, -1));
+       if(!lua_isnil(L, -1))
+               item = read_item(L, -1);
+       return true;
 }
 
-bool scriptapi_craftitem_on_place_on_ground(lua_State *L, const char *name,
-               ServerActiveObject *placer, v3f pos,
-               bool &callback_exists)
+bool scriptapi_item_on_place(lua_State *L, ItemStack &item,
+               ServerActiveObject *placer, const PointedThing &pointed)
 {
        realitycheck(L);
        assert(lua_checkstack(L, 20));
-       //infostream<<"scriptapi_craftitem_on_place_on_ground"<<std::endl;
        StackUnroller stack_unroller(L);
 
-       bool result = false;
-       callback_exists = get_craftitem_callback(L, name, "on_place_on_ground");
-       if(callback_exists)
-       {
-               // Call function
-               lua_pushstring(L, name);
-               objectref_get_or_create(L, placer);
-               pushFloatPos(L, pos);
-               if(lua_pcall(L, 3, 1, 0))
-                       script_error(L, "error: %s", lua_tostring(L, -1));
-               result = lua_toboolean(L, -1);
-       }
-       return result;
+       // Push callback function on stack
+       if(!get_item_callback(L, item.name.c_str(), "on_place"))
+               return false;
+
+       // Call function
+       LuaItemStack::create(L, item);
+       objectref_get_or_create(L, placer);
+       push_pointed_thing(L, pointed);
+       if(lua_pcall(L, 3, 1, 0))
+               script_error(L, "error: %s", lua_tostring(L, -1));
+       if(!lua_isnil(L, -1))
+               item = read_item(L, -1);
+       return true;
 }
 
-bool scriptapi_craftitem_on_use(lua_State *L, const char *name,
-               ServerActiveObject *user, const PointedThing& pointed,
-               bool &callback_exists)
+bool scriptapi_item_on_use(lua_State *L, ItemStack &item,
+               ServerActiveObject *user, const PointedThing &pointed)
 {
        realitycheck(L);
        assert(lua_checkstack(L, 20));
-       //infostream<<"scriptapi_craftitem_on_use"<<std::endl;
        StackUnroller stack_unroller(L);
 
-       bool result = false;
-       callback_exists = get_craftitem_callback(L, name, "on_use");
-       if(callback_exists)
-       {
-               // Call function
-               lua_pushstring(L, name);
-               objectref_get_or_create(L, user);
-               pushPointedThing(L, pointed);
-               if(lua_pcall(L, 3, 1, 0))
-                       script_error(L, "error: %s", lua_tostring(L, -1));
-               result = lua_toboolean(L, -1);
-       }
-       return result;
+       // Push callback function on stack
+       if(!get_item_callback(L, item.name.c_str(), "on_use"))
+               return false;
+
+       // Call function
+       LuaItemStack::create(L, item);
+       objectref_get_or_create(L, user);
+       push_pointed_thing(L, pointed);
+       if(lua_pcall(L, 3, 1, 0))
+               script_error(L, "error: %s", lua_tostring(L, -1));
+       if(!lua_isnil(L, -1))
+               item = read_item(L, -1);
+       return true;
 }
 
 /*
index af8afa3d9d30b2db480d244611d91098045fb694..198f6052559fac7ef507a824fa1db8f20528ae6f 100644 (file)
@@ -27,11 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 class Server;
 class ServerEnvironment;
 class ServerActiveObject;
+class ServerRemotePlayer;
 typedef struct lua_State lua_State;
 struct LuaEntityProperties;
+class ItemStack;
 struct PointedThing;
 //class IGameDef;
-class ServerRemotePlayer;
 
 void scriptapi_export(lua_State *L, Server *server);
 bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath,
@@ -66,17 +67,13 @@ void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player);
 bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player);
 void scriptapi_get_creative_inventory(lua_State *L, ServerRemotePlayer *player);
 
-/* craftitem */
-void scriptapi_add_craftitem(lua_State *L, const char *name);
-bool scriptapi_craftitem_on_drop(lua_State *L, const char *name,
-               ServerActiveObject *dropper, v3f pos,
-               bool &callback_exists);
-bool scriptapi_craftitem_on_place_on_ground(lua_State *L, const char *name,
-               ServerActiveObject *placer, v3f pos,
-               bool &callback_exists);
-bool scriptapi_craftitem_on_use(lua_State *L, const char *name,
-               ServerActiveObject *user, const PointedThing& pointed,
-               bool &callback_exists);
+/* item callbacks */
+bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,
+               ServerActiveObject *dropper, v3f pos);
+bool scriptapi_item_on_place(lua_State *L, ItemStack &item,
+               ServerActiveObject *placer, const PointedThing &pointed);
+bool scriptapi_item_on_use(lua_State *L, ItemStack &item,
+               ServerActiveObject *user, const PointedThing &pointed);
 
 /* luaentity */
 // Returns true if succesfully added into Lua; false otherwise.
index d704bf861bdaad04c1c903a9413911b9ddac5c5a..3c0cab2a914308b7bdafe971d1ee7aaba7caf321 100644 (file)
@@ -42,9 +42,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "script.h"
 #include "scriptapi.h"
 #include "nodedef.h"
-#include "tooldef.h"
+#include "itemdef.h"
 #include "craftdef.h"
-#include "craftitemdef.h"
 #include "mapgen.h"
 #include "content_abm.h"
 #include "mods.h"
@@ -853,10 +852,9 @@ Server::Server(
        m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
        m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
        m_lua(NULL),
-       m_toolmgr(createToolDefManager()),
+       m_itemdef(createItemDefManager()),
        m_nodedef(createNodeDefManager()),
        m_craftdef(createCraftDefManager()),
-       m_craftitemdef(createCraftItemDefManager()),
        m_thread(this),
        m_emergethread(this),
        m_time_counter(0),
@@ -934,6 +932,9 @@ Server::Server(
        // Read Textures and calculate sha1 sums
        PrepareTextures();
 
+       // Apply item aliases in the node definition manager
+       m_nodedef->updateAliases(m_itemdef);
+
        // Initialize Environment
        
        m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
@@ -1042,10 +1043,9 @@ Server::~Server()
        // Delete Environment
        delete m_env;
 
-       delete m_toolmgr;
+       delete m_itemdef;
        delete m_nodedef;
        delete m_craftdef;
-       delete m_craftitemdef;
        
        // Deinitialize scripting
        infostream<<"Server: Deinitializing scripting"<<std::endl;
@@ -2106,15 +2106,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        Send some initialization data
                */
 
-               // Send tool definitions
-               SendToolDef(m_con, peer_id, m_toolmgr);
+               // Send item definitions
+               SendItemDef(m_con, peer_id, m_itemdef);
                
                // Send node definitions
                SendNodeDef(m_con, peer_id, m_nodedef);
                
-               // Send CraftItem definitions
-               SendCraftItemDef(m_con, peer_id, m_craftitemdef);
-               
                // Send texture announcement
                SendTextureAnnouncement(peer_id);
                
@@ -2362,13 +2359,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
        }
        else if(command == TOSERVER_INVENTORY_ACTION)
        {
-               /*// Ignore inventory changes if in creative mode
-               if(g_settings->getBool("creative_mode") == true)
-               {
-                       infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
-                                       <<std::endl;
-                       return;
-               }*/
                // Strip command and create a stream
                std::string datastring((char*)&data[2], datasize-2);
                infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
@@ -2382,15 +2372,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        <<std::endl;
                        return;
                }
-               // Create context
-               InventoryContext c;
-               c.current_player = player;
 
                /*
                        Handle restrictions and special cases of the move action
                */
-               if(a->getType() == IACTION_MOVE
-                               && g_settings->getBool("creative_mode") == false)
+               if(a->getType() == IACTION_MOVE)
                {
                        InventoryList *rlist = player->inventory.getList("craftresult");
                        assert(rlist);
@@ -2401,17 +2387,28 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                        IMoveAction *ma = (IMoveAction*)a;
 
+                       ma->from_inv.applyCurrentPlayer(player->getName());
+                       ma->to_inv.applyCurrentPlayer(player->getName());
+
+                       bool from_inv_is_current_player =
+                               (ma->from_inv.type == InventoryLocation::PLAYER) &&
+                               (ma->from_inv.name == player->getName());
+
+                       bool to_inv_is_current_player =
+                               (ma->to_inv.type == InventoryLocation::PLAYER) &&
+                               (ma->to_inv.name == player->getName());
+
                        /*
                                Disable moving items into craftresult from elsewhere
                        */
-                       if(ma->to_inv == "current_player"
+                       if(to_inv_is_current_player
                                        && ma->to_list == "craftresult"
-                                       && (ma->from_inv != "current_player"
+                                       && (!from_inv_is_current_player
                                        || ma->from_list != "craftresult"))
                        {
                                infostream<<"Ignoring IMoveAction from "
-                                               <<ma->from_inv<<":"<<ma->from_list
-                                               <<" to "<<ma->to_inv<<":"<<ma->to_list
+                                               <<(ma->from_inv.dump())<<":"<<ma->from_list
+                                               <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
                                                <<" because dst is craftresult"
                                                <<" and src isn't craftresult"<<std::endl;
                                delete a;
@@ -2421,74 +2418,66 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        /*
                                Handle crafting (source is craftresult, which is preview)
                        */
-                       if(ma->from_inv == "current_player"
+                       if(from_inv_is_current_player
                                        && ma->from_list == "craftresult"
-                                       && player->craftresult_is_preview)
+                                       && player->craftresult_is_preview
+                                       && g_settings->getBool("creative_mode") == false)
                        {
+                               ItemStack crafting_result;
+                               bool crafting_possible = GetCraftingResult(peer_id,
+                                               crafting_result, false);
+
                                /*
-                                       If the craftresult is placed on itself, crafting takes
-                                       place and result is moved into main list
+                                       If the craftresult is placed on itself,
+                                       crafting takes place and result is moved
+                                       into main list.
                                */
-                               if(ma->to_inv == "current_player"
+                               if(crafting_possible
+                                               && to_inv_is_current_player
                                                && ma->to_list == "craftresult")
                                {
-                                       // Except if main list doesn't have free slots
-                                       if(mlist->getFreeSlots() == 0){
-                                               infostream<<"Cannot craft: Main list doesn't have"
-                                                               <<" free slots"<<std::endl;
-                                               delete a;
-                                               return;
-                                       }
-                                       
-                                       player->craftresult_is_preview = false;
-                                       clist->decrementMaterials(1);
-
-                                       InventoryItem *item1 = rlist->changeItem(0, NULL);
-                                       mlist->addItem(item1);
+                                       if(mlist->roomForItem(crafting_result))
+                                       {
+                                               actionstream<<player->getName()
+                                                       <<" crafts "
+                                                       <<crafting_result.getItemString()
+                                                       <<std::endl;
 
-                                       srp->m_inventory_not_sent = true;
+                                               // Decrement crafting materials
+                                               GetCraftingResult(peer_id, crafting_result, true);
+                                               mlist->addItem(crafting_result);
+                                               rlist->clearItems();
+                                               player->craftresult_is_preview = true;
+                                               srp->m_inventory_not_sent = true;
+                                       }
 
-                                       delete a;
-                                       return;
                                }
                                /*
-                                       Disable action if there are no free slots in
-                                       destination
-                                       
-                                       If the item is placed on an item that is not of the
-                                       same kind, the existing item will be first moved to
-                                       craftresult and immediately moved to the free slot.
+                                       Otherwise, if the destination is part of
+                                       the same player's inventory, crafting
+                                       takes place normally.
                                */
-                               do{
-                                       Inventory *inv_to = InventoryManager::getInventory(&c, ma->to_inv);
-                                       if(!inv_to) break;
-                                       InventoryList *list_to = inv_to->getList(ma->to_list);
-                                       if(!list_to) break;
-                                       if(list_to->getFreeSlots() == 0){
-                                               infostream<<"Cannot craft: Destination doesn't have"
-                                                               <<" free slots"<<std::endl;
-                                               delete a;
-                                               return;
+                               else if(crafting_possible
+                                               && to_inv_is_current_player)
+                               {
+                                       InventoryList *list = player->inventory.getList(ma->to_list);
+                                       if(list && list->itemFits(ma->to_i, crafting_result))
+                                       {
+                                               actionstream<<player->getName()
+                                                       <<" crafts "
+                                                       <<crafting_result.getItemString()
+                                                       <<std::endl;
+
+                                               // Decrement crafting materials
+                                               GetCraftingResult(peer_id, crafting_result, true);
+                                               list->addItem(ma->to_i, crafting_result);
+                                               rlist->clearItems();
+                                               player->craftresult_is_preview = true;
+                                               srp->m_inventory_not_sent = true;
                                        }
-                               }while(0); // Allow break
+                               }
 
-                               /*
-                                       Ok, craft normally.
-                               */
-                               player->craftresult_is_preview = false;
-                               clist->decrementMaterials(1);
-                               
-                               /* Print out action */
-                               InventoryItem *item = rlist->getItem(0);
-                               std::string itemstring = "NULL";
-                               if(item)
-                                       itemstring = item->getItemString();
-                               actionstream<<player->getName()<<" crafts "
-                                               <<itemstring<<std::endl;
-
-                               // Do the action
-                               a->apply(&c, this, m_env);
-                               
+                               // Do not apply the action normally.
                                delete a;
                                return;
                        }
@@ -2498,63 +2487,38 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        */
                        
                        // Disallow moving items in elsewhere than player's inventory
-                       // if not allowed to build
+                       // if not allowed to interact
                        if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
-                                       && (ma->from_inv != "current_player"
-                                       || ma->to_inv != "current_player"))
+                                       && (from_inv_is_current_player
+                                       || to_inv_is_current_player))
                        {
                                infostream<<"Cannot move outside of player's inventory: "
-                                               <<"No build privilege"<<std::endl;
+                                               <<"No interact privilege"<<std::endl;
                                delete a;
                                return;
                        }
 
-                       // If player is not an admin, check for ownership of src
-                       if(ma->from_inv != "current_player"
-                                       && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
+                       // If player is not an admin, check for ownership of src and dst
+                       if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
                        {
-                               Strfnd fn(ma->from_inv);
-                               std::string id0 = fn.next(":");
-                               if(id0 == "nodemeta")
+                               std::string owner_from = getInventoryOwner(ma->from_inv);
+                               if(owner_from != "" && owner_from != player->getName())
                                {
-                                       v3s16 p;
-                                       p.X = stoi(fn.next(","));
-                                       p.Y = stoi(fn.next(","));
-                                       p.Z = stoi(fn.next(","));
-                                       NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
-                                       if(meta->getOwner() != "" &&
-                                                       meta->getOwner() != player->getName())
-                                       {
-                                               infostream<<"Cannot move item: "
-                                                               "not owner of metadata"
-                                                               <<std::endl;
-                                               delete a;
-                                               return;
-                                       }
+                                       infostream<<"WARNING: "<<player->getName()
+                                               <<" tried to access an inventory that"
+                                               <<" belongs to "<<owner_from<<std::endl;
+                                       delete a;
+                                       return;
                                }
-                       }
-                       // If player is not an admin, check for ownership of dst
-                       if(ma->to_inv != "current_player"
-                                       && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
-                       {
-                               Strfnd fn(ma->to_inv);
-                               std::string id0 = fn.next(":");
-                               if(id0 == "nodemeta")
+
+                               std::string owner_to = getInventoryOwner(ma->to_inv);
+                               if(owner_to != "" && owner_to != player->getName())
                                {
-                                       v3s16 p;
-                                       p.X = stoi(fn.next(","));
-                                       p.Y = stoi(fn.next(","));
-                                       p.Z = stoi(fn.next(","));
-                                       NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
-                                       if(meta->getOwner() != "" &&
-                                                       meta->getOwner() != player->getName())
-                                       {
-                                               infostream<<"Cannot move item: "
-                                                               "not owner of metadata"
-                                                               <<std::endl;
-                                               delete a;
-                                               return;
-                                       }
+                                       infostream<<"WARNING: "<<player->getName()
+                                               <<" tried to access an inventory that"
+                                               <<" belongs to "<<owner_to<<std::endl;
+                                       delete a;
+                                       return;
                                }
                        }
                }
@@ -2564,40 +2528,32 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                else if(a->getType() == IACTION_DROP)
                {
                        IDropAction *da = (IDropAction*)a;
-                       // Disallow dropping items if not allowed to build
+
+                       da->from_inv.applyCurrentPlayer(player->getName());
+
+                       // Disallow dropping items if not allowed to interact
                        if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
                        {
                                delete a;
                                return;
                        }
                        // If player is not an admin, check for ownership
-                       else if (da->from_inv != "current_player"
-                                       && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
+                       else if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
                        {
-                               Strfnd fn(da->from_inv);
-                               std::string id0 = fn.next(":");
-                               if(id0 == "nodemeta")
+                               std::string owner_from = getInventoryOwner(da->from_inv);
+                               if(owner_from != "" && owner_from != player->getName())
                                {
-                                       v3s16 p;
-                                       p.X = stoi(fn.next(","));
-                                       p.Y = stoi(fn.next(","));
-                                       p.Z = stoi(fn.next(","));
-                                       NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
-                                       if(meta->getOwner() != "" &&
-                                                       meta->getOwner() != player->getName())
-                                       {
-                                               infostream<<"Cannot move item: "
-                                                               "not owner of metadata"
-                                                               <<std::endl;
-                                               delete a;
-                                               return;
-                                       }
+                                       infostream<<"WARNING: "<<player->getName()
+                                               <<" tried to access an inventory that"
+                                               <<" belongs to "<<owner_from<<std::endl;
+                                       delete a;
+                                       return;
                                }
                        }
                }
                
                // Do the action
-               a->apply(&c, this, m_env);
+               a->apply(this, srp);
                // Eat the action
                delete a;
        }
@@ -2809,8 +2765,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        return;
 
                u16 item = readU16(&data[2]);
-               player->wieldItem(item);
-               SendWieldedItem(player);
+               srp->setWieldIndex(item);
+               SendWieldedItem(srp);
        }
        else if(command == TOSERVER_RESPAWN)
        {
@@ -2880,7 +2836,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                v3f player_pos = srp->m_last_good_position;
 
                // Update wielded item
-               srp->wieldItem(item_i);
+               if(srp->getWieldIndex() != item_i)
+               {
+                       srp->setWieldIndex(item_i);
+                       SendWieldedItem(srp);
+               }
 
                // Get pointed to node (undefined if not POINTEDTYPE_NODE)
                v3s16 p_under = pointed.node_undersurface;
@@ -2900,23 +2860,26 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                }
 
+               v3f pointed_pos_under = player_pos;
+               v3f pointed_pos_above = player_pos;
+               if(pointed.type == POINTEDTHING_NODE)
+               {
+                       pointed_pos_under = intToFloat(p_under, BS);
+                       pointed_pos_above = intToFloat(p_above, BS);
+               }
+               else if(pointed.type == POINTEDTHING_OBJECT)
+               {
+                       pointed_pos_under = pointed_object->getBasePosition();
+                       pointed_pos_above = pointed_pos_under;
+               }
+
                /*
                        Check that target is reasonably close
                        (only when digging or placing things)
                */
                if(action == 0 || action == 2 || action == 3)
                {
-                       v3f pointed_pos = player_pos;
-                       if(pointed.type == POINTEDTHING_NODE)
-                       {
-                               pointed_pos = intToFloat(p_under, BS);
-                       }
-                       else if(pointed.type == POINTEDTHING_OBJECT)
-                       {
-                               pointed_pos = pointed_object->getBasePosition();
-                       }
-
-                       float d = player_pos.getDistanceFrom(pointed_pos);
+                       float d = player_pos.getDistanceFrom(pointed_pos_under);
                        float max_d = BS * 10; // Just some large enough value
                        if(d > max_d){
                                actionstream<<"Player "<<player->getName()
@@ -2926,7 +2889,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                                <<". ignoring."<<std::endl;
                                // Re-send block to revert change on client-side
                                RemoteClient *client = getClient(peer_id);
-                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
+                               v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
                                client->SetBlockNotSent(blockpos);
                                // Do nothing else
                                return;
@@ -2936,8 +2899,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                /*
                        Make sure the player is allowed to do it
                */
-               bool build_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0;
-               if(!build_priv)
+               bool interact_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0;
+               if(!interact_priv)
                {
                        infostream<<"Ignoring interaction from player "<<player->getName()
                                        <<" because privileges are "<<getPlayerPrivs(player)
@@ -2956,7 +2919,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        NOTE: This can be used in the future to check if
                                        somebody is cheating, by checking the timing.
                                */
-                               bool cannot_punch_node = !build_priv;
+                               bool cannot_punch_node = !interact_priv;
 
                                MapNode n(CONTENT_IGNORE);
 
@@ -2984,7 +2947,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        }
                        else if(pointed.type == POINTEDTHING_OBJECT)
                        {
-                               if(!build_priv)
+                               if(!interact_priv)
                                        return;
 
                                // Skip if object has been removed
@@ -3023,7 +2986,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        content_t material = CONTENT_IGNORE;
                        u8 mineral = MINERAL_NONE;
 
-                       bool cannot_remove_node = !build_priv;
+                       bool cannot_remove_node = !interact_priv;
                        
                        MapNode n(CONTENT_IGNORE);
                        try
@@ -3114,64 +3077,44 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                InventoryList *mlist = player->inventory.getList("main");
                                if(mlist != NULL)
                                {
-                                       InventoryItem *item = mlist->getItem(item_i);
-                                       if(item && (std::string)item->getName() == "ToolItem")
-                                       {
-                                               ToolItem *titem = (ToolItem*)item;
-                                               std::string toolname = titem->getToolName();
-
-                                               // Get digging properties for material and tool
-                                               ToolDiggingProperties tp =
-                                                               m_toolmgr->getDiggingProperties(toolname);
-                                               DiggingProperties prop =
-                                                               getDiggingProperties(material, &tp, m_nodedef);
-
-                                               if(prop.diggable == false)
-                                               {
-                                                       infostream<<"Server: WARNING: Player digged"
-                                                                       <<" with impossible material + tool"
-                                                                       <<" combination"<<std::endl;
-                                               }
-                                               
-                                               bool weared_out = titem->addWear(prop.wear);
-
-                                               if(weared_out)
-                                               {
-                                                       mlist->deleteItem(item_i);
-                                               }
-
-                                               srp->m_inventory_not_sent = true;
-                                       }
+                                       ItemStack &item = mlist->getItem(item_i);
+
+                                       // Get digging properties for material and tool
+                                       ToolDiggingProperties tp =
+                                                       item.getToolDiggingProperties(m_itemdef);
+                                       DiggingProperties prop =
+                                                       getDiggingProperties(material, &tp, m_nodedef);
+                                       item.addWear(prop.wear, m_itemdef);
+                                       srp->m_inventory_not_sent = true;
                                }
 
                                /*
                                        Add dug item to inventory
                                */
 
-                               InventoryItem *item = NULL;
+                               ItemStack item;
 
                                if(mineral != MINERAL_NONE)
                                        item = getDiggedMineralItem(mineral, this);
                                
                                // If not mineral
-                               if(item == NULL)
+                               if(item.empty())
                                {
                                        const std::string &dug_s = m_nodedef->get(material).dug_item;
                                        if(dug_s != "")
                                        {
-                                               std::istringstream is(dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is, this);
+                                               item.deSerialize(dug_s, m_itemdef);
                                        }
                                }
                                
-                               if(item != NULL)
+                               if(!item.empty())
                                {
                                        // Add a item to inventory
                                        player->inventory.addItem("main", item);
                                        srp->m_inventory_not_sent = true;
                                }
 
-                               item = NULL;
+                               item.clear();
                                
                                {
                                        const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
@@ -3179,12 +3122,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                        if(extra_dug_s != "" && extra_rarity != 0
                                           && myrand() % extra_rarity == 0)
                                        {
-                                               std::istringstream is(extra_dug_s, std::ios::binary);
-                                               item = InventoryItem::deSerialize(is, this);
+                                               item.deSerialize(extra_dug_s, m_itemdef);
                                        }
                                }
                        
-                               if(item != NULL)
+                               if(!item.empty())
                                {
                                        // Add a item to inventory
                                        player->inventory.addItem("main", item);
@@ -3226,242 +3168,155 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                */
                else if(action == 3)
                {
-                       if(pointed.type == POINTEDTHING_NODE)
+                       if(!interact_priv)
                        {
-                               InventoryList *ilist = player->inventory.getList("main");
-                               if(ilist == NULL)
-                                       return;
-
-                               // Get item
-                               InventoryItem *item = ilist->getItem(item_i);
-                               
-                               // If there is no item, it is not possible to add it anywhere
-                               if(item == NULL)
-                                       return;
+                               infostream<<"Not allowing player "
+                                       <<player->getName()<<" to place item: "
+                                       <<"no interact privileges"<<std::endl;
+                               return;
+                       }
 
-                               /*
-                                       Handle material items
-                               */
-                               if(std::string("MaterialItem") == item->getName())
-                               {
-                                       bool cannot_place_node = !build_priv;
-
-                                       try{
-                                               // Don't add a node if this is not a free space
-                                               MapNode n2 = m_env->getMap().getNode(p_above);
-                                               if(m_nodedef->get(n2).buildable_to == false)
-                                               {
-                                                       infostream<<"Client "<<peer_id<<" tried to place"
-                                                                       <<" node in invalid position."<<std::endl;
-                                                       cannot_place_node = true;
-                                               }
-                                       }
-                                       catch(InvalidPositionException &e)
-                                       {
-                                               infostream<<"Server: Ignoring ADDNODE: Node not found"
-                                                               <<" Adding block to emerge queue."
-                                                               <<std::endl;
-                                               m_emerge_queue.addBlock(peer_id,
-                                                               getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
-                                               cannot_place_node = true;
-                                       }
+                       ItemStack item = srp->getWieldedItem();
 
-                                       if(cannot_place_node)
-                                       {
-                                               // Client probably has wrong data.
-                                               // Set block not sent, so that client will get
-                                               // a valid one.
-                                               RemoteClient *client = getClient(peer_id);
-                                               v3s16 blockpos = getNodeBlockPos(p_above);
-                                               client->SetBlockNotSent(blockpos);
-                                               return;
-                                       }
+                       if(pointed.type == POINTEDTHING_OBJECT)
+                       {
+                               // Right click object
 
-                                       // Reset build time counter
-                                       getClient(peer_id)->m_time_from_building = 0.0;
-                                       
-                                       // Create node data
-                                       MaterialItem *mitem = (MaterialItem*)item;
-                                       MapNode n;
-                                       n.setContent(mitem->getMaterial());
-
-                                       actionstream<<player->getName()<<" places material "
-                                                       <<(int)mitem->getMaterial()
-                                                       <<" at "<<PP(p_under)<<std::endl;
-                               
-                                       // Calculate direction for wall mounted stuff
-                                       if(m_nodedef->get(n).wall_mounted)
-                                               n.param2 = packDir(p_under - p_above);
+                               // Skip if object has been removed
+                               if(pointed_object->m_removed)
+                                       return;
 
-                                       // Calculate the direction for furnaces and chests and stuff
-                                       if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
-                                       {
-                                               v3f playerpos = player->getPosition();
-                                               v3f blockpos = intToFloat(p_above, BS) - playerpos;
-                                               blockpos = blockpos.normalize();
-                                               n.param1 = 0;
-                                               if (fabs(blockpos.X) > fabs(blockpos.Z)) {
-                                                       if (blockpos.X < 0)
-                                                               n.param1 = 3;
-                                                       else
-                                                               n.param1 = 1;
-                                               } else {
-                                                       if (blockpos.Z < 0)
-                                                               n.param1 = 2;
-                                                       else
-                                                               n.param1 = 0;
-                                               }
-                                       }
+                               actionstream<<player->getName()<<" right-clicks object "
+                                       <<pointed.object_id<<std::endl;
 
-                                       /*
-                                               Send to all close-by players
-                                       */
-                                       core::list<u16> far_players;
-                                       sendAddNode(p_above, n, 0, &far_players, 30);
-                                       
-                                       /*
-                                               Handle inventory
-                                       */
-                                       InventoryList *ilist = player->inventory.getList("main");
-                                       if(g_settings->getBool("creative_mode") == false && ilist)
-                                       {
-                                               // Remove from inventory and send inventory
-                                               if(mitem->getCount() <= 1)
-                                                       ilist->deleteItem(item_i);
-                                               else
-                                                       mitem->remove(1);
-                                               srp->m_inventory_not_sent = true;
-                                       }
-                                       
-                                       /*
-                                               Add node.
+                               // Do stuff
+                               pointed_object->rightClick(srp);
+                       }
+                       else if(scriptapi_item_on_place(m_lua,
+                                       item, srp, pointed))
+                       {
+                               // Placement was handled in lua
 
-                                               This takes some time so it is done after the quick stuff
-                                       */
-                                       core::map<v3s16, MapBlock*> modified_blocks;
-                                       {
-                                               MapEditEventIgnorer ign(&m_ignore_map_edit_events);
+                               // Apply returned ItemStack
+                               if(g_settings->getBool("creative_mode") == false)
+                                       srp->setWieldedItem(item);
+                       }
+                       else if(pointed.type == POINTEDTHING_NODE &&
+                                       item.getDefinition(m_itemdef).type == ITEM_NODE)
+                       {
+                               bool cannot_place_node = !interact_priv;
 
-                                               std::string p_name = std::string(player->getName());
-                                               m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
-                                       }
-                                       /*
-                                               Set blocks not sent to far players
-                                       */
-                                       for(core::list<u16>::Iterator
-                                                       i = far_players.begin();
-                                                       i != far_players.end(); i++)
+                               try{
+                                       // Don't add a node if this is not a free space
+                                       MapNode n2 = m_env->getMap().getNode(p_above);
+                                       if(m_nodedef->get(n2).buildable_to == false)
                                        {
-                                               u16 peer_id = *i;
-                                               RemoteClient *client = getClient(peer_id);
-                                               if(client==NULL)
-                                                       continue;
-                                               client->SetBlocksNotSent(modified_blocks);
+                                               infostream<<"Client "<<peer_id<<" tried to place"
+                                                               <<" node in invalid position."<<std::endl;
+                                               cannot_place_node = true;
                                        }
-
-                                       /*
-                                               Run script hook
-                                       */
-                                       scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
-
-                                       /*
-                                               Calculate special events
-                                       */
-                                       
-                                       /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
-                                       {
-                                               u32 count = 0;
-                                               for(s16 z=-1; z<=1; z++)
-                                               for(s16 y=-1; y<=1; y++)
-                                               for(s16 x=-1; x<=1; x++)
-                                               {
-                                                       
-                                               }
-                                       }*/
                                }
-                               /*
-                                       Place other item (not a block)
-                               */
-                               else
+                               catch(InvalidPositionException &e)
                                {
-                                       if(!build_priv)
-                                       {
-                                               infostream<<"Not allowing player to place item: "
-                                                               "no build privileges"<<std::endl;
-                                               return;
-                                       }
+                                       infostream<<"Server: Ignoring ADDNODE: Node not found"
+                                                       <<" Adding block to emerge queue."
+                                                       <<std::endl;
+                                       m_emerge_queue.addBlock(peer_id,
+                                                       getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
+                                       cannot_place_node = true;
+                               }
 
-                                       // Calculate a position for it
-                                       v3f pos = player_pos;
-                                       if(pointed.type == POINTEDTHING_NOTHING)
-                                       {
-                                               infostream<<"Not allowing player to place item: "
-                                                               "pointing to nothing"<<std::endl;
-                                               return;
-                                       }
-                                       else if(pointed.type == POINTEDTHING_NODE)
-                                       {
-                                               pos = intToFloat(p_above, BS);
-                                       }
-                                       else if(pointed.type == POINTEDTHING_OBJECT)
-                                       {
-                                               pos = pointed_object->getBasePosition();
+                               if(cannot_place_node)
+                               {
+                                       // Client probably has wrong data.
+                                       // Set block not sent, so that client will get
+                                       // a valid one.
+                                       RemoteClient *client = getClient(peer_id);
+                                       v3s16 blockpos = getNodeBlockPos(p_above);
+                                       client->SetBlockNotSent(blockpos);
+                                       return;
+                               }
 
-                                               // Randomize a bit
-                                               pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-                                               pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
-                                       }
+                               // Reset build time counter
+                               getClient(peer_id)->m_time_from_building = 0.0;
 
-                                       //pos.Y -= BS*0.45;
-                                       //pos.Y -= BS*0.25; // let it drop a bit
+                               // Create node data
+                               MapNode n(m_nodedef, item.name, 0, 0);
 
-                                       /*
-                                               Check that the block is loaded so that the item
-                                               can properly be added to the static list too
-                                       */
-                                       v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
-                                       MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
-                                       if(block==NULL)
-                                       {
-                                               infostream<<"Error while placing item: "
-                                                               "block not found"<<std::endl;
-                                               return;
-                                       }
+                               actionstream<<player->getName()<<" places material "
+                                               <<item.name
+                                               <<" at "<<PP(p_under)<<std::endl;
 
-                                       actionstream<<player->getName()<<" places "<<item->getName()
-                                                       <<" at "<<PP(pos)<<std::endl;
+                               // Calculate direction for wall mounted stuff
+                               if(m_nodedef->get(n).wall_mounted)
+                                       n.param2 = packDir(p_under - p_above);
 
-                                       /*
-                                               Place the item
-                                       */
-                                       bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
-                                       if(remove && g_settings->getBool("creative_mode") == false)
-                                       {
-                                               InventoryList *ilist = player->inventory.getList("main");
-                                               if(ilist){
-                                                       // Remove from inventory and send inventory
-                                                       ilist->deleteItem(item_i);
-                                                       srp->m_inventory_not_sent = true;
-                                               }
+                               // Calculate the direction for furnaces and chests and stuff
+                               if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
+                               {
+                                       v3f playerpos = player->getPosition();
+                                       v3f blockpos = intToFloat(p_above, BS) - playerpos;
+                                       blockpos = blockpos.normalize();
+                                       n.param1 = 0;
+                                       if (fabs(blockpos.X) > fabs(blockpos.Z)) {
+                                               if (blockpos.X < 0)
+                                                       n.param1 = 3;
+                                               else
+                                                       n.param1 = 1;
+                                       } else {
+                                               if (blockpos.Z < 0)
+                                                       n.param1 = 2;
+                                               else
+                                                       n.param1 = 0;
                                        }
                                }
-                       }
-                       else if(pointed.type == POINTEDTHING_OBJECT)
-                       {
-                               // Right click object
 
-                               if(!build_priv)
-                                       return;
+                               /*
+                                       Send to all close-by players
+                               */
+                               core::list<u16> far_players;
+                               sendAddNode(p_above, n, 0, &far_players, 30);
+                               
+                               /*
+                                       Handle inventory
+                               */
+                               if(g_settings->getBool("creative_mode") == false)
+                               {
+                                       // Remove from inventory and send inventory
+                                       item.remove(1);
+                                       srp->setWieldedItem(item);
+                               }
 
-                               // Skip if object has been removed
-                               if(pointed_object->m_removed)
-                                       return;
+                               /*
+                                       Add node.
 
-                               actionstream<<player->getName()<<" right-clicks object "
-                                       <<pointed.object_id<<std::endl;
+                                       This takes some time so it is done after the quick stuff
+                               */
+                               core::map<v3s16, MapBlock*> modified_blocks;
+                               {
+                                       MapEditEventIgnorer ign(&m_ignore_map_edit_events);
 
-                               // Do stuff
-                               pointed_object->rightClick(srp);
+                                       std::string p_name = std::string(player->getName());
+                                       m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
+                               }
+                               /*
+                                       Set blocks not sent to far players
+                               */
+                               for(core::list<u16>::Iterator
+                                               i = far_players.begin();
+                                               i != far_players.end(); i++)
+                               {
+                                       u16 peer_id = *i;
+                                       RemoteClient *client = getClient(peer_id);
+                                       if(client==NULL)
+                                               continue;
+                                       client->SetBlocksNotSent(modified_blocks);
+                               }
+
+                               /*
+                                       Run script hook
+                               */
+                               scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
                        }
 
                } // action == 3
@@ -3471,38 +3326,25 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                */
                else if(action == 4)
                {
-                       InventoryList *ilist = player->inventory.getList("main");
-                       if(ilist == NULL)
-                               return;
-
-                       // Get item
-                       InventoryItem *item = ilist->getItem(item_i);
-                       
-                       // If there is no item, it is not possible to add it anywhere
-                       if(item == NULL)
-                               return;
-
-                       // Requires build privs
-                       if(!build_priv)
+                       // Requires interact privs
+                       if(!interact_priv)
                        {
                                infostream<<"Not allowing player to use item: "
-                                               "no build privileges"<<std::endl;
+                                               "no interact privileges"<<std::endl;
                                return;
                        }
 
-                       actionstream<<player->getName()<<" uses "<<item->getName()
+                       ItemStack item = srp->getWieldedItem();
+
+                       actionstream<<player->getName()<<" uses "<<item.name
                                        <<", pointing at "<<pointed.dump()<<std::endl;
 
-                       bool remove = item->use(m_env, srp, pointed);
-                       
-                       if(remove && g_settings->getBool("creative_mode") == false)
+                       if(scriptapi_item_on_use(m_lua,
+                                       item, srp, pointed))
                        {
-                               InventoryList *ilist = player->inventory.getList("main");
-                               if(ilist){
-                                       // Remove from inventory and send inventory
-                                       ilist->deleteItem(item_i);
-                                       srp->m_inventory_not_sent = true;
-                               }
+                               // Apply returned ItemStack
+                               if(g_settings->getBool("creative_mode") == false)
+                                       srp->setWieldedItem(item);
                        }
 
                } // action == 4
@@ -3515,9 +3357,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        infostream<<"WARNING: Server: Invalid action "
                                        <<action<<std::endl;
                }
-
-               // Complete add_to_inventory_later
-               srp->completeAddToInventoryLater(item_i);
        }
        else
        {
@@ -3549,6 +3388,9 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
        case InventoryLocation::UNDEFINED:
        {}
        break;
+       case InventoryLocation::CURRENT_PLAYER:
+       {}
+       break;
        case InventoryLocation::PLAYER:
        {
                Player *player = m_env->getPlayer(loc.name.c_str());
@@ -3570,6 +3412,33 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
        }
        return NULL;
 }
+std::string Server::getInventoryOwner(const InventoryLocation &loc)
+{
+       switch(loc.type){
+       case InventoryLocation::UNDEFINED:
+       {}
+       break;
+       case InventoryLocation::CURRENT_PLAYER:
+       {}
+       break;
+       case InventoryLocation::PLAYER:
+       {
+               return loc.name;
+       }
+       break;
+       case InventoryLocation::NODEMETA:
+       {
+               NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
+               if(!meta)
+                       return "";
+               return meta->getOwner();
+       }
+       break;
+       default:
+               assert(0);
+       }
+       return "";
+}
 void Server::setInventoryModified(const InventoryLocation &loc)
 {
        switch(loc.type){
@@ -3604,64 +3473,6 @@ void Server::setInventoryModified(const InventoryLocation &loc)
                assert(0);
        }
 }
-#if 0
-Inventory* Server::getInventory(InventoryContext *c, std::string id)
-{
-       if(id == "current_player")
-       {
-               assert(c->current_player);
-               return &(c->current_player->inventory);
-       }
-       
-       Strfnd fn(id);
-       std::string id0 = fn.next(":");
-
-       if(id0 == "nodemeta")
-       {
-               v3s16 p;
-               p.X = stoi(fn.next(","));
-               p.Y = stoi(fn.next(","));
-               p.Z = stoi(fn.next(","));
-
-               InventoryLocation loc;
-               loc.setNodeMeta(p);
-               return getInventory(loc);
-       }
-
-       infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
-       return NULL;
-}
-void Server::inventoryModified(InventoryContext *c, std::string id)
-{
-       if(id == "current_player")
-       {
-               assert(c->current_player);
-               ServerRemotePlayer *srp =
-                               static_cast<ServerRemotePlayer*>(c->current_player);
-               srp->m_inventory_not_sent = true;
-               return;
-       }
-       
-       Strfnd fn(id);
-       std::string id0 = fn.next(":");
-
-       if(id0 == "nodemeta")
-       {
-               v3s16 p;
-               p.X = stoi(fn.next(","));
-               p.Y = stoi(fn.next(","));
-               p.Z = stoi(fn.next(","));
-               v3s16 blockpos = getNodeBlockPos(p);
-
-               InventoryLocation loc;
-               loc.setNodeMeta(p);
-               setInventoryModified(loc);
-               return;
-       }
-
-       infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
-}
-#endif
 
 core::list<PlayerInfo> Server::getPlayerInfo()
 {
@@ -3783,8 +3594,8 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
        con.Send(peer_id, 0, data, true);
 }
 
-void Server::SendToolDef(con::Connection &con, u16 peer_id,
-               IToolDefManager *tooldef)
+void Server::SendItemDef(con::Connection &con, u16 peer_id,
+               IItemDefManager *itemdef)
 {
        DSTACK(__FUNCTION_NAME);
        std::ostringstream os(std::ios_base::binary);
@@ -3792,16 +3603,18 @@ void Server::SendToolDef(con::Connection &con, u16 peer_id,
        /*
                u16 command
                u32 length of the next item
-               serialized ToolDefManager
+               zlib-compressed serialized ItemDefManager
        */
-       writeU16(os, TOCLIENT_TOOLDEF);
+       writeU16(os, TOCLIENT_ITEMDEF);
        std::ostringstream tmp_os(std::ios::binary);
-       tooldef->serialize(tmp_os);
-       os<<serializeLongString(tmp_os.str());
+       itemdef->serialize(tmp_os);
+       std::ostringstream tmp_os2(std::ios::binary);
+       compressZlib(tmp_os.str(), tmp_os2);
+       os<<serializeLongString(tmp_os2.str());
 
        // Make data buffer
        std::string s = os.str();
-       infostream<<"Server::SendToolDef(): Sending tool definitions: size="
+       infostream<<"Server::SendItemDef(): Sending item definitions: size="
                        <<s.size()<<std::endl;
        SharedBuffer<u8> data((u8*)s.c_str(), s.size());
        // Send as reliable
@@ -3817,12 +3630,14 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id,
        /*
                u16 command
                u32 length of the next item
-               serialized NodeDefManager
+               zlib-compressed serialized NodeDefManager
        */
        writeU16(os, TOCLIENT_NODEDEF);
        std::ostringstream tmp_os(std::ios::binary);
        nodedef->serialize(tmp_os);
-       os<<serializeLongString(tmp_os.str());
+       std::ostringstream tmp_os2(std::ios::binary);
+       compressZlib(tmp_os.str(), tmp_os2);
+       os<<serializeLongString(tmp_os2.str());
 
        // Make data buffer
        std::string s = os.str();
@@ -3833,31 +3648,6 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id,
        con.Send(peer_id, 0, data, true);
 }
 
-void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
-               ICraftItemDefManager *craftitemdef)
-{
-       DSTACK(__FUNCTION_NAME);
-       std::ostringstream os(std::ios_base::binary);
-
-       /*
-               u16 command
-               u32 length of the next item
-               serialized CraftItemDefManager
-       */
-       writeU16(os, TOCLIENT_CRAFTITEMDEF);
-       std::ostringstream tmp_os(std::ios::binary);
-       craftitemdef->serialize(tmp_os);
-       os<<serializeLongString(tmp_os.str());
-
-       // Make data buffer
-       std::string s = os.str();
-       infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
-                       <<s.size()<<std::endl;
-       SharedBuffer<u8> data((u8*)s.c_str(), s.size());
-       // Send as reliable
-       con.Send(peer_id, 0, data, true);
-}
-
 /*
        Non-static send methods
 */
@@ -3891,28 +3681,18 @@ void Server::SendInventory(u16 peer_id)
        m_con.Send(peer_id, 0, data, true);
 }
 
-std::string getWieldedItemString(const Player *player)
-{
-       const InventoryItem *item = player->getWieldItem();
-       if (item == NULL)
-               return std::string("");
-       std::ostringstream os(std::ios_base::binary);
-       item->serialize(os);
-       return os.str();
-}
-
-void Server::SendWieldedItem(const Player* player)
+void Server::SendWieldedItem(const ServerRemotePlayer* srp)
 {
        DSTACK(__FUNCTION_NAME);
 
-       assert(player);
+       assert(srp);
 
        std::ostringstream os(std::ios_base::binary);
 
        writeU16(os, TOCLIENT_PLAYERITEM);
        writeU16(os, 1);
-       writeU16(os, player->peer_id);
-       os<<serializeString(getWieldedItemString(player));
+       writeU16(os, srp->peer_id);
+       os<<serializeString(srp->getWieldedItem().getItemString());
 
        // Make data buffer
        std::string s = os.str();
@@ -3934,8 +3714,10 @@ void Server::SendPlayerItems()
        for(i = players.begin(); i != players.end(); ++i)
        {
                Player *p = *i;
+               ServerRemotePlayer *srp =
+                       static_cast<ServerRemotePlayer*>(p);
                writeU16(os, p->peer_id);
-               os<<serializeString(getWieldedItemString(p));
+               os<<serializeString(srp->getWieldedItem().getItemString());
        }
 
        // Make data buffer
@@ -4576,6 +4358,44 @@ void Server::RespawnPlayer(Player *player)
        SendPlayerHP(player);
 }
 
+bool Server::GetCraftingResult(u16 peer_id, ItemStack &result, bool decrementInput)
+{
+       DSTACK(__FUNCTION_NAME);
+       
+       Player* player = m_env->getPlayer(peer_id);
+       assert(player);
+
+       // Get the crafting InventoryList of the player in which we will operate
+       InventoryList *clist = player->inventory.getList("craft");
+       assert(clist);
+
+       // Mangle crafting grid to an another format
+       CraftInput ci;
+       ci.method = CRAFT_METHOD_NORMAL;
+       ci.width = 3;
+       for(u16 i=0; i<9; i++)
+       {
+               ci.items.push_back(clist->getItem(i));
+       }
+
+       // Find out what is crafted and add it to result item slot
+       CraftOutput co;
+       bool found = m_craftdef->getCraftResult(ci, co, decrementInput, this);
+       if(found)
+               result.deSerialize(co.item, m_itemdef);
+
+       if(decrementInput)
+       {
+               // CraftInput has been changed, apply changes in clist
+               for(u16 i=0; i<9; i++)
+               {
+                       clist->changeItem(i, ci.items[i]);
+               }
+       }
+
+       return found;
+}
+
 void Server::UpdateCrafting(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
@@ -4601,12 +4421,11 @@ void Server::UpdateCrafting(u16 peer_id)
        if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
        {
                // Grab item out of craftresult
-               InventoryItem *item = rlist->changeItem(0, NULL);
+               ItemStack item = rlist->changeItem(0, ItemStack());
                // Try to put in main
-               InventoryItem *leftover = mlist->addItem(item);
-               // If there are leftovers, put them back to craftresult and
-               // delete leftovers
-               delete rlist->addItem(leftover);
+               ItemStack leftover = mlist->addItem(item);
+               // If there are leftovers, put them back to craftresult
+               rlist->addItem(leftover);
                // Inventory was modified
                srp->m_inventory_not_sent = true;
        }
@@ -4616,28 +4435,17 @@ void Server::UpdateCrafting(u16 peer_id)
        if(rlist->getUsedSlots() == 0)
                player->craftresult_is_preview = true;
        
-       // If it is a preview, clear the possible old preview in it
-       if(player->craftresult_is_preview)
-               rlist->clearItems();
-
        // If it is a preview, find out what is the crafting result
        // and put it in
        if(player->craftresult_is_preview)
        {
-               // Mangle crafting grid to an another format
-               std::vector<InventoryItem*> items;
-               for(u16 i=0; i<9; i++){
-                       if(clist->getItem(i) == NULL)
-                               items.push_back(NULL);
-                       else
-                               items.push_back(clist->getItem(i)->clone());
-               }
-               CraftPointerInput cpi(3, items);
+               // Clear the possible old preview in it
+               rlist->clearItems();
 
-               // Find out what is crafted and add it to result item slot
-               InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
-               if(result)
-                       rlist->addItem(result);
+               // Put the new preview in
+               ItemStack crafting_result;
+               if(GetCraftingResult(peer_id, crafting_result, false))
+                       rlist->addItem(crafting_result);
        }
 }
 
@@ -4734,9 +4542,9 @@ void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
 
 // IGameDef interface
 // Under envlock
-IToolDefManager* Server::getToolDefManager()
+IItemDefManager* Server::getItemDefManager()
 {
-       return m_toolmgr;
+       return m_itemdef;
 }
 INodeDefManager* Server::getNodeDefManager()
 {
@@ -4746,10 +4554,6 @@ ICraftDefManager* Server::getCraftDefManager()
 {
        return m_craftdef;
 }
-ICraftItemDefManager* Server::getCraftItemDefManager()
-{
-       return m_craftitemdef;
-}
 ITextureSource* Server::getTextureSource()
 {
        return NULL;
@@ -4759,9 +4563,9 @@ u16 Server::allocateUnknownNodeId(const std::string &name)
        return m_nodedef->allocateDummy(name);
 }
 
-IWritableToolDefManager* Server::getWritableToolDefManager()
+IWritableItemDefManager* Server::getWritableItemDefManager()
 {
-       return m_toolmgr;
+       return m_itemdef;
 }
 IWritableNodeDefManager* Server::getWritableNodeDefManager()
 {
@@ -4771,10 +4575,6 @@ IWritableCraftDefManager* Server::getWritableCraftDefManager()
 {
        return m_craftdef;
 }
-IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
-{
-       return m_craftitemdef;
-}
 
 const ModSpec* Server::getModSpec(const std::string &modname)
 {
@@ -4874,7 +4674,7 @@ ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
                {
                        // Warning: double code below
                        // Backup actual inventory
-                       player->inventory_backup = new Inventory();
+                       player->inventory_backup = new Inventory(m_itemdef);
                        *(player->inventory_backup) = player->inventory;
                        // Set creative inventory
                        player->resetInventory();
@@ -4919,7 +4719,7 @@ ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
                {
                        // Warning: double code above
                        // Backup actual inventory
-                       player->inventory_backup = new Inventory();
+                       player->inventory_backup = new Inventory(m_itemdef);
                        *(player->inventory_backup) = player->inventory;
                        // Set creative inventory
                        player->resetInventory();
index 5938f915dd4ddabfc0d6d30218e2059c4b127486..d979cb5186e2a4022d8b19a6548971c62fd991ab 100644 (file)
@@ -36,10 +36,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "inventorymanager.h"
 struct LuaState;
 typedef struct lua_State lua_State;
-class IWritableToolDefManager;
+class IWritableItemDefManager;
 class IWritableNodeDefManager;
 class IWritableCraftDefManager;
-class IWritableCraftItemDefManager;
 
 /*
        Some random functions
@@ -437,6 +436,7 @@ public:
                Shall be called with the environment and the connection locked.
        */
        Inventory* getInventory(const InventoryLocation &loc);
+       std::string getInventoryOwner(const InventoryLocation &loc);
        void setInventoryModified(const InventoryLocation &loc);
 
        // Connection must be locked when called
@@ -514,17 +514,15 @@ public:
        
        // IGameDef interface
        // Under envlock
-       virtual IToolDefManager* getToolDefManager();
+       virtual IItemDefManager* getItemDefManager();
        virtual INodeDefManager* getNodeDefManager();
        virtual ICraftDefManager* getCraftDefManager();
-       virtual ICraftItemDefManager* getCraftItemDefManager();
        virtual ITextureSource* getTextureSource();
        virtual u16 allocateUnknownNodeId(const std::string &name);
        
-       IWritableToolDefManager* getWritableToolDefManager();
+       IWritableItemDefManager* getWritableItemDefManager();
        IWritableNodeDefManager* getWritableNodeDefManager();
        IWritableCraftDefManager* getWritableCraftDefManager();
-       IWritableCraftItemDefManager* getWritableCraftItemDefManager();
 
        const ModSpec* getModSpec(const std::string &modname);
 
@@ -545,12 +543,10 @@ private:
                        const std::wstring &reason);
        static void SendDeathscreen(con::Connection &con, u16 peer_id,
                        bool set_camera_point_target, v3f camera_point_target);
-       static void SendToolDef(con::Connection &con, u16 peer_id,
-                       IToolDefManager *tooldef);
+       static void SendItemDef(con::Connection &con, u16 peer_id,
+                       IItemDefManager *itemdef);
        static void SendNodeDef(con::Connection &con, u16 peer_id,
                        INodeDefManager *nodedef);
-       static void SendCraftItemDef(con::Connection &con, u16 peer_id,
-                       ICraftItemDefManager *nodedef);
        
        /*
                Non-static send methods.
@@ -562,7 +558,7 @@ private:
        // Envlock and conlock should be locked when calling these
        void SendInventory(u16 peer_id);
        // send wielded item info about player to all
-       void SendWieldedItem(const Player *player);
+       void SendWieldedItem(const ServerRemotePlayer *srp);
        // send wielded item info about all players to all players
        void SendPlayerItems();
        void SendChatMessage(u16 peer_id, const std::wstring &message);
@@ -599,6 +595,7 @@ private:
        void HandlePlayerHP(Player *player, s16 damage);
        void RespawnPlayer(Player *player);
        
+       bool GetCraftingResult(u16 peer_id, ItemStack &result, bool decrementInput);
        void UpdateCrafting(u16 peer_id);
        
        // When called, connection mutex should be locked
@@ -664,8 +661,8 @@ private:
        // Envlock and conlock should be locked when using Lua
        lua_State *m_lua;
 
-       // Tool definition manager
-       IWritableToolDefManager *m_toolmgr;
+       // Item definition manager
+       IWritableItemDefManager *m_itemdef;
        
        // Node definition manager
        IWritableNodeDefManager *m_nodedef;
@@ -673,9 +670,6 @@ private:
        // Craft definition manager
        IWritableCraftDefManager *m_craftdef;
        
-       // CraftItem definition manager
-       IWritableCraftItemDefManager *m_craftitemdef;
-       
        // Mods
        core::list<ModSpec> m_mods;
        
@@ -740,7 +734,7 @@ private:
        core::list<std::string> m_modspaths;
 
        bool m_shutdown_requested;
-       
+
        /*
                Map edit event queue. Automatically receives all map edits.
                The constructor of this class registers us to receive them through
index ca3d2c3b98d2790de87d64905446a6fb0e273e99..2609e301534a82abb2fd61112eb56c549e22d5fb 100644 (file)
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "serverobject.h"
 #include <fstream>
 #include "inventory.h"
-#include "tooldef.h"
+#include "materials.h"
 
 ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos):
        ActiveObject(0),
@@ -67,10 +67,31 @@ void ServerActiveObject::registerType(u16 type, Factory f)
        m_types.insert(type, f);
 }
 
-void ServerActiveObject::getWieldDiggingProperties(ToolDiggingProperties *dst)
+ItemStack ServerActiveObject::getWieldedItem() const
 {
-       *dst = ToolDiggingProperties();
+       const Inventory *inv = getInventory();
+       if(inv)
+       {
+               const InventoryList *list = inv->getList(getWieldList());
+               if(list)
+                       return list->getItem(getWieldIndex());
+       }
+       return ItemStack();
 }
 
-
+bool ServerActiveObject::setWieldedItem(const ItemStack &item)
+{
+       Inventory *inv = getInventory();
+       if(inv)
+       {
+               InventoryList *list = inv->getList(getWieldList());
+               if (list)
+               {
+                       list->changeItem(getWieldIndex(), item);
+                       setInventoryModified();
+                       return true;
+               }
+       }
+       return false;
+}
 
index fd8a51a9e06d716f7145aa2a9439ded617a669eb..94ceb4895c4feb9bee56393dafeaf381c1734eee 100644 (file)
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes.h"
 #include "activeobject.h"
 #include "utility.h"
+#include "inventorymanager.h"
 
 /*
 
@@ -41,7 +42,7 @@ Some planning
 */
 
 class ServerEnvironment;
-class InventoryItem;
+class ItemStack;
 class Player;
 struct ToolDiggingProperties;
 
@@ -138,19 +139,27 @@ public:
        {}
        virtual void rightClick(ServerActiveObject *clicker)
        {}
-       virtual void getWieldDiggingProperties(ToolDiggingProperties *dst);
-       virtual void damageWieldedItem(u16 amount)
-       {}
-       // If all fits, eats item and returns true. Otherwise returns false.
-       virtual bool addToInventory(InventoryItem *item)
-       { return false; }
-       virtual void addToInventoryLater(InventoryItem *item)
-       {}
        virtual void setHP(s16 hp)
        {}
        virtual s16 getHP()
        { return 0; }
 
+       // Inventory and wielded item
+       virtual Inventory* getInventory()
+       { return NULL; }
+       virtual const Inventory* getInventory() const
+       { return NULL; }
+       virtual InventoryLocation getInventoryLocation() const
+       { return InventoryLocation(); }
+       virtual void setInventoryModified()
+       {}
+       virtual std::string getWieldList() const
+       { return ""; }
+       virtual int getWieldIndex() const
+       { return 0; }
+       virtual ItemStack getWieldedItem() const;
+       virtual bool setWieldedItem(const ItemStack &item);
+
        /*
                Number of players which know about this object. Object won't be
                deleted until this is 0 to keep the id preserved for the right
index 1681900e009d66f2cc3e8846c19f2d2965e96c84..db4b44707905d0b28363b225e26c6c7e678667d5 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "log.h"
 #include "gamedef.h"
-#include "tooldef.h"
+#include "inventory.h"
 #include "environment.h"
 #include "materials.h"
 
@@ -31,6 +31,7 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env):
        ServerActiveObject(env, v3f(0,0,0)),
        m_last_good_position(0,0,0),
        m_last_good_position_age(0),
+       m_wield_index(0),
        m_inventory_not_sent(false),
        m_hp_not_sent(false),
        m_respawn_active(false),
@@ -57,7 +58,6 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 pee
 }
 ServerRemotePlayer::~ServerRemotePlayer()
 {
-       clearAddToInventoryLater();
 }
 
 void ServerRemotePlayer::setPosition(const v3f &position)
@@ -67,12 +67,41 @@ void ServerRemotePlayer::setPosition(const v3f &position)
        m_position_not_sent = true;
 }
 
-InventoryItem* ServerRemotePlayer::getWieldedItem()
+Inventory* ServerRemotePlayer::getInventory()
 {
-       InventoryList *list = inventory.getList("main");
-       if (list)
-               return list->getItem(m_selected_item);
-       return NULL;
+       return &inventory;
+}
+
+const Inventory* ServerRemotePlayer::getInventory() const
+{
+       return &inventory;
+}
+
+InventoryLocation ServerRemotePlayer::getInventoryLocation() const
+{
+       InventoryLocation loc;
+       loc.setPlayer(getName());
+       return loc;
+}
+
+void ServerRemotePlayer::setInventoryModified()
+{
+       m_inventory_not_sent = true;
+}
+
+std::string ServerRemotePlayer::getWieldList() const
+{
+       return "main";
+}
+
+int ServerRemotePlayer::getWieldIndex() const
+{
+       return m_wield_index;
+}
+
+void ServerRemotePlayer::setWieldIndex(int i)
+{
+       m_wield_index = i;
 }
 
 /* ServerActiveObject interface */
@@ -156,8 +185,10 @@ void ServerRemotePlayer::punch(ServerActiveObject *puncher,
        mp.crackiness = -0.5;
        mp.cuttability = 0.5;
 
-       ToolDiggingProperties tp;
-       puncher->getWieldDiggingProperties(&tp);
+       IItemDefManager *idef = m_env->getGameDef()->idef();
+       ItemStack punchitem = puncher->getWieldedItem();
+       ToolDiggingProperties tp =
+               punchitem.getToolDiggingProperties(idef);
 
        HittingProperties hitprop = getHittingProperties(&mp, &tp,
                        time_from_last_punch);
@@ -167,7 +198,8 @@ void ServerRemotePlayer::punch(ServerActiveObject *puncher,
                        <<" HP"<<std::endl;
        
        setHP(getHP() - hitprop.hp);
-       puncher->damageWieldedItem(hitprop.wear);
+       punchitem.addWear(hitprop.wear, idef);
+       puncher->setWieldedItem(punchitem);
        
        if(hitprop.hp != 0)
        {
@@ -201,109 +233,6 @@ void ServerRemotePlayer::moveTo(v3f pos, bool continuous)
        m_last_good_position_age = 0;
 }
 
-void ServerRemotePlayer::getWieldDiggingProperties(ToolDiggingProperties *dst)
-{
-       IGameDef *gamedef = m_env->getGameDef();
-       IToolDefManager *tdef = gamedef->tdef();
-
-       InventoryItem *item = getWieldedItem();
-       if(item == NULL || std::string(item->getName()) != "ToolItem"){
-               *dst = ToolDiggingProperties();
-               return;
-       }
-       ToolItem *titem = (ToolItem*)item;
-       *dst = tdef->getDiggingProperties(titem->getToolName());
-}
-
-void ServerRemotePlayer::damageWieldedItem(u16 amount)
-{
-       infostream<<"Damaging "<<getName()<<"'s wielded item for amount="
-                       <<amount<<std::endl;
-       InventoryList *list = inventory.getList("main");
-       if(!list)
-               return;
-       InventoryItem *item = list->getItem(m_selected_item);
-       if(item && (std::string)item->getName() == "ToolItem"){
-               ToolItem *titem = (ToolItem*)item;
-               bool weared_out = titem->addWear(amount);
-               if(weared_out)
-                       list->deleteItem(m_selected_item);
-       }
-}
-bool ServerRemotePlayer::addToInventory(InventoryItem *item)
-{
-       infostream<<"Adding "<<item->getName()<<" into "<<getName()
-                       <<"'s inventory"<<std::endl;
-       
-       InventoryList *ilist = inventory.getList("main");
-       if(ilist == NULL)
-               return false;
-       
-       // In creative mode, just delete the item
-       if(g_settings->getBool("creative_mode")){
-               return false;
-       }
-
-       // Skip if inventory has no free space
-       if(ilist->roomForItem(item) == false)
-       {
-               infostream<<"Player inventory has no free space"<<std::endl;
-               return false;
-       }
-
-       // Add to inventory
-       InventoryItem *leftover = ilist->addItem(item);
-       assert(!leftover);
-       
-       m_inventory_not_sent = true;
-
-       return true;
-}
-void ServerRemotePlayer::addToInventoryLater(InventoryItem *item)
-{
-       infostream<<"Adding (later) "<<item->getName()<<" into "<<getName()
-                       <<"'s inventory"<<std::endl;
-       m_additional_items.push_back(item);
-}
-void ServerRemotePlayer::clearAddToInventoryLater()
-{
-       for (std::vector<InventoryItem*>::iterator
-                       i = m_additional_items.begin();
-                       i != m_additional_items.end(); i++)
-       {
-               delete *i;
-       }
-       m_additional_items.clear();
-}
-void ServerRemotePlayer::completeAddToInventoryLater(u16 preferred_index)
-{
-       InventoryList *ilist = inventory.getList("main");
-       if(ilist == NULL)
-       {
-               clearAddToInventoryLater();
-               return;
-       }
-       
-       // In creative mode, just delete the items
-       if(g_settings->getBool("creative_mode"))
-       {
-               clearAddToInventoryLater();
-               return;
-       }
-       
-       for (std::vector<InventoryItem*>::iterator
-                       i = m_additional_items.begin();
-                       i != m_additional_items.end(); i++)
-       {
-               InventoryItem *item = *i;
-               InventoryItem *leftover = item;
-               leftover = ilist->addItem(preferred_index, leftover);
-               leftover = ilist->addItem(leftover);
-               delete leftover;
-       }
-       m_additional_items.clear();
-       m_inventory_not_sent = true;
-}
 void ServerRemotePlayer::setHP(s16 hp_)
 {
        s16 oldhp = hp;
index bdc3bba208f5553edddb1112c6661962b3a899fe..9d943764619db4a4e8f56948ea216469ee1a8b41 100644 (file)
@@ -46,9 +46,6 @@ public:
        
        virtual void setPosition(const v3f &position);
        
-       // Returns a reference
-       virtual InventoryItem* getWieldedItem();
-       
        /* ServerActiveObject interface */
 
        u8 getType() const
@@ -77,19 +74,20 @@ public:
        virtual std::string getDescription()
        {return std::string("player ")+getName();}
 
-       virtual void getWieldDiggingProperties(ToolDiggingProperties *dst);
-       virtual void damageWieldedItem(u16 amount);
-       // If all fits, eats item and returns true. Otherwise returns false.
-       virtual bool addToInventory(InventoryItem *item);
-       virtual void addToInventoryLater(InventoryItem *item);
-       void clearAddToInventoryLater();
-       void completeAddToInventoryLater(u16 preferred_index);
+       virtual Inventory* getInventory();
+       virtual const Inventory* getInventory() const;
+       virtual InventoryLocation getInventoryLocation() const;
+       virtual void setInventoryModified();
+       virtual std::string getWieldList() const;
+       virtual int getWieldIndex() const;
+       virtual void setWieldIndex(int i);
+
        virtual void setHP(s16 hp_);
        virtual s16 getHP();
        
        v3f m_last_good_position;
        float m_last_good_position_age;
-       std::vector<InventoryItem*> m_additional_items;
+       int m_wield_index;
        bool m_inventory_not_sent;
        bool m_hp_not_sent;
        bool m_respawn_active;
index f778f2d9149431122d91838daec2187e13c09999..caf31d1af9d2e823372e18852b0b583f2ce928ed 100644 (file)
@@ -47,6 +47,75 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        assert(exception_thrown);\
 }
 
+/*
+       A few item and node definitions for those tests that need them
+*/
+
+#define CONTENT_STONE 0
+#define CONTENT_GRASS 0x800
+
+void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *ndef)
+{
+       content_t i;
+       ItemDefinition itemdef;
+       ContentFeatures f;
+
+       /*
+               Stone
+       */
+       i = CONTENT_STONE;
+       itemdef = ItemDefinition();
+       itemdef.type = ITEM_NODE;
+       itemdef.name = "default:stone";
+       itemdef.description = "Stone";
+       itemdef.inventory_image = "[inventorycube"
+               "{default_stone.png"
+               "{default_stone.png"
+               "{default_stone.png";
+       f = ContentFeatures();
+       f.name = itemdef.name;
+       for(int i = 0; i < 6; i++)
+               f.tname_tiles[i] = "default_stone.png";
+       f.param_type = CPT_MINERAL;
+       f.is_ground_content = true;
+       f.dug_item = itemdef.name;
+       f.material.diggability = DIGGABLE_NORMAL;
+       f.material.weight = 5.0;
+       f.material.crackiness = 1.0;
+       f.material.crumbliness = -0.1;
+       f.material.cuttability = -0.2;
+       idef->registerItem(itemdef);
+       ndef->set(i, f);
+
+       /*
+               Grass
+       */
+       i = CONTENT_GRASS;
+       itemdef = ItemDefinition();
+       itemdef.type = ITEM_NODE;
+       itemdef.name = "default:dirt_with_grass";
+       itemdef.description = "Dirt with grass";
+       itemdef.inventory_image = "[inventorycube"
+               "{default_grass.png"
+               "{default_dirt.png&default_grass_side.png"
+               "{default_dirt.png&default_grass_side.png";
+       f = ContentFeatures();
+       f.name = itemdef.name;
+       f.tname_tiles[0] = "default_grass.png";
+       f.tname_tiles[1] = "default_dirt.png";
+       for(int i = 2; i < 6; i++)
+               f.tname_tiles[i] = "default_dirt.png^default_grass_side.png";
+       f.is_ground_content = true;
+       f.dug_item = itemdef.name;
+       f.material.diggability = DIGGABLE_NORMAL;
+       f.material.weight = 1.2;
+       f.material.crackiness = 0.0;
+       f.material.crumbliness = 1.2;
+       f.material.cuttability = -0.4;
+       idef->registerItem(itemdef);
+       ndef->set(i, f);
+}
+
 struct TestUtilities
 {
        void Run()
@@ -96,7 +165,118 @@ struct TestSettings
                assert(fabs(s.getV3F("coord2").Z - 3.3) < 0.001);
        }
 };
+
+struct TestSerialization
+{
+       // To be used like this:
+       //   mkstr("Some\0string\0with\0embedded\0nuls")
+       // since std::string("...") doesn't work as expected in that case.
+       template<size_t N> std::string mkstr(const char (&s)[N])
+       {
+               return std::string(s, N - 1);
+       }
+
+       void Run()
+       {
+               // Tests some serialization primitives
+
+               assert(serializeString("") == mkstr("\0\0"));
+               assert(serializeWideString(L"") == mkstr("\0\0"));
+               assert(serializeLongString("") == mkstr("\0\0\0\0"));
+               assert(serializeJsonString("") == "\"\"");
                
+               std::string teststring = "Hello world!";
+               assert(serializeString(teststring) ==
+                       mkstr("\0\14Hello world!"));
+               assert(serializeWideString(narrow_to_wide(teststring)) ==
+                       mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!"));
+               assert(serializeLongString(teststring) ==
+                       mkstr("\0\0\0\14Hello world!"));
+               assert(serializeJsonString(teststring) ==
+                       "\"Hello world!\"");
+
+               std::string teststring2;
+               std::wstring teststring2_w;
+               std::string teststring2_w_encoded;
+               {
+                       std::ostringstream tmp_os;
+                       std::wostringstream tmp_os_w;
+                       std::ostringstream tmp_os_w_encoded;
+                       for(int i = 0; i < 256; i++)
+                       {
+                               tmp_os<<(char)i;
+                               tmp_os_w<<(wchar_t)i;
+                               tmp_os_w_encoded<<(char)0<<(char)i;
+                       }
+                       teststring2 = tmp_os.str();
+                       teststring2_w = tmp_os_w.str();
+                       teststring2_w_encoded = tmp_os_w_encoded.str();
+               }
+               assert(serializeString(teststring2) ==
+                       mkstr("\1\0") + teststring2);
+               assert(serializeWideString(teststring2_w) ==
+                       mkstr("\1\0") + teststring2_w_encoded);
+               assert(serializeLongString(teststring2) ==
+                       mkstr("\0\0\1\0") + teststring2);
+               assert(serializeJsonString(teststring2) ==
+                       mkstr("\"") +
+                       "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
+                       "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
+                       "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
+                       "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
+                       " !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
+                       "\\/" + teststring2.substr(0x30, 0x5c-0x30) +
+                       "\\\\" + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
+                       "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
+                       "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
+                       "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
+                       "\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" +
+                       "\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" +
+                       "\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" +
+                       "\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" +
+                       "\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" +
+                       "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" +
+                       "\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" +
+                       "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" +
+                       "\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" +
+                       "\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" +
+                       "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
+                       "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
+                       "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
+                       "\"");
+
+               {
+                       std::istringstream is(serializeString(teststring2), std::ios::binary);
+                       assert(deSerializeString(is) == teststring2);
+                       assert(!is.eof());
+                       is.get();
+                       assert(is.eof());
+               }
+               {
+                       std::istringstream is(serializeWideString(teststring2_w), std::ios::binary);
+                       assert(deSerializeWideString(is) == teststring2_w);
+                       assert(!is.eof());
+                       is.get();
+                       assert(is.eof());
+               }
+               {
+                       std::istringstream is(serializeLongString(teststring2), std::ios::binary);
+                       assert(deSerializeLongString(is) == teststring2);
+                       assert(!is.eof());
+                       is.get();
+                       assert(is.eof());
+               }
+               {
+                       std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
+                       //dstream<<serializeJsonString(deSerializeJsonString(is));
+                       assert(deSerializeJsonString(is) == teststring2);
+                       assert(!is.eof());
+                       is.get();
+                       assert(is.eof());
+               }
+       }
+};
+
 struct TestCompress
 {
        void Run()
@@ -283,11 +463,11 @@ struct TestVoxelManipulator
 
                infostream<<"*** Setting (-1,0,-1)=2 ***"<<std::endl;
                
-               v.setNodeNoRef(v3s16(-1,0,-1), MapNode(2));
+               v.setNodeNoRef(v3s16(-1,0,-1), MapNode(CONTENT_GRASS));
 
                v.print(infostream, nodedef);
 
-               assert(v.getNode(v3s16(-1,0,-1)).getContent() == 2);
+               assert(v.getNode(v3s16(-1,0,-1)).getContent() == CONTENT_GRASS);
 
                infostream<<"*** Reading from inexistent (0,0,-1) ***"<<std::endl;
 
@@ -301,7 +481,7 @@ struct TestVoxelManipulator
                
                v.print(infostream, nodedef);
 
-               assert(v.getNode(v3s16(-1,0,-1)).getContent() == 2);
+               assert(v.getNode(v3s16(-1,0,-1)).getContent() == CONTENT_GRASS);
                EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,1,1)));
        }
 };
@@ -1086,16 +1266,18 @@ void run_tests()
 {
        DSTACK(__FUNCTION_NAME);
        
-       // Create node definitions
-       IWritableNodeDefManager *nodedef = createNodeDefManager();
-       content_mapnode_init(nodedef);
+       // Create item and node definitions
+       IWritableItemDefManager *idef = createItemDefManager();
+       IWritableNodeDefManager *ndef = createNodeDefManager();
+       define_some_nodes(idef, ndef);
 
        infostream<<"run_tests() started"<<std::endl;
        TEST(TestUtilities);
        TEST(TestSettings);
        TEST(TestCompress);
-       TESTPARAMS(TestMapNode, nodedef);
-       TESTPARAMS(TestVoxelManipulator, nodedef);
+       TEST(TestSerialization);
+       TESTPARAMS(TestMapNode, ndef);
+       TESTPARAMS(TestVoxelManipulator, ndef);
        //TEST(TestMapBlock);
        //TEST(TestMapSector);
        if(INTERNET_SIMULATOR == false){
index 89f3451973e078dc8b47fa10916110df8e891e3e..1d5f4d8338c97ec1d4237c5a3c5e09de7dfbddb7 100644 (file)
@@ -337,6 +337,12 @@ public:
                return ap.atlas;
        }
 
+       // Returns a pointer to the irrlicht device
+       virtual IrrlichtDevice* getDevice()
+       {
+               return m_device;
+       }
+
        // Update new texture pointer and texture coordinates to an
        // AtlasPointer based on it's texture id
        void updateAP(AtlasPointer &ap);
@@ -469,8 +475,6 @@ u32 TextureSource::getTextureId(const std::string &name)
        return 0;
 }
 
-// Draw a progress bar on the image
-void make_progressbar(float value, video::IImage *image);
 // Brighten image
 void brighten(video::IImage *image);
 
@@ -816,14 +820,12 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
                if(j == CONTENT_IGNORE || j == CONTENT_AIR)
                        continue;
                const ContentFeatures &f = ndef->get(j);
-               for(std::set<std::string>::const_iterator
-                               i = f.used_texturenames.begin();
-                               i != f.used_texturenames.end(); i++)
+               for(u32 i=0; i<6; i++)
                {
-                       std::string name = *i;
+                       std::string name = f.tname_tiles[i];
                        sourcelist[name] = true;
 
-                       if(f.often_contains_mineral){
+                       if(f.param_type == CPT_MINERAL){
                                for(int k=1; k<MINERAL_COUNT; k++){
                                        std::string mineraltexture = mineral_block_texture(k);
                                        std::string fulltexture = name + "^" + mineraltexture;
@@ -1316,23 +1318,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                                }
                        }
                }
-               /*
-                       [progressbarN
-                       Adds a progress bar, 0.0 <= N <= 1.0
-               */
-               else if(part_of_name.substr(0,12) == "[progressbar")
-               {
-                       if(baseimg == NULL)
-                       {
-                               errorstream<<"generate_image(): baseimg==NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       float value = stof(part_of_name.substr(12));
-                       make_progressbar(value, baseimg);
-               }
                /*
                        "[brighten"
                */
@@ -1442,23 +1427,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        std::string imagename_left = sf.next("{");
                        std::string imagename_right = sf.next("{");
 
-#if 1
-                       // TODO: Create cube with different textures on different sides
-
-                       if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
-                       {
-                               errorstream<<"generate_image(): EVDF_RENDER_TO_TARGET"
-                                               " not supported. Creating fallback image"<<std::endl;
-                               baseimg = generate_image_from_scratch(
-                                               imagename_top, device, sourcecache);
-                               return true;
-                       }
-                       
-                       u32 w0 = 64;
-                       u32 h0 = 64;
-                       //infostream<<"inventorycube w="<<w0<<" h="<<h0<<std::endl;
-                       core::dimension2d<u32> dim(w0,h0);
-                       
                        // Generate images for the faces of the cube
                        video::IImage *img_top = generate_image_from_scratch(
                                        imagename_top, device, sourcecache);
@@ -1482,84 +1450,65 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        img_left->drop();
                        img_right->drop();
                        
-                       // Create render target texture
-                       video::ITexture *rtt = NULL;
-                       std::string rtt_name = part_of_name + "_RTT";
-                       rtt = driver->addRenderTargetTexture(dim, rtt_name.c_str(),
-                                       video::ECF_A8R8G8B8);
-                       assert(rtt);
-                       
-                       // Set render target
-                       driver->setRenderTarget(rtt, true, true,
-                                       video::SColor(0,0,0,0));
-                       
-                       // Get a scene manager
-                       scene::ISceneManager *smgr_main = device->getSceneManager();
-                       assert(smgr_main);
-                       scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
-                       assert(smgr);
-                       
                        /*
-                               Create scene:
-                               - An unit cube is centered at 0,0,0
-                               - Camera looks at cube from Y+, Z- towards Y-, Z+
+                               Draw a cube mesh into a render target texture
                        */
-
                        scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
                        setMeshColor(cube, video::SColor(255, 255, 255, 255));
-
-                       scene::IMeshSceneNode* cubenode = smgr->addMeshSceneNode(cube, NULL, -1, v3f(0,0,0), v3f(0,45,0), v3f(1,1,1), true);
-                       cube->drop();
-
-                       // Set texture of cube
-                       cubenode->getMaterial(0).setTexture(0, texture_top);
-                       cubenode->getMaterial(1).setTexture(0, texture_top);
-                       cubenode->getMaterial(2).setTexture(0, texture_right);
-                       cubenode->getMaterial(3).setTexture(0, texture_right);
-                       cubenode->getMaterial(4).setTexture(0, texture_left);
-                       cubenode->getMaterial(5).setTexture(0, texture_left);
-                       cubenode->setMaterialFlag(video::EMF_LIGHTING, true);
-                       cubenode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
-                       cubenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
-
-                       scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
-                                       v3f(0, 1.0, -1.5), v3f(0, 0, 0));
+                       cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
+                       cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
+                       cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
+                       cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
+                       cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
+                       cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
+
+                       core::dimension2d<u32> dim(64,64);
+                       std::string rtt_texture_name = part_of_name + "_RTT";
+
+                       v3f camera_position(0, 1.0, -1.5);
+                       camera_position.rotateXZBy(45);
+                       v3f camera_lookat(0, 0, 0);
+                       core::CMatrix4<f32> camera_projection_matrix;
                        // Set orthogonal projection
-                       core::CMatrix4<f32> pm;
-                       pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100);
-                       camera->setProjectionMatrix(pm, true);
-
-                       /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
-                                       v3f(-50, 100, -75), video::SColorf(0.5,0.5,0.5), 1000);
-
-                       smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
-
-                       // Render scene
-                       driver->beginScene(true, true, video::SColor(0,0,0,0));
-                       smgr->drawAll();
-                       driver->endScene();
+                       camera_projection_matrix.buildProjectionMatrixOrthoLH(
+                                       1.65, 1.65, 0, 100);
+
+                       video::SColorf ambient_light(0.2,0.2,0.2);
+                       v3f light_position(10, 100, -50);
+                       video::SColorf light_color(0.5,0.5,0.5);
+                       f32 light_radius = 1000;
+
+                       video::ITexture *rtt = generateTextureFromMesh(
+                                       cube, device, dim, rtt_texture_name,
+                                       camera_position,
+                                       camera_lookat,
+                                       camera_projection_matrix,
+                                       ambient_light,
+                                       light_position,
+                                       light_color,
+                                       light_radius);
                        
-                       // NOTE: The scene nodes should not be dropped, otherwise
-                       //       smgr->drop() segfaults
-                       /*cube->drop();
-                       camera->drop();
-                       light->drop();*/
-                       // Drop scene manager
-                       smgr->drop();
-                       
-                       // Unset render target
-                       driver->setRenderTarget(0, true, true, 0);
+                       // Drop mesh
+                       cube->drop();
 
                        // Free textures of images
                        driver->removeTexture(texture_top);
                        driver->removeTexture(texture_left);
                        driver->removeTexture(texture_right);
                        
+                       if(rtt == NULL)
+                       {
+                               errorstream<<"generate_image(): render to texture failed."
+                                               " Creating fallback image"<<std::endl;
+                               baseimg = generate_image_from_scratch(
+                                               imagename_top, device, sourcecache);
+                               return true;
+                       }
+
                        // Create image of render target
                        video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
-
                        assert(image);
-                       
+
                        baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
 
                        if(image)
@@ -1567,7 +1516,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                                image->copyTo(baseimg);
                                image->drop();
                        }
-#endif
                }
                else
                {
@@ -1579,38 +1527,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
        return true;
 }
 
-void make_progressbar(float value, video::IImage *image)
-{
-       if(image == NULL)
-               return;
-       
-       core::dimension2d<u32> size = image->getDimension();
-
-       u32 barheight = size.Height/16;
-       u32 barpad_x = size.Width/16;
-       u32 barpad_y = size.Height/16;
-       u32 barwidth = size.Width - barpad_x*2;
-       v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
-
-       u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
-
-       video::SColor active(255,255,0,0);
-       video::SColor inactive(255,0,0,0);
-       for(u32 x0=0; x0<barwidth; x0++)
-       {
-               video::SColor *c;
-               if(x0 < barvalue_i)
-                       c = &active;
-               else
-                       c = &inactive;
-               u32 x = x0 + barpos.X;
-               for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
-               {
-                       image->setPixel(x,y, *c);
-               }
-       }
-}
-
 void brighten(video::IImage *image)
 {
        if(image == NULL)
index 23849ca1fcc382258797be30e1e321947efea598..c0d8914b09b6c09d288dc045f8caa9271588d0da 100644 (file)
@@ -110,6 +110,8 @@ public:
                {return AtlasPointer(0);}
        virtual video::ITexture* getTextureRaw(const std::string &name)
                {return NULL;}
+       virtual IrrlichtDevice* getDevice()
+               {return NULL;}
        virtual void updateAP(AtlasPointer &ap){};
 };
 
@@ -126,6 +128,8 @@ public:
                {return AtlasPointer(0);}
        virtual video::ITexture* getTextureRaw(const std::string &name)
                {return NULL;}
+       virtual IrrlichtDevice* getDevice()
+               {return NULL;}
        virtual void updateAP(AtlasPointer &ap){};
 
        virtual void processQueue()=0;
diff --git a/src/tooldef.cpp b/src/tooldef.cpp
deleted file mode 100644 (file)
index 7d7ecea..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
-Minetest-c55
-Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 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 General Public License for more details.
-
-You should have received a copy of the GNU 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.
-*/
-
-#include "tooldef.h"
-#include "irrlichttypes.h"
-#include "log.h"
-#include <sstream>
-#include "utility.h"
-#include <map>
-
-ToolDiggingProperties::ToolDiggingProperties(float full_punch_interval_,
-               float a, float b, float c, float d, float e,
-               float f, float g, float h, float i, float j):
-       full_punch_interval(full_punch_interval_),
-       basetime(a),
-       dt_weight(b),
-       dt_crackiness(c),
-       dt_crumbliness(d),
-       dt_cuttability(e),
-       basedurability(f),
-       dd_weight(g),
-       dd_crackiness(h),
-       dd_crumbliness(i),
-       dd_cuttability(j)
-{}
-
-std::string ToolDefinition::dump()
-{
-       std::ostringstream os(std::ios::binary);
-       os<<"[ToolDefinition::dump() not implemented due to lazyness]"
-                       <<std::endl;
-       return os.str();
-}
-
-void ToolDefinition::serialize(std::ostream &os)
-{
-       writeU8(os, 0); // version
-       os<<serializeString(imagename);
-       writeF1000(os, properties.basetime);
-       writeF1000(os, properties.dt_weight);
-       writeF1000(os, properties.dt_crackiness);
-       writeF1000(os, properties.dt_crumbliness);
-       writeF1000(os, properties.dt_cuttability);
-       writeF1000(os, properties.basedurability);
-       writeF1000(os, properties.dd_weight);
-       writeF1000(os, properties.dd_crackiness);
-       writeF1000(os, properties.dd_crumbliness);
-       writeF1000(os, properties.dd_cuttability);
-       writeF1000(os, properties.full_punch_interval);
-}
-
-void ToolDefinition::deSerialize(std::istream &is)
-{
-       int version = readU8(is);
-       if(version != 0) throw SerializationError(
-                       "unsupported ToolDefinition version");
-       imagename = deSerializeString(is);
-       properties.basetime = readF1000(is);
-       properties.dt_weight = readF1000(is);
-       properties.dt_crackiness = readF1000(is);
-       properties.dt_crumbliness = readF1000(is);
-       properties.dt_cuttability = readF1000(is);
-       properties.basedurability = readF1000(is);
-       properties.dd_weight = readF1000(is);
-       properties.dd_crackiness = readF1000(is);
-       properties.dd_crumbliness = readF1000(is);
-       properties.dd_cuttability = readF1000(is);
-       try{
-               properties.full_punch_interval = readF1000(is);
-       }catch(SerializationError &e){} // Temporary for 0.4.dev
-}
-
-class CToolDefManager: public IWritableToolDefManager
-{
-public:
-       virtual ~CToolDefManager()
-       {
-               clear();
-       }
-       virtual const ToolDefinition* getToolDefinition(const std::string &toolname_) const
-       {
-               // Convert name according to possible alias
-               std::string toolname = getAlias(toolname_);
-               // Get the definition
-               core::map<std::string, ToolDefinition*>::Node *n;
-               n = m_tool_definitions.find(toolname);
-               if(n == NULL)
-                       return NULL;
-               return n->getValue();
-       }
-       virtual std::string getImagename(const std::string &toolname) const
-       {
-               const ToolDefinition *def = getToolDefinition(toolname);
-               if(def == NULL)
-                       return "";
-               return def->imagename;
-       }
-       virtual ToolDiggingProperties getDiggingProperties(
-                       const std::string &toolname) const
-       {
-               const ToolDefinition *def = getToolDefinition(toolname);
-               // If tool does not exist, just return an impossible
-               if(def == NULL){
-                       // If tool does not exist, try empty name
-                       const ToolDefinition *def = getToolDefinition("");
-                       if(def == NULL) // If that doesn't exist either, return default
-                               return ToolDiggingProperties();
-                       return def->properties;
-               }
-               return def->properties;
-       }
-       virtual std::string getAlias(const std::string &name) const
-       {
-               std::map<std::string, std::string>::const_iterator i;
-               i = m_aliases.find(name);
-               if(i != m_aliases.end())
-                       return i->second;
-               return name;
-       }
-       // IWritableToolDefManager
-       virtual bool registerTool(std::string toolname, const ToolDefinition &def)
-       {
-               infostream<<"registerTool: registering tool \""<<toolname<<"\""<<std::endl;
-               m_tool_definitions[toolname] = new ToolDefinition(def);
-
-               // Remove conflicting alias if it exists
-               bool alias_removed = (m_aliases.erase(toolname) != 0);
-               if(alias_removed)
-                       infostream<<"tdef: erased alias "<<toolname
-                                       <<" because tool was defined"<<std::endl;
-               
-               return true;
-       }
-       virtual void clear()
-       {
-               for(core::map<std::string, ToolDefinition*>::Iterator
-                               i = m_tool_definitions.getIterator();
-                               i.atEnd() == false; i++){
-                       delete i.getNode()->getValue();
-               }
-               m_tool_definitions.clear();
-               m_aliases.clear();
-       }
-       virtual void setAlias(const std::string &name,
-                       const std::string &convert_to)
-       {
-               if(getToolDefinition(name) != NULL){
-                       infostream<<"tdef: not setting alias "<<name<<" -> "<<convert_to
-                                       <<": "<<name<<" is already defined"<<std::endl;
-                       return;
-               }
-               infostream<<"tdef: setting alias "<<name<<" -> "<<convert_to
-                               <<std::endl;
-               m_aliases[name] = convert_to;
-       }
-       virtual void serialize(std::ostream &os)
-       {
-               writeU8(os, 0); // version
-               u16 count = m_tool_definitions.size();
-               writeU16(os, count);
-               for(core::map<std::string, ToolDefinition*>::Iterator
-                               i = m_tool_definitions.getIterator();
-                               i.atEnd() == false; i++){
-                       std::string name = i.getNode()->getKey();
-                       ToolDefinition *def = i.getNode()->getValue();
-                       // Serialize name
-                       os<<serializeString(name);
-                       // Serialize ToolDefinition and write wrapped in a string
-                       std::ostringstream tmp_os(std::ios::binary);
-                       def->serialize(tmp_os);
-                       os<<serializeString(tmp_os.str());
-               }
-
-               writeU16(os, m_aliases.size());
-               for(std::map<std::string, std::string>::const_iterator
-                               i = m_aliases.begin(); i != m_aliases.end(); i++)
-               {
-                       os<<serializeString(i->first);
-                       os<<serializeString(i->second);
-               }
-       }
-       virtual void deSerialize(std::istream &is)
-       {
-               // Clear everything
-               clear();
-               // Deserialize
-               int version = readU8(is);
-               if(version != 0) throw SerializationError(
-                               "unsupported ToolDefManager version");
-               u16 count = readU16(is);
-               for(u16 i=0; i<count; i++){
-                       // Deserialize name
-                       std::string name = deSerializeString(is);
-                       // Deserialize a string and grab a ToolDefinition from it
-                       std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
-                       ToolDefinition def;
-                       def.deSerialize(tmp_is);
-                       // Register
-                       registerTool(name, def);
-               }
-
-               u16 num_aliases = readU16(is);
-               if(!is.eof()){
-                       for(u16 i=0; i<num_aliases; i++){
-                               std::string name = deSerializeString(is);
-                               std::string convert_to = deSerializeString(is);
-                               m_aliases[name] = convert_to;
-                       }
-               }
-       }
-private:
-       // Key is name
-       core::map<std::string, ToolDefinition*> m_tool_definitions;
-       // Aliases
-       std::map<std::string, std::string> m_aliases;
-};
-
-IWritableToolDefManager* createToolDefManager()
-{
-       return new CToolDefManager();
-}
-
diff --git a/src/tooldef.h b/src/tooldef.h
deleted file mode 100644 (file)
index 68a8948..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-Minetest-c55
-Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 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 General Public License for more details.
-
-You should have received a copy of the GNU 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.
-*/
-
-#ifndef TOOLDEF_HEADER
-#define TOOLDEF_HEADER
-
-#include <string>
-#include <iostream>
-
-struct ToolDiggingProperties
-{
-       // time = basetime + sum(feature here * feature in MaterialProperties)
-       float full_punch_interval;
-       float basetime;
-       float dt_weight;
-       float dt_crackiness;
-       float dt_crumbliness;
-       float dt_cuttability;
-       float basedurability;
-       float dd_weight;
-       float dd_crackiness;
-       float dd_crumbliness;
-       float dd_cuttability;
-
-       ToolDiggingProperties(float full_punch_interval_=2.0,
-                       float a=0.75, float b=0, float c=0, float d=0, float e=0,
-                       float f=50, float g=0, float h=0, float i=0, float j=0);
-};
-
-struct ToolDefinition
-{
-       std::string imagename;
-       ToolDiggingProperties properties;
-
-       ToolDefinition(){}
-       ToolDefinition(const std::string &imagename_,
-                       ToolDiggingProperties properties_):
-               imagename(imagename_),
-               properties(properties_)
-       {}
-       
-       std::string dump();
-       void serialize(std::ostream &os);
-       void deSerialize(std::istream &is);
-};
-
-class IToolDefManager
-{
-public:
-       IToolDefManager(){}
-       virtual ~IToolDefManager(){}
-       virtual const ToolDefinition* getToolDefinition(const std::string &toolname) const=0;
-       virtual std::string getImagename(const std::string &toolname) const =0;
-       virtual ToolDiggingProperties getDiggingProperties(
-                       const std::string &toolname) const =0;
-       virtual std::string getAlias(const std::string &name) const =0;
-       
-       virtual void serialize(std::ostream &os)=0;
-};
-
-class IWritableToolDefManager : public IToolDefManager
-{
-public:
-       IWritableToolDefManager(){}
-       virtual ~IWritableToolDefManager(){}
-       virtual const ToolDefinition* getToolDefinition(const std::string &toolname) const=0;
-       virtual std::string getImagename(const std::string &toolname) const =0;
-       virtual ToolDiggingProperties getDiggingProperties(
-                       const std::string &toolname) const =0;
-       virtual std::string getAlias(const std::string &name) const =0;
-                       
-       virtual bool registerTool(std::string toolname, const ToolDefinition &def)=0;
-       virtual void clear()=0;
-       // Set an alias so that entries named <name> will load as <convert_to>.
-       // Alias is not set if <name> has already been defined.
-       // Alias will be removed if <name> is defined at a later point of time.
-       virtual void setAlias(const std::string &name,
-                       const std::string &convert_to)=0;
-
-       virtual void serialize(std::ostream &os)=0;
-       virtual void deSerialize(std::istream &is)=0;
-};
-
-IWritableToolDefManager* createToolDefManager();
-
-#endif
-
index 4e9f307d8396c7d90be5ba699977cb99f2ba307d..06b60884f8b547485e8ec8426eeb65f0d7398251 100644 (file)
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "sha1.h"
 #include "base64.h"
 #include "log.h"
+#include <iomanip>
 
 TimeTaker::TimeTaker(const char *name, u32 *result)
 {
@@ -234,6 +235,100 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
        return true;
 }
 
+// Creates a string encoded in JSON format (almost equivalent to a C string literal)
+std::string serializeJsonString(const std::string &plain)
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"\"";
+       for(size_t i = 0; i < plain.size(); i++)
+       {
+               char c = plain[i];
+               switch(c)
+               {
+                       case '"': os<<"\\\""; break;
+                       case '\\': os<<"\\\\"; break;
+                       case '/': os<<"\\/"; break;
+                       case '\b': os<<"\\b"; break;
+                       case '\f': os<<"\\f"; break;
+                       case '\n': os<<"\\n"; break;
+                       case '\r': os<<"\\r"; break;
+                       case '\t': os<<"\\t"; break;
+                       default:
+                       {
+                               if(c >= 32 && c <= 126)
+                               {
+                                       os<<c;
+                               }
+                               else
+                               {
+                                       u32 cnum = (u32) (u8) c;
+                                       os<<"\\u"<<std::hex<<std::setw(4)<<std::setfill('0')<<cnum;
+                               }
+                               break;
+                       }
+               }
+       }
+       os<<"\"";
+       return os.str();
+}
+
+// Reads a string encoded in JSON format
+std::string deSerializeJsonString(std::istream &is)
+{
+       std::ostringstream os(std::ios::binary);
+       char c, c2;
+
+       // Parse initial doublequote
+       is >> c;
+       if(c != '"')
+               throw SerializationError("JSON string must start with doublequote");
+
+       // Parse characters
+       for(;;)
+       {
+               c = is.get();
+               if(is.eof())
+                       throw SerializationError("JSON string ended prematurely");
+               if(c == '"')
+               {
+                       return os.str();
+               }
+               else if(c == '\\')
+               {
+                       c2 = is.get();
+                       if(is.eof())
+                               throw SerializationError("JSON string ended prematurely");
+                       switch(c2)
+                       {
+                               default:  os<<c2; break;
+                               case 'b': os<<'\b'; break;
+                               case 'f': os<<'\f'; break;
+                               case 'n': os<<'\n'; break;
+                               case 'r': os<<'\r'; break;
+                               case 't': os<<'\t'; break;
+                               case 'u':
+                               {
+                                       char hexdigits[4+1];
+                                       is.read(hexdigits, 4);
+                                       if(is.eof())
+                                               throw SerializationError("JSON string ended prematurely");
+                                       hexdigits[4] = 0;
+                                       std::istringstream tmp_is(hexdigits, std::ios::binary);
+                                       int hexnumber;
+                                       tmp_is >> std::hex >> hexnumber;
+                                       os<<((char)hexnumber);
+                                       break;
+                               }
+                       }
+               }
+               else
+               {
+                       os<<c;
+               }
+       }
+       return os.str();
+}
+
 // Get an sha-1 hash of the player's name combined with
 // the password entered. That's what the server uses as
 // their password. (Exception : if the password field is
index 14b49772bdbdbf4f6e9b83e4ce44831638f866e9..50f27c11bf3d4814eb8cc0ed8d38d53cd648a759 100644 (file)
@@ -1694,6 +1694,12 @@ inline std::string deSerializeLongString(std::istream &is)
        return s;
 }
 
+// Creates a string encoded in JSON format (almost equivalent to a C string literal)
+std::string serializeJsonString(const std::string &plain);
+
+// Reads a string encoded in JSON format
+std::string deSerializeJsonString(std::istream &is);
+
 //
 
 inline u32 time_to_daynight_ratio(u32 time_of_day)