From 6a76c226e10e92c3e3339096f07f8ab065e2098b Mon Sep 17 00:00:00 2001 From: Kahrl Date: Thu, 12 Jan 2012 06:10:39 +0100 Subject: The huge item definition and item namespace unification patch (itemdef), see http://c55.me/minetest/wiki/doku.php?id=changes:itemdef --- data/builtin.lua | 589 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 397 insertions(+), 192 deletions(-) (limited to 'data/builtin.lua') diff --git a/data/builtin.lua b/data/builtin.lua index 1046e934e..1926d88b4 100644 --- a/data/builtin.lua +++ b/data/builtin.lua @@ -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 -- cgit v1.2.3