diff options
65 files changed, 7129 insertions, 7179 deletions
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 diff --git a/data/clienttextures/unknown_item.png b/data/clienttextures/unknown_item.png Binary files differnew file mode 100644 index 000000000..35cabf0ad --- /dev/null +++ b/data/clienttextures/unknown_item.png diff --git a/data/mods/bucket/init.lua b/data/mods/bucket/init.lua index 639a614d4..8ed9da522 100644 --- a/data/mods/bucket/init.lua +++ b/data/mods/bucket/init.lua @@ -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, }) diff --git a/data/mods/default/init.lua b/data/mods/default/init.lua index aa03eabe8..2bd950230 100644 --- a/data/mods/default/init.lua +++ b/data/mods/default/init.lua @@ -77,24 +77,25 @@ -- 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)) @@ -113,13 +114,16 @@ -- 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) @@ -133,10 +137,14 @@ -- 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 @@ -171,8 +179,6 @@ -- 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) @@ -194,12 +200,13 @@ -- ^ 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} @@ -216,9 +223,6 @@ -- - 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) @@ -230,14 +234,41 @@ -- - 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: @@ -271,29 +302,38 @@ -- 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}, @@ -309,7 +349,6 @@ -- climbable = false, -- buildable_to = false, -- wall_mounted = false, --- often_contains_mineral = false, -- dug_item = "", -- extra_dug_item = "", -- extra_dug_item_rarity = 2, @@ -329,34 +368,67 @@ -- 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) -- diff --git a/data/mods/experimental/init.lua b/data/mods/experimental/init.lua index 2aae9b199..a58d8f08d 100644 --- a/data/mods/experimental/init.lua +++ b/data/mods/experimental/init.lua @@ -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 diff --git a/data/mods/legacy/init.lua b/data/mods/legacy/init.lua index 3bd68f0bc..127b0e17f 100644 --- a/data/mods/legacy/init.lua +++ b/data/mods/legacy/init.lua @@ -5,97 +5,126 @@ -- 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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b3b326c4..e82e26dc7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/camera.cpp b/src/camera.cpp index c0e171468..066208569 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -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; -} diff --git a/src/camera.h b/src/camera.h index d5789d807..56c99d101 100644 --- a/src/camera.h +++ b/src/camera.h @@ -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 diff --git a/src/client.cpp b/src/client.cpp index 38ed14978..feb8a3a1e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -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; diff --git a/src/client.h b/src/client.h index 49794acf5..6d5e5c525 100644 --- a/src/client.h +++ b/src/client.h @@ -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; }; diff --git a/src/clientserver.h b/src/clientserver.h index 43de689e4..acb4f8530 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -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 diff --git a/src/connection.cpp b/src/connection.cpp index b9c5d2ac8..8f308c99f 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -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); diff --git a/src/content_cao.cpp b/src/content_cao.cpp index d581b35dc..a2708674b 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -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(); } /* diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index e8e4fd231..79f49cbf8 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -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 diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index a74760ba3..4701736cf 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -27,75 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #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: 0x000...0x07f (0...127): param2 is fully usable @@ -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); -} - - diff --git a/src/content_mapnode.h b/src/content_mapnode.h index b928e4407..003b7edd7 100644 --- a/src/content_mapnode.h +++ b/src/content_mapnode.h @@ -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); diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp index 8666051a4..9fb5450cf 100644 --- a/src/content_nodemeta.cpp +++ b/src/content_nodemeta.cpp @@ -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); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index f195b16bd..02be64c64 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -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() diff --git a/src/content_sao.h b/src/content_sao.h index c2bb9c3f5..f0c9cea90 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -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; diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 0cbb74ea0..5bcbf6f94 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -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() diff --git a/src/craftdef.h b/src/craftdef.h index cfd58ad10..57f26f049 100644 --- a/src/craftdef.h +++ b/src/craftdef.h @@ -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 index 4461e38a7..000000000 --- a/src/craftitemdef.cpp +++ /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 index b5d4b9348..000000000 --- a/src/craftitemdef.h +++ /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 diff --git a/src/environment.h b/src/environment.h index beb49885c..89c9fd676 100644 --- a/src/environment.h +++ b/src/environment.h @@ -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; diff --git a/src/game.cpp b/src/game.cpp index 0d08074ad..7bd5d9587 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -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" @@ -80,15 +79,6 @@ struct ChatLine }; /* - 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"; @@ -872,6 +854,12 @@ void the_game( } /* + After all content has been received: + Update cached textures, meshes and materials + */ + client.afterContentReceived(); + + /* Create skybox */ float old_brightness = 1.0; @@ -911,6 +899,11 @@ void the_game( } /* + 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,25 +2259,13 @@ 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; } diff --git a/src/gamedef.h b/src/gamedef.h index c450568b7..8df6988ad 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -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();} }; diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index 552e10db2..9b2aed377 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -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 ¤t_name) + const InventoryLocation ¤t_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("]"); } diff --git a/src/guiInventoryMenu.h b/src/guiInventoryMenu.h index 359268687..c3b3e5a64 100644 --- a/src/guiInventoryMenu.h +++ b/src/guiInventoryMenu.h @@ -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 ¤t_name); + const InventoryLocation ¤t_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; diff --git a/src/inventory.cpp b/src/inventory.cpp index 0d38bed78..ebd0b9c23 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -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; } diff --git a/src/inventory.h b/src/inventory.h index 15de3c8e7..3f5d83589 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -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 diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 195438d26..0149fd9ab 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -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; -} - - diff --git a/src/inventorymanager.h b/src/inventorymanager.h index 4abe5e73d..f6ae4feba 100644 --- a/src/inventorymanager.h +++ b/src/inventorymanager.h @@ -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 diff --git a/src/irrlichttypes.h b/src/irrlichttypes.h index bc17694fc..67c96a653 100644 --- a/src/irrlichttypes.h +++ b/src/irrlichttypes.h @@ -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 index 000000000..aa888bbdf --- /dev/null +++ b/src/itemdef.cpp @@ -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 index 000000000..5a432591d --- /dev/null +++ b/src/itemdef.h @@ -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 diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 016cb222e..73c13caca 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -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 diff --git a/src/mapblock.h b/src/mapblock.h index aa5aa43f7..f2d94753c 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -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; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index c051dda50..db9057e19 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -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, diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 36cc9be24..4d3e7d29d 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -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 diff --git a/src/materials.cpp b/src/materials.cpp index d2d37ebf4..146209d58 100644 --- a/src/materials.cpp +++ b/src/materials.cpp @@ -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) { diff --git a/src/materials.h b/src/materials.h index b25d047be..face7d5df 100644 --- a/src/materials.h +++ b/src/materials.h @@ -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, diff --git a/src/mesh.cpp b/src/mesh.cpp index 1d347a09f..5afb4af59 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -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; +} diff --git a/src/mesh.h b/src/mesh.h index 33e072927..57166235e 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -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. @@ -48,6 +49,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 */ void setMeshColor(scene::IMesh *mesh, const video::SColor &color); @@ -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 diff --git a/src/mineral.cpp b/src/mineral.cpp index 038251fa3..4e495fce6 100644 --- a/src/mineral.cpp +++ b/src/mineral.cpp @@ -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(); +} + diff --git a/src/mineral.h b/src/mineral.h index 4949fe07e..a945d0e02 100644 --- a/src/mineral.h +++ b/src/mineral.h @@ -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 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index d7769700b..87469bfb5 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -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() diff --git a/src/nodedef.h b/src/nodedef.h index fdf2f8c45..598ad7fb3 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -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,23 +202,9 @@ 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 */ bool isLiquid() const{ @@ -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(); diff --git a/src/player.cpp b/src/player.cpp index 963f67c28..b072746c6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -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 diff --git a/src/player.h b/src/player.h index 1c9dde7e0..43f6ee5ec 100644 --- a/src/player.h +++ b/src/player.h @@ -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; diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index b617626a0..187d1a894 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -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,46 +126,282 @@ public: } }; -std::string get_current_modname(lua_State *L) +/* + Getters for stuff in main tables +*/ + +static Server* get_server(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); + // Get server from registry + lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); + Server *server = (Server*)lua_touserdata(L, -1); lua_pop(L, 1); - return modname; + return server; } -void check_modname_prefix(lua_State *L, std::string &name) +static ServerEnvironment* get_env(lua_State *L) { - if(name.size() == 0) - throw LuaError(L, std::string("Name is empty")); - - if(name[0] == ':'){ - name = name.substr(1); - return; + // Get environment from registry + lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env"); + ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1); + lua_pop(L, 1); + return env; +} + +static void objectref_get(lua_State *L, u16 id) +{ + // 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 void luaentity_get(lua_State *L, u16 id) +{ + // 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 +} + +/* + Table field getters +*/ + +static bool getstringfield(lua_State *L, int table, + const char *fieldname, std::string &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isstring(L, -1)){ + size_t len = 0; + const char *ptr = lua_tolstring(L, -1, &len); + result.assign(ptr, len); + got = true; } - - 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"); + lua_pop(L, 1); + return got; +} + +static bool getintfield(lua_State *L, int table, + const char *fieldname, int &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isnumber(L, -1)){ + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +static bool getfloatfield(lua_State *L, int table, + const char *fieldname, float &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isnumber(L, -1)){ + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +static bool getboolfield(lua_State *L, int table, + const char *fieldname, bool &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isboolean(L, -1)){ + result = lua_toboolean(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +static std::string checkstringfield(lua_State *L, int table, + const char *fieldname) +{ + lua_getfield(L, table, fieldname); + std::string s = luaL_checkstring(L, -1); + lua_pop(L, 1); + return s; +} + +static std::string getstringfield_default(lua_State *L, int table, + const char *fieldname, const std::string &default_) +{ + std::string result = default_; + getstringfield(L, table, fieldname, result); + return result; +} + +static int getintfield_default(lua_State *L, int table, + const char *fieldname, int default_) +{ + int result = default_; + getintfield(L, table, fieldname, result); + return result; +} + +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_) +{ + bool result = default_; + getboolfield(L, table, fieldname, result); + return result; +} + +struct EnumString +{ + int num; + const char *str; +}; + +static bool string_to_enum(const EnumString *spec, int &result, + const std::string &str) +{ + const EnumString *esp = spec; + while(esp->str){ + if(str == std::string(esp->str)){ + result = esp->num; + return true; + } + esp++; + } + return false; +} + +/*static bool enum_to_string(const EnumString *spec, std::string &result, + int num) +{ + const EnumString *esp = spec; + while(esp){ + if(num == esp->num){ + result = esp->str; + return true; + } + esp++; + } + return false; +}*/ + +static int getenumfield(lua_State *L, int table, + const char *fieldname, const EnumString *spec, int default_) +{ + int result = default_; + string_to_enum(spec, result, + getstringfield_default(L, table, fieldname, "")); + return result; +} + +static void setfloatfield(lua_State *L, int table, + const char *fieldname, float value) +{ + lua_pushnumber(L, value); + if(table < 0) + table -= 1; + lua_setfield(L, table, fieldname); +} + +static void warn_if_field_exists(lua_State *L, int table, + const char *fieldname, const std::string &message) +{ + lua_getfield(L, table, fieldname); + if(!lua_isnil(L, -1)){ + infostream<<script_get_backtrace(L)<<std::endl; + infostream<<"WARNING: field \""<<fieldname<<"\": " + <<message<<std::endl; + } + lua_pop(L, 1); } +/* + EnumString definitions +*/ + +struct EnumString es_ItemType[] = +{ + {ITEM_NONE, "none"}, + {ITEM_NODE, "node"}, + {ITEM_CRAFT, "craft"}, + {ITEM_TOOL, "tool"}, + {0, NULL}, +}; + +struct EnumString es_DrawType[] = +{ + {NDT_NORMAL, "normal"}, + {NDT_AIRLIKE, "airlike"}, + {NDT_LIQUID, "liquid"}, + {NDT_FLOWINGLIQUID, "flowingliquid"}, + {NDT_GLASSLIKE, "glasslike"}, + {NDT_ALLFACES, "allfaces"}, + {NDT_ALLFACES_OPTIONAL, "allfaces_optional"}, + {NDT_TORCHLIKE, "torchlike"}, + {NDT_SIGNLIKE, "signlike"}, + {NDT_PLANTLIKE, "plantlike"}, + {NDT_FENCELIKE, "fencelike"}, + {NDT_RAILLIKE, "raillike"}, + {0, NULL}, +}; + +struct EnumString es_ContentParamType[] = +{ + {CPT_NONE, "none"}, + {CPT_LIGHT, "light"}, + {CPT_MINERAL, "mineral"}, + {CPT_FACEDIR_SIMPLE, "facedir_simple"}, + {0, NULL}, +}; + +struct EnumString es_LiquidType[] = +{ + {LIQUID_NONE, "none"}, + {LIQUID_FLOWING, "flowing"}, + {LIQUID_SOURCE, "source"}, + {0, NULL}, +}; + +struct EnumString es_NodeBoxType[] = +{ + {NODEBOX_REGULAR, "regular"}, + {NODEBOX_FIXED, "fixed"}, + {NODEBOX_WALLMOUNTED, "wallmounted"}, + {0, NULL}, +}; + +struct EnumString es_Diggability[] = +{ + {DIGGABLE_NOT, "not"}, + {DIGGABLE_NORMAL, "normal"}, + {DIGGABLE_CONSTANT, "constant"}, + {0, NULL}, +}; + +/* + C struct <-> Lua table converter functions +*/ + static void push_v3f(lua_State *L, v3f p) { lua_newtable(L); @@ -204,20 +439,6 @@ static v2f read_v2f(lua_State *L, int index) return p; } -static Server* get_server(lua_State *L) -{ - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - return (Server*)lua_touserdata(L, -1); -} - -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; @@ -365,170 +586,321 @@ static core::aabbox3d<f32> read_aabbox3df32(lua_State *L, int index, f32 scale) return box; } -static bool getstringfield(lua_State *L, int table, - const char *fieldname, std::string &result) +/* + ToolDiggingProperties +*/ + +static ToolDiggingProperties read_tool_digging_properties( + lua_State *L, int table) { - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isstring(L, -1)){ - result = lua_tostring(L, -1); - got = true; - } - lua_pop(L, 1); - return got; + 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 bool getintfield(lua_State *L, int table, - const char *fieldname, int &result) +static void set_tool_digging_properties(lua_State *L, int table, + const ToolDiggingProperties &prop) { - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); - got = true; - } - lua_pop(L, 1); - return got; + 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 bool getfloatfield(lua_State *L, int table, - const char *fieldname, float &result) +static void push_tool_digging_properties(lua_State *L, + const ToolDiggingProperties &prop) { - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); - got = true; - } - lua_pop(L, 1); - return got; + lua_newtable(L); + set_tool_digging_properties(L, -1, prop); } -static bool getboolfield(lua_State *L, int table, - const char *fieldname, bool &result) +/* + PointedThing +*/ + +static void push_pointed_thing(lua_State *L, const PointedThing& pointed) { - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isboolean(L, -1)){ - result = lua_toboolean(L, -1); - got = true; + 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"); } - lua_pop(L, 1); - return got; } -static std::string checkstringfield(lua_State *L, int table, - const char *fieldname) +/* + ItemDefinition +*/ + +static ItemDefinition read_item_definition(lua_State *L, int index) { - lua_getfield(L, table, fieldname); - std::string s = luaL_checkstring(L, -1); + 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); - return s; -} -static std::string getstringfield_default(lua_State *L, int table, - const char *fieldname, const std::string &default_) -{ - std::string result = default_; - getstringfield(L, table, fieldname, result); - return result; -} + def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max); + if(def.stack_max == 0) + def.stack_max = 1; -static int getintfield_default(lua_State *L, int table, - const char *fieldname, int default_) -{ - int result = default_; - getintfield(L, table, fieldname, result); - return result; -} + lua_getfield(L, index, "on_use"); + def.usable = lua_isfunction(L, -1); + lua_pop(L, 1); -/*static float getfloatfield_default(lua_State *L, int table, - const char *fieldname, float default_) -{ - float result = default_; - getfloatfield(L, table, fieldname, result); - return result; -}*/ + getboolfield(L, index, "liquids_pointable", def.liquids_pointable); -static bool getboolfield_default(lua_State *L, int table, - const char *fieldname, bool default_) -{ - bool result = default_; - getboolfield(L, table, fieldname, result); - return result; + 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; } -struct EnumString -{ - int num; - const char *str; -}; +/* + ContentFeatures +*/ -static bool string_to_enum(const EnumString *spec, int &result, - const std::string &str) +static ContentFeatures read_content_features(lua_State *L, int index) { - const EnumString *esp = spec; - while(esp->str){ - if(str == std::string(esp->str)){ - result = esp->num; - return true; + 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++; + } } - esp++; } - return false; -} + lua_pop(L, 1); -/*static bool enum_to_string(const EnumString *spec, std::string &result, - int num) -{ - const EnumString *esp = spec; - while(esp){ - if(num == esp->num){ - result = esp->str; - return true; + 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; + } } - esp++; } - return false; -}*/ + lua_pop(L, 1); -static int getenumfield(lua_State *L, int table, - const char *fieldname, const EnumString *spec, int default_) -{ - int result = default_; - string_to_enum(spec, result, - getstringfield_default(L, table, fieldname, "")); - return result; -} + f.alpha = getintfield_default(L, index, "alpha", 255); -static void setfloatfield(lua_State *L, int table, - const char *fieldname, float value) -{ - lua_pushnumber(L, value); - if(table < 0) - table -= 1; - lua_setfield(L, table, fieldname); -} + /* 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); -static void warn_if_field_exists(lua_State *L, int table, - const char *fieldname, const std::string &message) -{ - lua_getfield(L, table, fieldname); - if(!lua_isnil(L, -1)){ - infostream<<script_get_backtrace(L)<<std::endl; - infostream<<"WARNING: field \""<<fieldname<<"\": " - <<message<<std::endl; + 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, IGameDef *gamedef, int forcesize=-1) + 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 @@ -537,39 +909,30 @@ static void inventory_set_list_from_lua(Inventory *inv, const char *name, return; } // Otherwise set list - std::list<std::string> items; + std::vector<ItemStack> items; luaL_checktype(L, tableindex, LUA_TTABLE); - int table = tableindex; lua_pushnil(L); - while(lua_next(L, table) != 0){ + while(lua_next(L, tableindex) != 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); + 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::list<std::string>::const_iterator + for(std::vector<ItemStack>::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; + invlist->changeItem(index, *i); index++; } while(forcesize != -1 && index < forcesize){ - InventoryItem *olditem = invlist->changeItem(index, NULL); - delete olditem; + invlist->deleteItem(index); index++; } + dstream<<"inventory_set_list_from_lua done\n"; } static void inventory_get_list_to_lua(Inventory *inv, const char *name, @@ -588,375 +951,317 @@ static void inventory_get_list_to_lua(Inventory *inv, const char *name, lua_newtable(L); int table = lua_gettop(L); for(u32 i=0; i<invlist->getSize(); i++){ - InventoryItem *item = invlist->getItem(i); + ItemStack 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()); - } + 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 + Helpful macros for userdata classes */ -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); -} +#define method(class, name) {#name, class::l_##name} /* - ToolDefinition + LuaItemStack */ -static ToolDefinition read_tool_definition(lua_State *L, int table) +class LuaItemStack { - ToolDefinition def; - getstringfield(L, table, "image", def.imagename); - def.properties = read_tool_digging_properties(L, table); - return def; -} +private: + ItemStack m_stack; -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); -} + static const char className[]; + static const luaL_reg methods[]; -/* - EnumString definitions -*/ + // Exported functions + + // garbage collector + static int gc_object(lua_State *L) + { + LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1)); + delete o; + return 0; + } -struct EnumString es_DrawType[] = -{ - {NDT_NORMAL, "normal"}, - {NDT_AIRLIKE, "airlike"}, - {NDT_LIQUID, "liquid"}, - {NDT_FLOWINGLIQUID, "flowingliquid"}, - {NDT_GLASSLIKE, "glasslike"}, - {NDT_ALLFACES, "allfaces"}, - {NDT_ALLFACES_OPTIONAL, "allfaces_optional"}, - {NDT_TORCHLIKE, "torchlike"}, - {NDT_SIGNLIKE, "signlike"}, - {NDT_PLANTLIKE, "plantlike"}, - {NDT_FENCELIKE, "fencelike"}, - {NDT_RAILLIKE, "raillike"}, - {0, NULL}, -}; + // 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; + } -struct EnumString es_ContentParamType[] = -{ - {CPT_NONE, "none"}, - {CPT_LIGHT, "light"}, - {CPT_MINERAL, "mineral"}, - {CPT_FACEDIR_SIMPLE, "facedir_simple"}, - {0, NULL}, -}; + // 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; + } -struct EnumString es_LiquidType[] = -{ - {LIQUID_NONE, "none"}, - {LIQUID_FLOWING, "flowing"}, - {LIQUID_SOURCE, "source"}, - {0, NULL}, -}; + // 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; + } -struct EnumString es_NodeBoxType[] = -{ - {NODEBOX_REGULAR, "regular"}, - {NODEBOX_FIXED, "fixed"}, - {NODEBOX_WALLMOUNTED, "wallmounted"}, - {0, NULL}, -}; + // 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; + } -struct EnumString es_Diggability[] = -{ - {DIGGABLE_NOT, "not"}, - {DIGGABLE_NORMAL, "normal"}, - {DIGGABLE_CONSTANT, "constant"}, - {0, NULL}, -}; + // 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; + } -/* - Getters for stuff in main tables -*/ + // 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; + } -static void objectref_get(lua_State *L, u16 id) -{ - // 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 -} + // 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; + } -static void luaentity_get(lua_State *L, u16 id) -{ - // 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 -} + // 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; + } -/* - Object wrappers -*/ + // 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; + } -#define method(class, name) {#name, class::l_##name} + // 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; + } -/* - ItemStack -*/ + // 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; + } -class ItemStack -{ -private: - InventoryItem *m_stack; + // 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; + } - static const char className[]; - static const luaL_reg methods[]; + // 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; - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) { - ItemStack *o = *(ItemStack **)(lua_touserdata(L, 1)); - delete o; - return 0; + // 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; } @@ -2070,73 +2394,6 @@ private: 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) - { - 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); - // Do it - inventory_get_list_to_lua(&player->inventory, name, L); - return 1; - } - - // get_wielded_itemstring(self) - static int l_get_wielded_itemstring(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); - if(player == NULL) return 0; - // Do it - InventoryItem *item = player->getWieldedItem(); - if(item == NULL){ - lua_pushnil(L); - return 1; - } - lua_pushstring(L, item->getItemString().c_str()); - return 1; - } - - // get_wielded_item(self) - static int l_get_wielded_item(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); - if(player == NULL) return 0; - // Do it - InventoryItem *item0 = player->getWieldedItem(); - push_stack_item(L, item0); - 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: @@ -2749,454 +2960,318 @@ public: } }; -// register_abm({...}) -static int l_register_abm(lua_State *L) +// debug(text) +// Writes a line to dstream +static int l_debug(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); + std::string text = lua_tostring(L, 1); + dstream << text << std::endl; + return 0; +} - 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++; +// 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); } - 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 */ + 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; } -// register_tool(name, {lots of stuff}) -static int l_register_tool(lua_State *L) +// register_item_raw({lots of stuff}) +static int l_register_item_raw(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); + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; - tooldef->registerTool(name, def); - return 0; /* number of results */ -} + // Get the writable item and node definition managers from the server + IWritableItemDefManager *idef = + get_server(L)->getWritableItemDefManager(); + IWritableNodeDefManager *ndef = + get_server(L)->getWritableNodeDefManager(); -// 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 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"); + } // 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); + // Read the item definition and register it + ItemDefinition def = read_item_definition(L, table); + idef->registerItem(def); - lua_pushvalue(L, table); - scriptapi_add_craftitem(L, name.c_str()); + // 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); + } return 0; /* number of results */ } -// register_node(name, {lots of stuff}) -static int l_register_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); - 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); + std::string convert_to = luaL_checkstring(L, 2); - /* - Create definition - */ + // Get the writable item definition manager from the server + IWritableItemDefManager *idef = + get_server(L)->getWritableItemDefManager(); - ContentFeatures f; - - // Default to getting the corresponding NodeItem when dug - f.dug_item = std::string("NodeItem \"")+name+"\" 1"; + idef->registerAlias(name, convert_to); - // Default to unknown_block.png as all textures - f.setAllTextures("unknown_block.png"); - - /* - Read definiton from Lua - */ + return 0; /* number of results */ +} - f.name = name; - - /* Visual definition */ +// helper for register_craft +static bool read_craft_recipe_shaped(lua_State *L, int index, + int &width, std::vector<std::string> &recipe) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; - f.drawtype = (NodeDrawType)getenumfield(L, nodedef_table, "drawtype", es_DrawType, - NDT_NORMAL); - getfloatfield(L, nodedef_table, "visual_scale", f.visual_scale); + if(!lua_istable(L, index)) + return false; - lua_getfield(L, nodedef_table, "tile_images"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); + 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); - int i = 0; - while(lua_next(L, table) != 0){ + while(lua_next(L, table2) != 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] = ""; + 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); - i++; - if(i==6){ - lua_pop(L, 1); - break; - } + colcount++; } - // 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; - } + if(rowcount == 0){ + width = colcount; + } else { + if(colcount != width) + return false; } - } - 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, "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); + // removes value, keeps key for next iteration lua_pop(L, 1); + rowcount++; } - 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); - } - 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) -{ - 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(); - - nodedef->setAlias(name, convert_to); - - return 0; /* number of results */ + return width != 0; } -// alias_tool(name, convert_to_name) -static int l_alias_tool(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 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); + 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; } -// alias_craftitem(name, convert_to_name) -static int l_alias_craftitem(lua_State *L) +// helper for register_craft +static bool read_craft_replacements(lua_State *L, int index, + CraftReplacements &replacements) { - 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_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(output, width, input); - craftdef->registerCraft(def); + 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 = 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,35 +3391,23 @@ 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); @@ -3370,30 +3433,12 @@ 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) +// 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) { - 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) -{ - // 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; } /* diff --git a/src/scriptapi.h b/src/scriptapi.h index af8afa3d9..198f60525 100644 --- a/src/scriptapi.h +++ b/src/scriptapi.h @@ -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. diff --git a/src/server.cpp b/src/server.cpp index d704bf861..3c0cab2a9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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(); diff --git a/src/server.h b/src/server.h index 5938f915d..d979cb518 100644 --- a/src/server.h +++ b/src/server.h @@ -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 diff --git a/src/serverobject.cpp b/src/serverobject.cpp index ca3d2c3b9..2609e3015 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -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; +} diff --git a/src/serverobject.h b/src/serverobject.h index fd8a51a9e..94ceb4895 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -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 diff --git a/src/serverremoteplayer.cpp b/src/serverremoteplayer.cpp index 1681900e0..db4b44707 100644 --- a/src/serverremoteplayer.cpp +++ b/src/serverremoteplayer.cpp @@ -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; diff --git a/src/serverremoteplayer.h b/src/serverremoteplayer.h index bdc3bba20..9d9437646 100644 --- a/src/serverremoteplayer.h +++ b/src/serverremoteplayer.h @@ -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; diff --git a/src/test.cpp b/src/test.cpp index f778f2d91..caf31d1af 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -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){ diff --git a/src/tile.cpp b/src/tile.cpp index 89f345197..1d5f4d833 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -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; @@ -1317,23 +1319,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" */ else if(part_of_name.substr(0,9) == "[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) diff --git a/src/tile.h b/src/tile.h index 23849ca1f..c0d8914b0 100644 --- a/src/tile.h +++ b/src/tile.h @@ -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 index 7d7eceab0..000000000 --- a/src/tooldef.cpp +++ /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 index 68a894898..000000000 --- a/src/tooldef.h +++ /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 - diff --git a/src/utility.cpp b/src/utility.cpp index 4e9f307d8..06b60884f 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -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 diff --git a/src/utility.h b/src/utility.h index 14b49772b..50f27c11b 100644 --- a/src/utility.h +++ b/src/utility.h @@ -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) |