diff options
author | sapier <sapier at gmx dot net> | 2012-02-04 12:41:25 +0100 |
---|---|---|
committer | sapier <sapier at gmx dot net> | 2012-02-04 12:41:25 +0100 |
commit | 77df09540c4d7eadef760779e123af88a48aafaa (patch) | |
tree | febf1009328d30c31a14a1a1ca1600e99b4c3e6b | |
parent | 3454e6779337d8523ae76c7fa16eb8c565aa2381 (diff) | |
parent | a1eb2836c0764829ebad1462432bb3c5f32750df (diff) | |
download | minetest-77df09540c4d7eadef760779e123af88a48aafaa.tar.xz |
Merge remote branch 'upstream/master' into sapier_experimental
Conflicts:
src/scriptapi.cpp
83 files changed, 9587 insertions, 8608 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 68fffb012..ef9016526 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ project(minetest) # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH dev-20120106-1) +set(VERSION_PATCH dev-20120122-1) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") MESSAGE(STATUS "*** Will build version ${VERSION_STRING} ***") diff --git a/data/builtin.lua b/data/builtin.lua index 1046e934e..3a38b60ad 100644 --- a/data/builtin.lua +++ b/data/builtin.lua @@ -80,15 +80,305 @@ function dump(o, dumped) end -- --- Built-in node definitions. Also defined in C. +-- Item definition helpers -- -minetest.register_nodedef_defaults({ +function minetest.inventorycube(img1, img2, img3) + img2 = img2 or img1 + img3 = img3 or img1 + return "[inventorycube" + .. "{" .. img1:gsub("%^", "&") + .. "{" .. img2:gsub("%^", "&") + .. "{" .. img3:gsub("%^", "&") +end + +function minetest.pos_to_string(pos) + return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")" +end + +function minetest.get_pointed_thing_position(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.dir_to_facedir(dir) + if math.abs(dir.x) > math.abs(dir.z) then + if dir.x < 0 then + return 3 + else + return 1 + end + else + if dir.z < 0 then + return 2 + else + return 0 + end + end +end + +function minetest.dir_to_wallmounted(dir) + if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then + if dir.y < 0 then + return 1 + else + return 0 + end + elseif math.abs(dir.x) > math.abs(dir.z) then + if dir.x < 0 then + return 3 + else + return 2 + end + else + if dir.z < 0 then + return 5 + else + return 4 + end + end +end + +function minetest.get_node_drops(nodename, toolname) + local drop = ItemStack({name=nodename}):get_definition().drop + if drop == nil then + -- default drop + return {ItemStack({name=nodename})} + elseif type(drop) == "string" then + -- itemstring drop + return {ItemStack(drop)} + elseif drop.items == nil then + -- drop = {} to disable default drop + return {} + end + + -- Extended drop table + local got_items = {} + local got_count = 0 + local _, item, tool + for _, item in ipairs(drop.items) do + local good_rarity = true + local good_tool = true + if item.rarity ~= nil then + good_rarity = item.rarity < 1 or math.random(item.rarity) == 1 + end + if item.tools ~= nil then + good_tool = false + for _, tool in ipairs(item.tools) do + if tool:sub(1, 1) == '~' then + good_tool = toolname:find(tool:sub(2)) ~= nil + else + good_tool = toolname == tool + end + if good_tool then + break + end + end + end + if good_rarity and good_tool then + got_count = got_count + 1 + for _, add_item in ipairs(item.items) do + got_items[#got_items+1] = add_item + end + if drop.max_items ~= nil and got_count == drop.max_items then + break + end + end + end + return got_items +end + +function minetest.item_place_node(itemstack, placer, pointed_thing) + local item = itemstack:peek_item() + local def = itemstack:get_definition() + if def.type == "node" and pointed_thing.type == "node" then + local pos = pointed_thing.above + local oldnode = minetest.env:get_node(pos) + local olddef = ItemStack({name=oldnode.name}):get_definition() + + if not olddef.buildable_to then + minetest.log("info", placer:get_player_name() .. " tried to place" + .. " node in invalid position " .. minetest.pos_to_string(pos) + .. ", replacing " .. oldnode.name) + return + end + + minetest.log("action", placer:get_player_name() .. " places node " + .. def.name .. " at " .. minetest.pos_to_string(pos)) + + local newnode = {name = def.name, param1 = 0, param2 = 0} + + -- Calculate direction for wall mounted stuff like torches and signs + if def.paramtype2 == 'wallmounted' then + local under = pointed_thing.under + local above = pointed_thing.above + local dir = {x = under.x - above.x, y = under.y - above.y, z = under.z - above.z} + newnode.param2 = minetest.dir_to_wallmounted(dir) + -- Calculate the direction for furnaces and chests and stuff + elseif def.paramtype2 == 'facedir' then + local playerpos = placer:getpos() + local dir = {x = pos.x - playerpos.x, y = pos.y - playerpos.y, z = pos.z - playerpos.z} + newnode.param2 = minetest.dir_to_facedir(dir) + minetest.log("action", "facedir: " .. newnode.param2) + end + + -- Add node and update + minetest.env:add_node(pos, newnode) + + -- Set metadata owner + if def.metadata_name ~= "" then + minetest.env:get_meta(pos):set_owner(placer:get_player_name()) + end + + -- Run script hook + local _, callback + for _, callback in ipairs(minetest.registered_on_placenodes) do + callback(pos, newnode, placer) + end + + itemstack:take_item() + end + return itemstack +end + +function minetest.item_place_object(itemstack, placer, pointed_thing) + local pos = minetest.get_pointed_thing_position(pointed_thing, true) + if pos ~= nil then + local item = itemstack:take_item() + minetest.env:add_item(pos, item) + end + return itemstack +end + +function minetest.item_place(itemstack, placer, pointed_thing) + if itemstack:get_definition().type == "node" then + return minetest.item_place_node(itemstack, placer, pointed_thing) + else + return minetest.item_place_object(itemstack, placer, pointed_thing) + end +end + +function minetest.item_drop(itemstack, dropper, pos) + minetest.env:add_item(pos, itemstack) + return "" +end + +function minetest.item_eat(hp_change, replace_with_item) + return function(itemstack, user, pointed_thing) -- closure + if itemstack:take_item() ~= nil then + user:set_hp(user:get_hp() + hp_change) + itemstack:add_item(replace_with_item) -- note: replace_with_item is optional + end + return itemstack + end +end + +function minetest.node_punch(pos, node, puncher) + -- Run script hook + local _, callback + for _, callback in ipairs(minetest.registered_on_punchnodes) do + callback(pos, node, puncher) + end + +end + +function minetest.node_dig(pos, node, digger) + minetest.debug("node_dig") + + local def = ItemStack({name=node.name}):get_definition() + if not def.diggable then + minetest.debug("not diggable") + minetest.log("info", digger:get_player_name() .. " tried to dig " + .. node.name .. " which is not diggable " + .. minetest.pos_to_string(pos)) + return + end + + local meta = minetest.env:get_meta(pos) + if meta ~= nil and not meta:get_allow_removal() then + minetest.debug("dig prevented by metadata") + minetest.log("info", digger:get_player_name() .. " tried to dig " + .. node.name .. ", but removal is disabled by metadata " + .. minetest.pos_to_string(pos)) + return + end + + minetest.log('action', digger:get_player_name() .. " digs " + .. node.name .. " at " .. minetest.pos_to_string(pos)) + + if not minetest.setting_getbool("creative_mode") then + local wielded = digger:get_wielded_item() + local drops = minetest.get_node_drops(node.name, wielded:get_name()) + + -- Wear out tool + mp = def.material + tp = wielded:get_tool_digging_properties() + dp = minetest.get_digging_properties(mp, tp) + wielded:add_wear(dp.wear) + digger:set_wielded_item(wielded) + + -- Add dropped items + local _, dropped_item + for _, dropped_item in ipairs(drops) do + digger:get_inventory():add_item("main", dropped_item) + end + end + + -- Remove node and update + minetest.env:remove_node(pos) + + -- Run script hook + local _, callback + for _, callback in ipairs(minetest.registered_on_dignodes) do + callback(pos, node, digger) + end +end + +-- +-- Item definition 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, + usable = false, + liquids_pointable = false, + tool_digging_properties = nil, + + -- Interaction callbacks + on_place = minetest.item_place, + on_drop = minetest.item_drop, + on_use = nil, + + on_punch = minetest.node_punch, + on_dig = minetest.node_dig, + + -- 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}, @@ -96,6 +386,7 @@ minetest.register_nodedef_defaults({ alpha = 255, post_effect_color = {a=0, r=0, g=0, b=0}, paramtype = "none", + paramtype2 = "none", is_ground_content = false, sunlight_propagates = false, walkable = true, @@ -103,11 +394,6 @@ minetest.register_nodedef_defaults({ diggable = true, climbable = false, buildable_to = false, - wall_mounted = false, - often_contains_mineral = false, - dug_item = "", - extra_dug_item = "", - extra_dug_item_rarity = 2, metadata_name = "", liquidtype = "none", liquid_alternative_flowing = "", @@ -124,219 +410,354 @@ 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 -}) + legacy_facedir_simple = false, + legacy_wallmounted = false, +} -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 + local subname = name:sub(#expected_prefix+1) + if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then + error("Name " .. name .. " does not follow naming conventions: " .. + "contains unallowed characters") 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} + return name + end +end + +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 + 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 stackstring_put_item(stackstring, item) - if item == nil then - return stackstring, false +function minetest.register_item(name, itemdef) + -- Check name + if name == nil then + error("Unable to register item: 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)) + if forbidden_item_names[name] then + error("Unable to register item: Name is forbidden: " .. name) 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 + 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 + + -- Flowing liquid uses param2 + if itemdef.type == "node" and itemdef.liquidtype == "flowing" then + itemdef.paramtype2 = "flowingliquid" end - return stackstring, true -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') -end -test_stackstring() --- --- NodeItem helpers --- + -- BEGIN 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 + -- END Legacy stuff -minetest.inventorycube = function(img1, img2, img3) - img2 = img2 or img1 - img3 = img3 or img1 - return "[inventorycube" - .. "{" .. img1:gsub("%^", "&") - .. "{" .. img2:gsub("%^", "&") - .. "{" .. img3:gsub("%^", "&") + -- 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 --- --- CraftItem helpers --- +function minetest.register_node(name, nodedef) + nodedef.type = "node" + minetest.register_item(name, nodedef) +end + +function minetest.register_craftitem(name, craftitemdef) + craftitemdef.type = "craft" -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 -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 + -- BEGIN Legacy stuff + if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then + craftitemdef.inventory_image = craftitemdef.image end + -- END Legacy stuff + + minetest.register_item(name, craftitemdef) +end + +function minetest.register_tool(name, tooldef) + tooldef.type = "tool" + tooldef.stack_max = 1 + + -- BEGIN 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 + -- END Legacy stuff + + minetest.register_item(name, tooldef) +end + +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 +843,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 @@ -438,4 +859,10 @@ minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registr minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration() minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration() +-- +-- Set random seed +-- + +math.randomseed(os.time()) + -- END 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..f0e6b6dc2 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_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname" -- ^ location = eg. {type="player", name="celeron55"} -- {type="node", pos={x=, y=, z=}} +-- minetest.get_current_modname() -> string +-- minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname" -- --- 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 @@ -161,6 +169,8 @@ -- - get_meta(pos) -- Get a NodeMetaRef at that position -- - get_player_by_name(name) -- Get an ObjectRef to a player -- - get_objects_inside_radius(pos, radius) +-- - set_timeofday(val): val: 0...1; 0 = midnight, 0.5 = midday +-- - get_timeofday() -- -- NodeMetaRef (this stuff is subject to change in a future version) -- - get_type() @@ -168,11 +178,10 @@ -- - set_text(text) -- eg. set the text of a sign -- - get_text() -- - get_owner() +-- - set_owner(string) -- 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 +203,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 +226,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 +237,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 +305,38 @@ -- myvariable = whatever, -- } -- --- Tool definition: +-- Item definition options (register_node, register_craftitem, register_tool) -- { --- 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: +-- Node definition options (register_node): -- { --- name = "modname:somenode", +-- <all fields allowed in 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}, @@ -301,6 +344,7 @@ -- alpha = 255, -- post_effect_color = {a=0, r=0, g=0, b=0}, -- paramtype = "none", +-- paramtype2 = "none", -- is_ground_content = false, -- sunlight_propagates = false, -- walkable = true, @@ -308,11 +352,8 @@ -- diggable = true, -- climbable = false, -- buildable_to = false, --- wall_mounted = false, --- often_contains_mineral = false, --- dug_item = "", --- extra_dug_item = "", --- extra_dug_item_rarity = 2, +-- drop = "", +-- -- alternatively drop = { max_items = ..., items = { ... } } -- metadata_name = "", -- liquidtype = "none", -- liquid_alternative_flowing = "", @@ -329,34 +370,54 @@ -- cuttability = 0, -- flammability = 0, -- }, --- cookresult_itemstring = "", -- Cannot be cooked --- furnace_cooktime = 3.0, --- furnace_burntime = -1, -- Cannot be used as fuel +-- legacy_facedir_simple = false, -- Support maps made in and before January 2012 +-- legacy_wallmounted = false, -- Support maps made in and before January 2012 -- } -- --- Craftitem definition options: --- minetest.register_craftitem("modname_name", { --- image = "default_image.png", --- 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), --- }) --- -- 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 +443,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 +657,610 @@ 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', + drop = 'default:cobble', + legacy_mineral = true, +}) + +minetest.register_node("default:stone_with_coal", { + description = "Stone with coal", + tile_images = {"default_stone.png^default_mineral_coal.png"}, + is_ground_content = true, + material = minetest.digprop_stonelike(1.0), + drop = 'default:coal_lump', +}) + +minetest.register_node("default:stone_with_iron", { + description = "Stone with iron", + tile_images = {"default_stone.png^default_mineral_iron.png"}, + is_ground_content = true, + material = minetest.digprop_stonelike(1.0), + drop = 'default:iron_lump', }) 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', + drop = 'default:dirt', }) 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', + drop = 'default:dirt', }) 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? + drop = 'default:sand', }) 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', + drop = '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', + drop = '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, + drop = { + max_items = 1, + items = { + { + -- player will get sapling with 1/20 chance + items = {'default:sapling'}, + rarity = 20, + }, + { + -- player will get leaves only if he get no saplings, + -- this is because max_items is 1 + items = {'default:leaves'}, + } + } + }, }) 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 +1271,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,12 +1302,14 @@ minetest.register_node("default:rail", { }) minetest.register_node("default:ladder", { + description = "Ladder", drawtype = "signlike", tile_images = {"default_ladder.png"}, inventory_image = "default_ladder.png", + wield_image = "default_ladder.png", paramtype = "light", + paramtype2 = "wallmounted", is_ground_content = true, - wall_mounted = true, walkable = false, climbable = true, selection_box = { @@ -1027,37 +1318,36 @@ minetest.register_node("default:ladder", { --wall_bottom = = <default> --wall_side = = <default> }, - furnace_burntime = 5, material = minetest.digprop_woodlike(0.5), + legacy_wallmounted = true, }) 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 +1365,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 +1387,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 +1410,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,17 +1430,18 @@ 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", + paramtype2 = "wallmounted", sunlight_propagates = true, walkable = false, - wall_mounted = true, light_source = LIGHT_MAX-1, selection_box = { type = "wallmounted", @@ -1156,17 +1450,19 @@ 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, + legacy_wallmounted = true, }) 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", + paramtype2 = "wallmounted", sunlight_propagates = true, walkable = false, - wall_mounted = true, metadata_name = "sign", selection_box = { type = "wallmounted", @@ -1175,88 +1471,91 @@ minetest.register_node("default:sign_wall", { --wall_side = <default> }, material = minetest.digprop_constanttime(0.5), - furnace_burntime = 10, + legacy_wallmounted = true, }) 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", + paramtype2 = "facedir", metadata_name = "chest", material = minetest.digprop_woodlike(1.0), - furnace_burntime = 30, + legacy_facedir_simple = true, }) 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", + paramtype2 = "facedir", metadata_name = "locked_chest", material = minetest.digprop_woodlike(1.0), - furnace_burntime = 30, + legacy_facedir_simple = true, }) 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", + paramtype2 = "facedir", metadata_name = "furnace", material = minetest.digprop_stonelike(3.0), + legacy_facedir_simple = true, }) 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", + paramtype2 = "facedir", material = minetest.digprop_stonelike(3.0), - furnace_burntime = 1, + legacy_facedir_simple = true, }) 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 +1563,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 +1572,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 +1744,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 +1811,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 +1833,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/legacy/textures/mineral_coal.png b/data/mods/default/textures/default_mineral_coal.png Binary files differindex 3ff9692fb..3ff9692fb 100644 --- a/data/mods/legacy/textures/mineral_coal.png +++ b/data/mods/default/textures/default_mineral_coal.png diff --git a/data/mods/legacy/textures/mineral_iron.png b/data/mods/default/textures/default_mineral_iron.png Binary files differindex 51b15d95d..51b15d95d 100644 --- a/data/mods/legacy/textures/mineral_iron.png +++ b/data/mods/default/textures/default_mineral_iron.png diff --git a/data/mods/experimental/init.lua b/data/mods/experimental/init.lua index 2aae9b199..9a8f8868d 100644 --- a/data/mods/experimental/init.lua +++ b/data/mods/experimental/init.lua @@ -4,6 +4,8 @@ -- For testing random stuff +experimental = {} + function on_step(dtime) -- print("experimental on_step") --[[ @@ -20,11 +22,32 @@ function on_step(dtime) end end --]] + --[[ + if experimental.t1 == nil then + experimental.t1 = 0 + end + experimental.t1 = experimental.t1 + dtime + if experimental.t1 >= 2 then + experimental.t1 = experimental.t1 - 2 + minetest.log("time of day is "..minetest.env:get_timeofday()) + if experimental.day then + minetest.log("forcing day->night") + experimental.day = false + minetest.env:set_timeofday(0.0) + else + minetest.log("forcing night->day") + experimental.day = true + minetest.env:set_timeofday(0.5) + end + minetest.log("time of day is "..minetest.env:get_timeofday()) + end + --]] end 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 +79,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 +190,6 @@ minetest.register_abm({ inv:set_stack("fuel", 1, stack) end, }) ---[[ minetest.register_abm({ nodenames = {"experimental:luafurnace"}, interval = 1.0, @@ -231,7 +244,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 +252,7 @@ minetest.register_craft({ {'node "default:cobble"', 'node "default:cobble"', 'node "default:cobble"'}, } }) +--]] -- -- Random stuff @@ -261,38 +274,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'} } }) @@ -302,7 +293,7 @@ minetest.register_node("experimental:tnt", { "default_tnt_side.png", "default_tnt_side.png"}, inventory_image = minetest.inventorycube("default_tnt_top.png", "default_tnt_side.png", "default_tnt_side.png"), - dug_item = '', -- Get nothing + drop = '', -- Get nothing material = { diggability = "not", }, @@ -363,7 +354,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 +371,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 +538,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/give_initial_stuff/init.lua b/data/mods/give_initial_stuff/init.lua index e52784d64..9cf6b51b5 100644 --- a/data/mods/give_initial_stuff/init.lua +++ b/data/mods/give_initial_stuff/init.lua @@ -2,11 +2,11 @@ minetest.register_on_newplayer(function(player) print("on_newplayer") if minetest.setting_getbool("give_initial_stuff") then print("giving give_initial_stuff to player") - player:add_to_inventory('tool "SteelPick" 0') - player:add_to_inventory('node "torch" 99') - player:add_to_inventory('tool "SteelAxe" 0') - player:add_to_inventory('tool "SteelShovel" 0') - player:add_to_inventory('node "cobble" 99') + player:get_inventory():add_item('main', 'default:pick_steel') + player:get_inventory():add_item('main', 'default:torch 99') + player:get_inventory():add_item('main', 'default:axe_steel') + player:get_inventory():add_item('main', 'default:shovel_steel') + player:get_inventory():add_item('main', 'default:cobble 99') end end) diff --git a/data/mods/legacy/init.lua b/data/mods/legacy/init.lua index 3bd68f0bc..7f9088ce0 100644 --- a/data/mods/legacy/init.lua +++ b/data/mods/legacy/init.lua @@ -5,97 +5,128 @@ -- 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("stone_with_coal", "default:stone_with_coal") +minetest.register_alias("stone_with_iron", "default:stone_with_iron") +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 baf9fed5a..a736991b4 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 @@ -117,7 +116,6 @@ set(common_SRCS serverobject.cpp serverlinkableobject.cpp noise.cpp - mineral.cpp porting.cpp materials.cpp defaultsettings.cpp diff --git a/src/camera.cpp b/src/camera.cpp index c0e171468..b36daf1d7 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,10 +37,9 @@ 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), - m_viewing_range_max(5.0), m_camera_position(0,0,0), m_camera_direction(0,0,0), @@ -49,7 +48,6 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): m_fov_x(1.0), m_fov_y(1.0), - m_wanted_frametime(0.0), m_added_frametime(0), m_added_frames(0), m_range_old(0), @@ -77,15 +75,13 @@ 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); - - updateSettings(); + m_wieldnode = m_wieldmgr->addMeshSceneNode(createCubeMesh(v3f(1,1,1)), NULL); // need a dummy mesh } Camera::~Camera() { + m_wieldnode->setMesh(NULL); m_wieldmgr->drop(); - m_wieldnode->drop(); } bool Camera::successfullyCreated(std::wstring& error_message) @@ -258,14 +254,17 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize) // *100.0 helps in large map coordinates m_cameranode->setTarget(m_camera_position + 100 * m_camera_direction); - // FOV and and aspect ratio + // Get FOV setting + f32 fov_degrees = g_settings->getFloat("fov"); + fov_degrees = MYMAX(fov_degrees, 10.0); + fov_degrees = MYMIN(fov_degrees, 170.0); + + // FOV and aspect ratio m_aspect = (f32)screensize.X / (f32) screensize.Y; + m_fov_y = fov_degrees * PI / 180.0; m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y)); m_cameranode->setAspectRatio(m_aspect); m_cameranode->setFOV(m_fov_y); - // Just so big a value that everything rendered is visible - // Some more allowance that m_viewing_range_max * BS because of active objects etc. - m_cameranode->setFarValue(m_viewing_range_max * BS * 10); // Position the wielded item v3f wield_position = v3f(45, -35, 65); @@ -292,7 +291,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); @@ -342,7 +341,18 @@ void Camera::updateViewingRange(f32 frametime_in) <<m_draw_control.blocks_would_have_drawn <<std::endl;*/ - m_draw_control.wanted_min_range = m_viewing_range_min; + // Get current viewing range and FPS settings + f32 viewing_range_min = g_settings->getS16("viewing_range_nodes_min"); + viewing_range_min = MYMAX(5.0, viewing_range_min); + + f32 viewing_range_max = g_settings->getS16("viewing_range_nodes_max"); + viewing_range_max = MYMAX(viewing_range_min, viewing_range_max); + + f32 wanted_fps = g_settings->getFloat("wanted_fps"); + wanted_fps = MYMAX(wanted_fps, 1.0); + f32 wanted_frametime = 1.0 / wanted_fps; + + m_draw_control.wanted_min_range = viewing_range_min; m_draw_control.wanted_max_blocks = (2.0*m_draw_control.blocks_would_have_drawn)+1; if (m_draw_control.wanted_max_blocks < 10) m_draw_control.wanted_max_blocks = 10; @@ -361,13 +371,13 @@ void Camera::updateViewingRange(f32 frametime_in) m_added_frametime = 0.0; m_added_frames = 0; - f32 wanted_frametime_change = m_wanted_frametime - frametime; + f32 wanted_frametime_change = wanted_frametime - frametime; //dstream<<"wanted_frametime_change="<<wanted_frametime_change<<std::endl; // If needed frametime change is small, just return // This value was 0.4 for many months until 2011-10-18 by c55; // Let's see how this works out. - if (fabs(wanted_frametime_change) < m_wanted_frametime*0.33) + if (fabs(wanted_frametime_change) < wanted_frametime*0.33) { //dstream<<"ignoring small wanted_frametime_change"<<std::endl; return; @@ -420,8 +430,8 @@ void Camera::updateViewingRange(f32 frametime_in) new_range += wanted_range_change; //f32 new_range_unclamped = new_range; - new_range = MYMAX(new_range, m_viewing_range_min); - new_range = MYMIN(new_range, m_viewing_range_max); + new_range = MYMAX(new_range, viewing_range_min); + new_range = MYMIN(new_range, viewing_range_max); /*dstream<<"new_range="<<new_range_unclamped <<", clamped to "<<new_range<<std::endl;*/ @@ -429,82 +439,45 @@ void Camera::updateViewingRange(f32 frametime_in) m_range_old = new_range; m_frametime_old = frametime; -} -void Camera::updateSettings() -{ - m_viewing_range_min = g_settings->getS16("viewing_range_nodes_min"); - m_viewing_range_min = MYMAX(5.0, m_viewing_range_min); - - m_viewing_range_max = g_settings->getS16("viewing_range_nodes_max"); - m_viewing_range_max = MYMAX(m_viewing_range_min, m_viewing_range_max); - - f32 fov_degrees = g_settings->getFloat("fov"); - fov_degrees = MYMAX(fov_degrees, 10.0); - fov_degrees = MYMIN(fov_degrees, 170.0); - m_fov_y = fov_degrees * PI / 180.0; + // Just so big a value that everything rendered is visible + // Some more allowance than viewing_range_max * BS because of active objects etc. + m_cameranode->setFarValue(viewing_range_max * BS * 10); - f32 wanted_fps = g_settings->getFloat("wanted_fps"); - wanted_fps = MYMAX(wanted_fps, 1.0); - 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 +485,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..7be8162b5 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; /* @@ -113,16 +110,13 @@ public: // Render distance feedback loop void updateViewingRange(f32 frametime_in); - // 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,16 +130,12 @@ 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; - // viewing_range_min_nodes setting - f32 m_viewing_range_min; - // viewing_range_max_nodes setting - f32 m_viewing_range_max; - // Absolute camera position v3f m_camera_position; // Absolute camera direction @@ -157,7 +147,6 @@ private: f32 m_fov_y; // Stuff for viewing range calculations - f32 m_wanted_frametime; f32 m_added_frametime; s16 m_added_frames; f32 m_range_old; @@ -182,46 +171,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..0463aa81c 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, @@ -228,15 +225,16 @@ Client::Client( m_server_ser_ver(SER_FMT_VER_INVALID), m_playeritem(0), m_inventory_updated(false), + m_inventory_from_server(NULL), + m_inventory_from_server_age(0.0), m_time_of_day(0), m_map_seed(0), m_password(password), 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 +249,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 +258,6 @@ Client::Client( player->updateName(playername); m_env.addPlayer(player); - - // Initialize player in the inventory context - m_inventory_context.current_player = player; } } @@ -282,6 +271,8 @@ Client::~Client() m_mesh_update_thread.setRun(false); while(m_mesh_update_thread.IsRunning()) sleep_ms(100); + + delete m_inventory_from_server; } void Client::connect(Address address) @@ -670,6 +661,30 @@ void Client::step(float dtime) } } } + + /* + If the server didn't update the inventory in a while, revert + the local inventory (so the player notices the lag problem + and knows something is wrong). + */ + if(m_inventory_from_server) + { + float interval = 10.0; + float count_before = floor(m_inventory_from_server_age / interval); + + m_inventory_from_server_age += dtime; + + float count_after = floor(m_inventory_from_server_age / interval); + + if(count_after != count_before) + { + // Do this every <interval> seconds after TOCLIENT_INVENTORY + // Reset the locally changed inventory to the authoritative inventory + Player *player = m_env.getLocalPlayer(); + player->inventory = *m_inventory_from_server; + m_inventory_updated = true; + } + } } // Virtual methods from con::PeerHandler @@ -912,7 +927,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) Update an existing block */ //infostream<<"Updating"<<std::endl; - block->deSerialize(istr, ser_version); + block->deSerialize(istr, ser_version, false); } else { @@ -921,7 +936,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) */ //infostream<<"Creating new"<<std::endl; block = new MapBlock(&m_env.getMap(), p, this); - block->deSerialize(istr, ser_version); + block->deSerialize(istr, ser_version, false); sector->insertBlock(block); } @@ -983,11 +998,15 @@ 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; + delete m_inventory_from_server; + m_inventory_from_server = new Inventory(player->inventory); + m_inventory_from_server_age = 0.0; + //infostream<<"Client got player inventory:"<<std::endl; //player->inventory.print(infostream); } @@ -1216,18 +1235,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 +1275,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 +1376,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 +1423,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 +1491,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 +1498,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 { @@ -1886,8 +1848,7 @@ void Client::addNode(v3s16 p, MapNode n) try { //TimeTaker timer3("Client::addNode(): addNodeAndUpdate"); - std::string st = std::string(""); - m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, st); + m_env.getMap().addNodeAndUpdate(p, n, modified_blocks); } catch(InvalidPositionException &e) {} @@ -1943,11 +1904,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 +1927,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,39 +1961,17 @@ 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) { + /* + Send it to the server + */ sendInventoryAction(a); + + /* + Predict some local inventory changes + */ + a->clientApply(this, this); } ClientActiveObject * Client::getSelectedActiveObject( @@ -2234,6 +2170,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 +2207,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 +2220,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..efdf315f7 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; @@ -374,6 +367,8 @@ private: u8 m_server_ser_ver; u16 m_playeritem; bool m_inventory_updated; + Inventory *m_inventory_from_server; + float m_inventory_from_server_age; core::map<v3s16, bool> m_active_blocks; PacketCounter m_packetcounter; // Received from the server. 0-23999 @@ -387,13 +382,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 83f1cedff..37da0f67d 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..dc1e1daed 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(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]), + video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]), + video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]), + video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]), + // front + video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]), + video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]), + video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]), + video::S3DVertex(min.X,min.Y,min.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 @@ -640,7 +602,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, break; case NDT_TORCHLIKE: { - v3s16 dir = unpackDir(n.param2); + v3s16 dir = n.getWallMountedDir(nodedef); AtlasPointer ap(0); if(dir == v3s16(0,-1,0)){ @@ -732,7 +694,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, ap.x0(), ap.y1()), }; - v3s16 dir = unpackDir(n.param2); + v3s16 dir = n.getWallMountedDir(nodedef); for(s32 i=0; i<4; i++) { @@ -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..ce7ee593f 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -27,80 +27,11 @@ 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 126 and 127 are reserved (CONTENT_AIR and CONTENT_IGNORE). - 0x800...0xfff (2048...4095): higher 4 bytes of param2 are not usable + 0x800...0xfff (2048...4095): higher 4 bits of param2 are not usable */ #define CONTENT_STONE 0 #define CONTENT_WATER 2 @@ -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..b36d57c89 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() { @@ -430,6 +441,10 @@ bool FurnaceNodeMetadata::step(float dtime) { if(dtime > 60.0) infostream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl; + + InventoryList *dst_list = m_inventory->getList("dst"); + assert(dst_list); + // Update at a fixed frequency const float interval = 2.0; m_step_accumulator += dtime; @@ -440,86 +455,110 @@ 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); - + bool changed_this_loop = false; + + // 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_this_loop = 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_this_loop = true; + m_src_time += dtime; + m_src_totaltime = cooktime; + infotext = "Furnace is cooking"; + } + else if(getBurnResult(true, burntime)) + { + // Fuel inserted + changed_this_loop = 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_this_loop = 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; + } } - - //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); + // 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 += "%)"; + } - if(fuel_item && fuel_item->getBurnTime() >= 0){ - m_fuel_totaltime = fuel_item->getBurnTime(); + if(infotext != m_infotext) + { + m_infotext = infotext; + changed_this_loop = true; + } + + if(burning && m_fuel_time >= m_fuel_totaltime) + { m_fuel_time = 0; - fuel_list->decrementMaterials(1); + m_fuel_totaltime = 0; + } + + if(changed_this_loop) + { changed = true; - } else { - //infostream<<"No fuel found"<<std::endl; - // No fuel, stop loop. + } + else + { m_step_accumulator = 0; break; } @@ -535,6 +574,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 +647,7 @@ public: GenericNodeMetadata(IGameDef *gamedef): NodeMetadata(gamedef), - m_inventory(new Inventory()), + m_inventory(NULL), m_text(""), m_owner(""), @@ -594,7 +670,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 +686,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 b8189b258..4a2bef872 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" /* Some helper functions */ @@ -112,9 +112,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) { @@ -132,10 +133,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) @@ -173,17 +174,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); @@ -193,19 +200,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(); } @@ -213,42 +213,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; + } + } } /* @@ -434,14 +450,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; + } } /* @@ -701,14 +727,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) @@ -1391,14 +1423,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/defaultsettings.cpp b/src/defaultsettings.cpp index 1e48183fa..2b6cb7f53 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -43,14 +43,25 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_rangeselect", "KEY_KEY_R"); settings->setDefault("keymap_freemove", "KEY_KEY_K"); settings->setDefault("keymap_fastmove", "KEY_KEY_J"); - settings->setDefault("keymap_frametime_graph", "KEY_F1"); settings->setDefault("keymap_screenshot", "KEY_F12"); - settings->setDefault("keymap_toggle_profiler", "KEY_F2"); + settings->setDefault("keymap_toggle_hud", "KEY_F1"); + settings->setDefault("keymap_toggle_chat", "KEY_F2"); settings->setDefault("keymap_toggle_force_fog_off", "KEY_F3"); settings->setDefault("keymap_toggle_update_camera", "KEY_F4"); + settings->setDefault("keymap_toggle_debug", "KEY_F5"); + settings->setDefault("keymap_toggle_profiler", "KEY_F6"); + settings->setDefault("keymap_increase_viewing_range_min", "KEY_PRIOR"); + settings->setDefault("keymap_decrease_viewing_range_min", "KEY_NEXT"); // Some (temporary) keys for debugging settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); + // Show debug info by default? + #ifdef NDEBUG + settings->setDefault("show_debug", "false"); + #else + settings->setDefault("show_debug", "true"); + #endif + settings->setDefault("wanted_fps", "30"); settings->setDefault("fps_max", "60"); settings->setDefault("viewing_range_nodes_max", "300"); @@ -66,7 +77,6 @@ void set_default_settings(Settings *settings) settings->setDefault("new_style_water", "false"); settings->setDefault("new_style_leaves", "false"); settings->setDefault("smooth_lighting", "true"); - settings->setDefault("frametime_graph", "false"); settings->setDefault("enable_texture_atlas", "true"); settings->setDefault("texture_path", ""); settings->setDefault("video_driver", "opengl"); @@ -84,7 +94,7 @@ void set_default_settings(Settings *settings) // Server stuff // "map-dir" doesn't exist by default. settings->setDefault("motd", ""); - settings->setDefault("max_users", "20"); + settings->setDefault("max_users", "100"); settings->setDefault("strict_protocol_version_checking", "true"); settings->setDefault("creative_mode", "false"); settings->setDefault("enable_damage", "true"); diff --git a/src/environment.cpp b/src/environment.cpp index a3d0950f0..d332e5d53 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2072,7 +2072,9 @@ void ClientEnvironment::step(float dtime) MapNode n = m_map->getNode(p); light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); } - catch(InvalidPositionException &e) {} + catch(InvalidPositionException &e){ + light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); + } player->updateLight(light); /* @@ -2119,7 +2121,6 @@ void ClientEnvironment::step(float dtime) if(m_active_object_light_update_interval.step(dtime, 0.21)) { // Update lighting - //u8 light = LIGHT_MAX; u8 light = 0; try{ // Get node at head @@ -2127,7 +2128,9 @@ void ClientEnvironment::step(float dtime) MapNode n = m_map->getNode(p); light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); } - catch(InvalidPositionException &e) {} + catch(InvalidPositionException &e){ + light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); + } obj->updateLight(light); } } @@ -2218,7 +2221,9 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) MapNode n = m_map->getNode(p); light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); } - catch(InvalidPositionException &e) {} + catch(InvalidPositionException &e){ + light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); + } object->updateLight(light); } return object->getId(); diff --git a/src/environment.h b/src/environment.h index dd8390c39..2391025ba 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..a1f0fe07f 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); @@ -316,6 +303,8 @@ PointedThing getPointedThing(Client *client, v3f player_position, should_show_hilightbox = false; selected_object = NULL; + INodeDefManager *nodedef = client->getNodeDefManager(); + // First try to find a pointed at active object if(look_for_object) { @@ -391,7 +380,7 @@ PointedThing getPointedThing(Client *client, v3f player_position, v3s16(-1,0,0), // left }; - const ContentFeatures &f = client->getNodeDefManager()->get(n); + const ContentFeatures &f = nodedef->get(n); if(f.selection_box.type == NODEBOX_FIXED) { @@ -460,7 +449,7 @@ PointedThing getPointedThing(Client *client, v3f player_position, } else if(f.selection_box.type == NODEBOX_WALLMOUNTED) { - v3s16 dir = unpackDir(n.param2); + v3s16 dir = n.getWallMountedDir(nodedef); v3f dir_f = v3f(dir.X, dir.Y, dir.Z); dir_f *= BS/2 - BS/6 - BS/20; v3f cpf = npf + dir_f; @@ -645,6 +634,36 @@ void draw_load_screen(const std::wstring &text, //return guitext; } +/* Profiler display */ + +void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, + gui::IGUIFont *font, u32 text_height, + u32 show_profiler, u32 show_profiler_max) +{ + if(show_profiler == 0) + { + guitext_profiler->setVisible(false); + } + else + { + + std::ostringstream os(std::ios_base::binary); + g_profiler->printPage(os, show_profiler, show_profiler_max); + std::wstring text = narrow_to_wide(os.str()); + guitext_profiler->setText(text.c_str()); + guitext_profiler->setVisible(true); + + s32 w = font->getDimension(text.c_str()).Width; + if(w < 400) + w = 400; + core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w, + 8+(text_height+5)*2 + + font->getDimension(text.c_str()).Height); + guitext_profiler->setRelativePosition(rect); + guitext_profiler->setVisible(true); + } +} + void the_game( bool &kill, bool random_input, @@ -691,12 +710,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 +742,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 +852,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 +869,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 +886,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 +931,11 @@ void the_game( } /* + A copy of the local inventory + */ + Inventory local_inventory(itemdef); + + /* Move into game */ @@ -937,6 +962,16 @@ void the_game( core::rect<s32>(0,0,400,text_height+5) + v2s32(100,200), false, false); + // Status text (displays info when showing and hiding GUI stuff, etc.) + gui::IGUIStaticText *guitext_status = guienv->addStaticText( + L"<Status>", + core::rect<s32>(0,0,0,0), + false, false); + guitext_status->setVisible(false); + + std::wstring statustext; + float statustext_time = 0; + // Chat text gui::IGUIStaticText *guitext_chat = guienv->addStaticText( L"", @@ -949,8 +984,7 @@ void the_game( // Profiler text (size is updated when text is updated) gui::IGUIStaticText *guitext_profiler = guienv->addStaticText( L"<Profiler>", - core::rect<s32>(6, 4+(text_height+5)*2, 400, - (text_height+5)*2 + text_height*35), + core::rect<s32>(0,0,0,0), false, false); guitext_profiler->setBackgroundColor(video::SColor(80,0,0,0)); guitext_profiler->setVisible(false); @@ -973,11 +1007,6 @@ void the_game( (new GUIPauseMenu(guienv, guiroot, -1, g_gamecallback, &g_menumgr))->drop(); - // Enable texts - /*guitext2->setVisible(true); - guitext_info->setVisible(true); - guitext_chat->setVisible(true);*/ - //s32 guitext_chat_pad_bottom = 70; /* @@ -1013,9 +1042,14 @@ void the_game( bool respawn_menu_active = false; bool update_wielded_item_trigger = false; - bool show_profiler = false; + bool show_hud = true; + bool show_chat = true; bool force_fog_off = false; bool disable_camera_update = false; + bool show_debug = g_settings->getBool("show_debug"); + bool show_debug_frametime = false; + u32 show_profiler = 0; + u32 show_profiler_max = 3; // Number of pages /* Main loop @@ -1252,20 +1286,10 @@ void the_game( g_profiler->print(infostream); } - std::ostringstream os(std::ios_base::binary); - g_profiler->print(os); - std::wstring text = narrow_to_wide(os.str()); - guitext_profiler->setText(text.c_str()); + update_profiler_gui(guitext_profiler, font, text_height, + show_profiler, show_profiler_max); g_profiler->clear(); - - s32 w = font->getDimension(text.c_str()).Width; - if(w < 400) - w = 400; - core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w, - 8+(text_height+5)*2 + - font->getDimension(text.c_str()).Height); - guitext_profiler->setRelativePosition(rect); } /* @@ -1289,7 +1313,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 +1326,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, "craftpreview", v2s32(7, 1), v2s32(1, 1))); menu->setDrawSpec(draw_spec); @@ -1352,12 +1378,14 @@ void the_game( if(g_settings->getBool("free_move")) { g_settings->set("free_move","false"); - chat_lines.push_back(ChatLine(L"free_move disabled")); + statustext = L"free_move disabled"; + statustext_time = 0; } else { g_settings->set("free_move","true"); - chat_lines.push_back(ChatLine(L"free_move enabled")); + statustext = L"free_move enabled"; + statustext_time = 0; } } else if(input->wasKeyDown(getKeySetting("keymap_fastmove"))) @@ -1365,25 +1393,14 @@ void the_game( if(g_settings->getBool("fast_move")) { g_settings->set("fast_move","false"); - chat_lines.push_back(ChatLine(L"fast_move disabled")); + statustext = L"fast_move disabled"; + statustext_time = 0; } else { g_settings->set("fast_move","true"); - chat_lines.push_back(ChatLine(L"fast_move enabled")); - } - } - else if(input->wasKeyDown(getKeySetting("keymap_frametime_graph"))) - { - if(g_settings->getBool("frametime_graph")) - { - g_settings->set("frametime_graph","false"); - chat_lines.push_back(ChatLine(L"frametime_graph disabled")); - } - else - { - g_settings->set("frametime_graph","true"); - chat_lines.push_back(ChatLine(L"frametime_graph enabled")); + statustext = L"fast_move enabled"; + statustext_time = 0; } } else if(input->wasKeyDown(getKeySetting("keymap_screenshot"))) @@ -1398,37 +1415,120 @@ void the_game( std::wstringstream sstr; sstr<<"Saved screenshot to '"<<filename<<"'"; infostream<<"Saved screenshot to '"<<filename<<"'"<<std::endl; - chat_lines.push_back(ChatLine(sstr.str())); + statustext = sstr.str(); + statustext_time = 0; } else{ infostream<<"Failed to save screenshot '"<<filename<<"'"<<std::endl; } image->drop(); } } - else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler"))) + else if(input->wasKeyDown(getKeySetting("keymap_toggle_hud"))) + { + show_hud = !show_hud; + if(show_hud) + statustext = L"HUD shown"; + else + statustext = L"HUD hidden"; + statustext_time = 0; + } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_chat"))) { - show_profiler = !show_profiler; - guitext_profiler->setVisible(show_profiler); - if(show_profiler) - chat_lines.push_back(ChatLine(L"Profiler disabled")); + show_chat = !show_chat; + if(show_chat) + statustext = L"Chat shown"; else - chat_lines.push_back(ChatLine(L"Profiler enabled")); + statustext = L"Chat hidden"; + statustext_time = 0; } else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off"))) { force_fog_off = !force_fog_off; if(force_fog_off) - chat_lines.push_back(ChatLine(L"Fog disabled")); + statustext = L"Fog disabled"; else - chat_lines.push_back(ChatLine(L"Fog enabled")); + statustext = L"Fog enabled"; + statustext_time = 0; } else if(input->wasKeyDown(getKeySetting("keymap_toggle_update_camera"))) { disable_camera_update = !disable_camera_update; if(disable_camera_update) - chat_lines.push_back(ChatLine(L"Camera update disabled")); + statustext = L"Camera update disabled"; + else + statustext = L"Camera update enabled"; + statustext_time = 0; + } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_debug"))) + { + // Initial / 3x toggle: Chat only + // 1x toggle: Debug text with chat + // 2x toggle: Debug text with frametime + if(!show_debug) + { + show_debug = true; + show_debug_frametime = false; + statustext = L"Debug info shown"; + statustext_time = 0; + } + else if(show_debug_frametime) + { + show_debug = false; + show_debug_frametime = false; + statustext = L"Debug info and frametime graph hidden"; + statustext_time = 0; + } + else + { + show_debug_frametime = true; + statustext = L"Frametime graph shown"; + statustext_time = 0; + } + } + else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler"))) + { + show_profiler = (show_profiler + 1) % (show_profiler_max + 1); + + // FIXME: This updates the profiler with incomplete values + update_profiler_gui(guitext_profiler, font, text_height, + show_profiler, show_profiler_max); + + if(show_profiler != 0) + { + std::wstringstream sstr; + sstr<<"Profiler shown (page "<<show_profiler + <<" of "<<show_profiler_max<<")"; + statustext = sstr.str(); + statustext_time = 0; + } else - chat_lines.push_back(ChatLine(L"Camera update enabled")); + { + statustext = L"Profiler hidden"; + statustext_time = 0; + } + } + else if(input->wasKeyDown(getKeySetting("keymap_increase_viewing_range_min"))) + { + s16 range = g_settings->getS16("viewing_range_nodes_min"); + s16 range_new = range + 10; + g_settings->set("viewing_range_nodes_min", itos(range_new)); + statustext = narrow_to_wide( + "Minimum viewing range changed to " + + itos(range_new)); + statustext_time = 0; + } + else if(input->wasKeyDown(getKeySetting("keymap_decrease_viewing_range_min"))) + { + s16 range = g_settings->getS16("viewing_range_nodes_min"); + s16 range_new = range - 10; + if(range_new < 0) + range_new = range; + g_settings->set("viewing_range_nodes_min", + itos(range_new)); + statustext = narrow_to_wide( + "Minimum viewing range changed to " + + itos(range_new)); + statustext_time = 0; } // Item selection with mouse wheel @@ -1473,15 +1573,18 @@ void the_game( // Viewing range selection if(input->wasKeyDown(getKeySetting("keymap_rangeselect"))) { + draw_control.range_all = !draw_control.range_all; if(draw_control.range_all) { - draw_control.range_all = false; - infostream<<"Disabled full viewing range"<<std::endl; + infostream<<"Enabled full viewing range"<<std::endl; + statustext = L"Enabled full viewing range"; + statustext_time = 0; } else { - draw_control.range_all = true; - infostream<<"Enabled full viewing range"<<std::endl; + infostream<<"Disabled full viewing range"<<std::endl; + statustext = L"Disabled full viewing range"; + statustext_time = 0; } } @@ -1691,31 +1794,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; } } @@ -1843,19 +1935,15 @@ void the_game( MapNode n = client.getNode(nodepos); // Get digging properties for material and tool - content_t material = n.getContent(); + MaterialProperties mp = nodedef->get(n.getContent()).material; ToolDiggingProperties tp = - tooldef->getDiggingProperties(playeritem_toolname); - DiggingProperties prop = - getDiggingProperties(material, &tp, nodedef); + playeritem.getToolDiggingProperties(itemdef); + DiggingProperties prop = getDiggingProperties(&mp, &tp); float dig_time_complete = 0.0; 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 +2010,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 +2024,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 +2082,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); } @@ -2130,55 +2211,110 @@ void the_game( //TimeTaker guiupdatetimer("Gui updating"); + const char program_name_and_version[] = + "Minetest-c55 " VERSION_STRING; + + if(show_debug) { static float drawtime_avg = 0; drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05; - static float beginscenetime_avg = 0; + /*static float beginscenetime_avg = 0; beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05; static float scenetime_avg = 0; scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05; static float endscenetime_avg = 0; - endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05; + endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;*/ char temptext[300]; - snprintf(temptext, 300, "Minetest-c55 %s (" + snprintf(temptext, 300, "%s (" "R: range_all=%i" ")" - " drawtime=%.0f, beginscenetime=%.0f" - ", scenetime=%.0f, endscenetime=%.0f", - VERSION_STRING, + " drawtime=%.0f, dtime_jitter = % .1f %%" + ", v_range = %.1f, RTT = %.3f", + program_name_and_version, draw_control.range_all, drawtime_avg, - beginscenetime_avg, - scenetime_avg, - endscenetime_avg + dtime_jitter1_max_fraction * 100.0, + draw_control.wanted_range, + client.getRTT() ); guitext->setText(narrow_to_wide(temptext).c_str()); + guitext->setVisible(true); + } + else if(show_hud || show_chat) + { + guitext->setText(narrow_to_wide(program_name_and_version).c_str()); + guitext->setVisible(true); + } + else + { + guitext->setVisible(false); } + if(show_debug) { char temptext[300]; snprintf(temptext, 300, "(% .1f, % .1f, % .1f)" - " (% .3f < btime_jitter < % .3f" - ", dtime_jitter = % .1f %%" - ", v_range = %.1f, RTT = %.3f)", + " (yaw = %.1f)", player_position.X/BS, player_position.Y/BS, player_position.Z/BS, - busytime_jitter1_min_sample, - busytime_jitter1_max_sample, - dtime_jitter1_max_fraction * 100.0, - draw_control.wanted_range, - client.getRTT() - ); + wrapDegrees_0_360(camera_yaw)); guitext2->setText(narrow_to_wide(temptext).c_str()); + guitext2->setVisible(true); + } + else + { + guitext2->setVisible(false); } { guitext_info->setText(infotext.c_str()); + guitext_info->setVisible(show_hud); + } + + { + float statustext_time_max = 3.0; + if(!statustext.empty()) + { + statustext_time += dtime; + if(statustext_time >= statustext_time_max) + { + statustext = L""; + statustext_time = 0; + } + } + guitext_status->setText(statustext.c_str()); + guitext_status->setVisible(!statustext.empty()); + + if(!statustext.empty()) + { + s32 status_y = screensize.Y - 130; + core::rect<s32> rect( + 10, + status_y - guitext_status->getTextHeight(), + screensize.X - 10, + status_y + ); + guitext_status->setRelativePosition(rect); + + // Fade out + video::SColor initial_color(255,0,0,0); + if(guienv->getSkin()) + initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT); + video::SColor final_color = initial_color; + final_color.setAlpha(0); + video::SColor fade_color = + initial_color.getInterpolated_quadratic( + initial_color, + final_color, + statustext_time / (float) statustext_time_max); + guitext_status->setOverrideColor(fade_color); + guitext_status->enableOverrideColor(true); + } } /* @@ -2249,20 +2385,22 @@ void the_game( screensize.X - 10, screensize.Y - guitext_chat_pad_bottom );*/ + + s32 chat_y = 5+(text_height+5); + if(show_debug) + chat_y += (text_height+5); core::rect<s32> rect( 10, - 50, + chat_y, screensize.X - 10, - 50 + guitext_chat->getTextHeight() + chat_y + guitext_chat->getTextHeight() ); guitext_chat->setRelativePosition(rect); - // Don't show chat if empty or profiler is enabled - if(chat_lines.size() == 0 || show_profiler) - guitext_chat->setVisible(false); - else - guitext_chat->setVisible(true); + // Don't show chat if empty or profiler or debug is enabled + guitext_chat->setVisible(chat_lines.size() != 0 + && show_chat && show_profiler == 0); } /* @@ -2285,25 +2423,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 */ @@ -2312,7 +2438,7 @@ void the_game( { TimeTaker timer("beginScene"); - driver->beginScene(true, true, bgcolor); + driver->beginScene(false, true, bgcolor); //driver->beginScene(false, true, bgcolor); beginscenetime = timer.stop(true); } @@ -2342,20 +2468,24 @@ void the_game( driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); - for(core::list< core::aabbox3d<f32> >::Iterator i=hilightboxes.begin(); - i != hilightboxes.end(); i++) + if(show_hud) { - /*infostream<<"hilightbox min=" - <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")" - <<" max=" - <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")" - <<std::endl;*/ - driver->draw3DBox(*i, video::SColor(255,0,0,0)); + for(core::list<aabb3f>::Iterator i=hilightboxes.begin(); + i != hilightboxes.end(); i++) + { + /*infostream<<"hilightbox min=" + <<"("<<i->MinEdge.X<<","<<i->MinEdge.Y<<","<<i->MinEdge.Z<<")" + <<" max=" + <<"("<<i->MaxEdge.X<<","<<i->MaxEdge.Y<<","<<i->MaxEdge.Z<<")" + <<std::endl;*/ + driver->draw3DBox(*i, video::SColor(255,0,0,0)); + } } /* Wielded tool */ + if(show_hud) { // Warning: This clears the Z buffer. camera.drawWieldedTool(); @@ -2371,16 +2501,17 @@ void the_game( /* Frametime log */ - if(g_settings->getBool("frametime_graph") == true) + if(show_debug_frametime) { s32 x = 10; + s32 y = screensize.Y - 10; for(core::list<float>::Iterator i = frametime_log.begin(); i != frametime_log.end(); i++) { - driver->draw2DLine(v2s32(x,50), - v2s32(x,50+(*i)*1000), + driver->draw2DLine(v2s32(x,y), + v2s32(x,y-(*i)*1000), video::SColor(255,255,255,255)); x++; } @@ -2389,12 +2520,15 @@ void the_game( /* Draw crosshair */ - driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0), - displaycenter + core::vector2d<s32>(10,0), - video::SColor(255,255,255,255)); - driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10), - displaycenter + core::vector2d<s32>(0,10), - video::SColor(255,255,255,255)); + if(show_hud) + { + driver->draw2DLine(displaycenter - core::vector2d<s32>(10,0), + displaycenter + core::vector2d<s32>(10,0), + video::SColor(255,255,255,255)); + driver->draw2DLine(displaycenter - core::vector2d<s32>(0,10), + displaycenter + core::vector2d<s32>(0,10), + video::SColor(255,255,255,255)); + } } // timer @@ -2410,8 +2544,9 @@ void the_game( /* Draw hotbar */ + if(show_hud) { - 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 +2617,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..10ab0b0bc 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -21,12 +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 +38,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 +50,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..f33460906 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,66 @@ 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; + int progressmid = + 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 = MYMIN(floor(wear * 600), 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); + + core::rect<s32> progressrect2 = progressrect; + progressrect2.LowerRightCorner.X = progressmid; + driver->draw2DRectangle(color, progressrect2, clip); + + color = video::SColor(255,0,0,0); + progressrect2 = progressrect; + progressrect2.UpperLeftCorner.X = progressmid; + driver->draw2DRectangle(color, progressrect2, 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,17 +124,18 @@ 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; + m_selected_amount = 0; + m_selected_dragging = false; + m_tooltip_element = NULL; } GUIInventoryMenu::~GUIInventoryMenu() @@ -131,6 +166,11 @@ void GUIInventoryMenu::removeChildren() if(e != NULL) e->remove(); }*/ + if(m_tooltip_element) + { + m_tooltip_element->remove(); + m_tooltip_element = NULL; + } } void GUIInventoryMenu::regenerateGui(v2u32 screensize) @@ -195,6 +235,17 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize) const wchar_t *text = L"Left click: Move all items, Right click: Move single item"; Environment->addStaticText(text, rect, false, true, this, 256); + + // Add tooltip + // Note: parent != this so that the tooltip isn't clipped by the menu rectangle + m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18)); + m_tooltip_element->enableOverrideColor(true); + m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60)); + m_tooltip_element->setDrawBackground(true); + m_tooltip_element->setDrawBorder(true); + m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255)); + m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); + m_tooltip_element->setWordWrap(false); } } @@ -214,15 +265,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, int phase) { video::IVideoDriver* driver = Environment->getVideoDriver(); @@ -232,7 +283,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,51 +295,93 @@ 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); - if(m_selected_item != NULL && m_selected_item->listname == s.listname - && m_selected_item->i == i) - { - /*s32 border = imgsize.X/12; - driver->draw2DRectangle(video::SColor(255,192,192,192), - core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*border, - rect.LowerRightCorner + v2s32(1,1)*border), - NULL); - driver->draw2DRectangle(video::SColor(255,0,0,0), - core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*((border+1)/2), - rect.LowerRightCorner + v2s32(1,1)*((border+1)/2)), - NULL);*/ - s32 border = 2; - driver->draw2DRectangle(video::SColor(255,255,0,0), - core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*border, - rect.LowerRightCorner + v2s32(1,1)*border), - &AbsoluteClippingRect); - } - - if(rect.isPointInside(m_pointer) && m_selected_item) - { - video::SColor bgcolor(255,192,192,192); - driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); - } - else + bool selected = m_selected_item + && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv + && m_selected_item->listname == s.listname + && m_selected_item->i == i; + bool hovering = rect.isPointInside(m_pointer); + + if(phase == 0) { - video::SColor bgcolor(255,128,128,128); - driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); + if(hovering && m_selected_item) + { + video::SColor bgcolor(255,192,192,192); + driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); + } + else + { + video::SColor bgcolor(255,128,128,128); + driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); + } } - if(item) + if(phase == 1) { - drawInventoryItem(driver, font, item, - rect, &AbsoluteClippingRect, tsrc); - } + // Draw item stack + if(selected) + { + item.takeItem(m_selected_amount); + } + if(!item.empty()) + { + drawItemStack(driver, font, item, + rect, &AbsoluteClippingRect, m_gamedef); + } + // Draw tooltip + std::string tooltip_text = ""; + if(hovering && !m_selected_item) + tooltip_text = item.getDefinition(m_gamedef->idef()).description; + if(tooltip_text != "") + { + m_tooltip_element->setVisible(true); + this->bringToFront(m_tooltip_element); + m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str()); + s32 tooltip_x = m_pointer.X + 15; + s32 tooltip_y = m_pointer.Y + 15; + s32 tooltip_width = m_tooltip_element->getTextWidth() + 15; + s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; + m_tooltip_element->setRelativePosition(core::rect<s32>( + core::position2d<s32>(tooltip_x, tooltip_y), + core::dimension2d<s32>(tooltip_width, tooltip_height))); + } + } } } +void GUIInventoryMenu::drawSelectedItem() +{ + if(!m_selected_item) + return; + + video::IVideoDriver* driver = Environment->getVideoDriver(); + + // Get font + gui::IGUIFont *font = NULL; + gui::IGUISkin* skin = Environment->getSkin(); + if (skin) + font = skin->getFont(); + + Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); + assert(inv); + InventoryList *list = inv->getList(m_selected_item->listname); + assert(list); + ItemStack stack = list->getItem(m_selected_item->i); + stack.count = m_selected_amount; + + core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y); + core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter()); + drawItemStack(driver, font, stack, rect, NULL, m_gamedef); +} + void GUIInventoryMenu::drawMenu() { + updateSelectedItem(); + gui::IGUISkin* skin = Environment->getSkin(); if (!skin) return; @@ -297,22 +390,98 @@ void GUIInventoryMenu::drawMenu() video::SColor bgcolor(140,0,0,0); driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect); + m_tooltip_element->setVisible(false); + /* Draw items + Phase 0: Item slot rectangles + Phase 1: Item images; prepare tooltip */ + for(int phase=0; phase<=1; phase++) 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], phase); } /* + Draw dragged item stack + */ + drawSelectedItem(); + + /* Call base class */ gui::IGUIElement::draw(); } +void GUIInventoryMenu::updateSelectedItem() +{ + // If the selected stack has become empty for some reason, deselect it. + // If the selected stack has become smaller, adjust m_selected_amount. + if(m_selected_item) + { + bool selection_valid = false; + if(m_selected_item->isValid()) + { + Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); + if(inv) + { + InventoryList *list = inv->getList(m_selected_item->listname); + if(list && (u32) m_selected_item->i < list->getSize()) + { + ItemStack stack = list->getItem(m_selected_item->i); + if(m_selected_amount > stack.count) + m_selected_amount = stack.count; + if(!stack.empty()) + selection_valid = true; + } + } + } + if(!selection_valid) + { + delete m_selected_item; + m_selected_item = NULL; + m_selected_amount = 0; + m_selected_dragging = false; + } + } + + // If craftresult is nonempty and nothing else is selected, select it now. + if(!m_selected_item) + { + for(u32 i=0; i<m_draw_spec.size(); i++) + { + const ListDrawSpec &s = m_draw_spec[i]; + if(s.listname == "craftpreview") + { + Inventory *inv = m_invmgr->getInventory(s.inventoryloc); + InventoryList *list = inv->getList("craftresult"); + if(list && list->getSize() >= 1 && !list->getItem(0).empty()) + { + m_selected_item = new ItemSpec; + m_selected_item->inventoryloc = s.inventoryloc; + m_selected_item->listname = "craftresult"; + m_selected_item->i = 0; + m_selected_amount = 0; + m_selected_dragging = false; + break; + } + } + } + } + + // If craftresult is selected, keep the whole stack selected + if(m_selected_item && m_selected_item->listname == "craftresult") + { + Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); + assert(inv); + InventoryList *list = inv->getList(m_selected_item->listname); + assert(list); + m_selected_amount = list->getItem(m_selected_item->i).count; + } +} + bool GUIInventoryMenu::OnEvent(const SEvent& event) { if(event.EventType==EET_KEY_INPUT_EVENT) @@ -325,102 +494,268 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) return true; } } - if(event.EventType==EET_MOUSE_INPUT_EVENT) + if(event.EventType==EET_MOUSE_INPUT_EVENT + && event.MouseInput.Event == EMIE_MOUSE_MOVED) + { + // Mouse moved + m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y); + } + if(event.EventType==EET_MOUSE_INPUT_EVENT + && event.MouseInput.Event != EMIE_MOUSE_MOVED) { - char amount = -1; + // Mouse event other than movement v2s32 p(event.MouseInput.X, event.MouseInput.Y); + m_pointer = p; + + // Get selected item and hovered/clicked item (s) + + updateSelectedItem(); ItemSpec s = getItemAtPos(p); - if(event.MouseInput.Event==EMIE_MOUSE_MOVED) - m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y); - else if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) - amount = 0; + Inventory *inv_selected = NULL; + Inventory *inv_s = NULL; + + if(m_selected_item) + { + inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc); + assert(inv_selected); + assert(inv_selected->getList(m_selected_item->listname) != NULL); + } + + u32 s_count = 0; + + if(s.isValid()) + { + inv_s = m_invmgr->getInventory(s.inventoryloc); + assert(inv_s); + + InventoryList *list = inv_s->getList(s.listname); + if(list != NULL && (u32) s.i < list->getSize()) + s_count = list->getItem(s.i).count; + else + s.i = -1; // make it invalid again + } + + bool identical = (m_selected_item != NULL) && s.isValid() && + (inv_selected == inv_s) && + (m_selected_item->listname == s.listname) && + (m_selected_item->i == s.i); + + // buttons: 0 = left, 1 = right, 2 = middle + // up/down: 0 = down (press), 1 = up (release), 2 = unknown event + int button = 0; + int updown = 2; + if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) + { button = 0; updown = 0; } else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) - amount = 1; + { button = 1; updown = 0; } else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) - amount = 10; - else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP && - m_selected_item && - (m_selected_item->listname != s.listname - || m_selected_item->i != s.i)) - amount = 0; - - - if(amount >= 0) + { button = 2; updown = 0; } + else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) + { button = 0; updown = 1; } + else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) + { button = 1; updown = 1; } + else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) + { button = 2; updown = 1; } + + // Set this number to a positive value to generate a move action + // from m_selected_item to s. + u32 move_amount = 0; + + // Set this number to a positive value to generate a drop action + // from m_selected_item. + u32 drop_amount = 0; + + // Set this number to a positive value to generate a craft action at s. + u32 craft_amount = 0; + + if(updown == 0) { - //infostream<<"Mouse action at p=("<<p.X<<","<<p.Y<<")"<<std::endl; - if(s.isValid()) + // Some mouse button has been pressed + + //infostream<<"Mouse button "<<button<<" pressed at p=(" + // <<p.X<<","<<p.Y<<")"<<std::endl; + + m_selected_dragging = false; + + if(s.isValid() && s.listname == "craftpreview") + { + // Craft preview has been clicked: craft + craft_amount = (button == 2 ? 10 : 1); + } + else if(m_selected_item == NULL) { - infostream<<"Mouse action on "<<s.inventoryname - <<"/"<<s.listname<<" "<<s.i<<std::endl; - if(m_selected_item) + if(s_count != 0) { - Inventory *inv_from = m_invmgr->getInventory(m_c, - m_selected_item->inventoryname); - Inventory *inv_to = m_invmgr->getInventory(m_c, - s.inventoryname); - assert(inv_from); - assert(inv_to); - InventoryList *list_from = - inv_from->getList(m_selected_item->listname); - InventoryList *list_to = - inv_to->getList(s.listname); - if(list_from == NULL) - infostream<<"from list doesn't exist"<<std::endl; - if(list_to == NULL) - infostream<<"to list doesn't exist"<<std::endl; - // Indicates whether source slot completely empties - bool source_empties = false; - if(list_from && list_to - && list_from->getItem(m_selected_item->i) != NULL) - { - infostream<<"Handing IACTION_MOVE to manager"<<std::endl; - IMoveAction *a = new IMoveAction(); - a->count = amount; - a->from_inv = m_selected_item->inventoryname; - a->from_list = m_selected_item->listname; - a->from_i = m_selected_item->i; - a->to_inv = s.inventoryname; - 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) - source_empties = true; - } - // Remove selection if target was left-clicked or source - // slot was emptied - if(amount == 0 || source_empties) + // Non-empty stack has been clicked: select it + m_selected_item = new ItemSpec(s); + + if(button == 1) // right + m_selected_amount = (s_count + 1) / 2; + else if(button == 2) // middle + m_selected_amount = MYMIN(s_count, 10); + else // left + m_selected_amount = s_count; + + m_selected_dragging = true; + } + } + else // m_selected_item != NULL + { + assert(m_selected_amount >= 1); + + if(s.isValid()) + { + // Clicked a slot: move + if(button == 1) // right + move_amount = 1; + else if(button == 2) // middle + move_amount = MYMIN(m_selected_amount, 10); + else // left + move_amount = m_selected_amount; + + if(identical) { - delete m_selected_item; - m_selected_item = NULL; + if(move_amount >= m_selected_amount) + m_selected_amount = 0; + else + m_selected_amount -= move_amount; + move_amount = 0; } } + else if(getAbsoluteClippingRect().isPointInside(m_pointer)) + { + // Clicked somewhere else: deselect + m_selected_amount = 0; + } else { - /* - Select if non-NULL - */ - Inventory *inv = m_invmgr->getInventory(m_c, - s.inventoryname); - assert(inv); - InventoryList *list = inv->getList(s.listname); - if(list->getItem(s.i) != NULL) - { - m_selected_item = new ItemSpec(s); - } + // Clicked outside of the window: drop + if(button == 1) // right + drop_amount = 1; + else if(button == 2) // middle + drop_amount = MYMIN(m_selected_amount, 10); + else // left + drop_amount = m_selected_amount; } } - else + } + else if(updown == 1) + { + // Some mouse button has been released + + //infostream<<"Mouse button "<<button<<" released at p=(" + // <<p.X<<","<<p.Y<<")"<<std::endl; + + if(m_selected_item != NULL && m_selected_dragging && s.isValid()) { - if(m_selected_item) + if(!identical) { - delete m_selected_item; - m_selected_item = NULL; + // Dragged to different slot: move all selected + move_amount = m_selected_amount; } } + else if(m_selected_item != NULL && m_selected_dragging && + !(getAbsoluteClippingRect().isPointInside(m_pointer))) + { + // Dragged outside of window: drop all selected + drop_amount = m_selected_amount; + } + + m_selected_dragging = false; + } + + // Possibly send inventory action to server + if(move_amount > 0) + { + // Send IACTION_MOVE + + assert(m_selected_item && m_selected_item->isValid()); + assert(s.isValid()); + + assert(inv_selected && inv_s); + InventoryList *list_from = inv_selected->getList(m_selected_item->listname); + InventoryList *list_to = inv_s->getList(s.listname); + assert(list_from && list_to); + ItemStack stack_from = list_from->getItem(m_selected_item->i); + ItemStack stack_to = list_to->getItem(s.i); + + // Check how many items can be moved + move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); + ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); + if(leftover.count == stack_from.count) + { + // Swap the stacks + m_selected_amount -= stack_to.count; + } + else if(leftover.empty()) + { + // Item fits + m_selected_amount -= move_amount; + } + else + { + // Item only fits partially + move_amount -= leftover.count; + m_selected_amount -= move_amount; + } + + infostream<<"Handing IACTION_MOVE to manager"<<std::endl; + IMoveAction *a = new IMoveAction(); + a->count = move_amount; + 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.inventoryloc; + a->to_list = s.listname; + a->to_i = s.i; + m_invmgr->inventoryAction(a); + } + else if(drop_amount > 0) + { + // Send IACTION_DROP + + assert(m_selected_item && m_selected_item->isValid()); + assert(inv_selected); + InventoryList *list_from = inv_selected->getList(m_selected_item->listname); + assert(list_from); + ItemStack stack_from = list_from->getItem(m_selected_item->i); + + // Check how many items can be dropped + drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count); + assert(drop_amount > 0 && drop_amount <= m_selected_amount); + m_selected_amount -= drop_amount; + + infostream<<"Handing IACTION_DROP to manager"<<std::endl; + IDropAction *a = new IDropAction(); + a->count = drop_amount; + a->from_inv = m_selected_item->inventoryloc; + a->from_list = m_selected_item->listname; + a->from_i = m_selected_item->i; + m_invmgr->inventoryAction(a); + } + else if(craft_amount > 0) + { + // Send IACTION_CRAFT + + assert(s.isValid()); + assert(inv_s); + + infostream<<"Handing IACTION_CRAFT to manager"<<std::endl; + ICraftAction *a = new ICraftAction(); + a->count = craft_amount; + a->craft_inv = s.inventoryloc; + m_invmgr->inventoryAction(a); + } + + // If m_selected_amount has been decreased to zero, deselect + if(m_selected_amount == 0) + { + delete m_selected_item; + m_selected_item = NULL; + m_selected_amount = 0; + m_selected_dragging = false; } } if(event.EventType==EET_GUI_EVENT) @@ -489,7 +824,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 +835,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 +850,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..ed8df88fc 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,8 +136,10 @@ public: void regenerateGui(v2u32 screensize); ItemSpec getItemAtPos(v2s32 p) const; - void drawList(const ListDrawSpec &s, ITextureSource *tsrc); + void drawList(const ListDrawSpec &s, int phase); + void drawSelectedItem(); void drawMenu(); + void updateSelectedItem(); bool OnEvent(const SEvent& event); @@ -153,15 +155,18 @@ 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; ItemSpec *m_selected_item; + u32 m_selected_amount; + bool m_selected_dragging; + v2s32 m_pointer; + gui::IGUIStaticText *m_tooltip_element; }; #endif diff --git a/src/inventory.cpp b/src/inventory.cpp index 0d38bed78..056cf552d 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -22,545 +22,430 @@ 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); + name = itemdef->getAlias(name); + 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; - - return tsrc->getTextureRaw(os.str()); -} - -video::ITexture * ToolItem::getImageRaw() const -{ - ITextureSource *tsrc = m_gamedef->tsrc(); - - return tsrc->getTextureRaw(getImageBasename()); -} -#endif - -bool ToolItem::isKnown() const -{ - IToolDefManager *tdef = m_gamedef->tdef(); - const ToolDefinition *def = tdef->getToolDefinition(m_toolname); - return (def != NULL); -} + do // This loop is just to allow "break;" + { + // The real thing -/* - CraftItem -*/ + // Apply item aliases + name = itemdef->getAlias(name); -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 the count + std::string count_str; + std::getline(is, count_str, ' '); + if(count_str.empty()) + { + count = 1; + break; + } + else + count = stoi(count_str); -#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 + // Read the wear + std::string wear_str; + std::getline(is, wear_str, ' '); + if(wear_str.empty()) + break; + else + wear = stoi(wear_str); -bool CraftItem::isKnown() const -{ - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - return (def != NULL); -} + // Read metadata + metadata = deSerializeJsonStringIfNeeded(is); -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; -} + // 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::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; + ItemStack newitem = newitem_; - bool callback_exists = false; - bool result = false; - - 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; - - result = scriptapi_craftitem_on_use( - env->getLua(), - m_subname.c_str(), user, pointed, - callback_exists); + if(takecount == 0 || count == 0) + return ItemStack(); - 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 +453,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 +464,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 +480,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 +511,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 +530,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 +549,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 +596,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 +612,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,148 +626,153 @@ 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++) + 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); +} + +void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count) +{ + if(this == dest && i == dest_i) + return; + + // Take item from source list + ItemStack item1; + if(count == 0) + item1 = changeItem(i, ItemStack()); + else + item1 = takeItem(i, count); + + if(item1.empty()) + return; + + // Try to add the item to destination list + u32 oldcount = item1.count; + item1 = dest->addItem(dest_i, item1); + + // If something is returned, the item was not fully added + if(!item1.empty()) { - InventoryItem *item = m_items[i]; - if(item != NULL) + // If olditem is returned, nothing was added. + 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 + addItem(i, item1); + + // If olditem is returned, nothing was added. + // Swap the items + if(nothing_added) { - o<<i<<": "; - item->serialize(o); - o<<"\n"; + // Take item from source list + item1 = changeItem(i, ItemStack()); + // Adding was not possible, swap the items. + ItemStack item2 = dest->changeItem(dest_i, item1); + // Put item from destination list to the source list + changeItem(i, item2); } } } @@ -927,8 +795,9 @@ void Inventory::clear() m_lists.clear(); } -Inventory::Inventory() +Inventory::Inventory(IItemDefManager *itemdef) { + m_itemdef = itemdef; } Inventory::Inventory(const Inventory &other) @@ -938,10 +807,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 +832,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 +863,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 +883,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 +909,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..fcac5f647 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -23,460 +23,225 @@ 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 - { - 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; - } - 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 + void remove(u16 n) { - os<<"craft"; - os<<" \""; - os<<m_subname; - os<<"\" "; - os<<m_count; + assert(count >= n); + count -= n; + if(count == 0) + clear(); // reset name, wear and metadata too } - 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 + // Returns false if item is not known and cannot be used + bool isKnown(IItemDefManager *itemdef) const { - os<<"tool"; - os<<" \""; - os<<m_toolname; - os<<"\" "; - os<<m_wear; + return itemdef->isKnown(name); } - virtual InventoryItem* clone() - { - return new ToolItem(m_gamedef, m_toolname, m_wear); - } - - 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() - { - return m_wear; - } - // Returns true if weared out - bool addWear(u16 add) + // Wear out (only tools) + // Returns true if the item is (was) a tool + bool addWear(s32 amount, IItemDefManager *itemdef) { - 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 there is room for a given item aftr it has been cooked - bool roomForCookedItem(const InventoryItem *item); + // Checks whether the given count of the given item name + // exists in this inventory list. + bool containsItem(const ItemStack &item) const; + + // 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; + + // Move an item to a different list (or a different stack in the same list) + // count is the maximum number of items to move (0 for everything) + void moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count = 0); - 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 +251,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 +275,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..b04a1c177 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -18,73 +18,92 @@ 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" +#include "craftdef.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); } /* @@ -106,17 +125,17 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is) { a = new IDropAction(is); } + else if(type == "Craft") + { + a = new ICraftAction(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::IMoveAction(std::istream &is) { @@ -125,14 +144,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 +161,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, IGameDef *gamedef) { - 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,84 +187,71 @@ 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) - { - infostream<<"IMoveAction::apply(): FAIL: source item not found: " - <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" - <<", from_list=\""<<from_list<<"\"" - <<" from_i="<<from_i<<std::endl; - return; - } /* - If the source and the destination slots are the same - */ - 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 - <<"\" i="<<from_i<<std::endl; - return; - } - - // Take item from source list - InventoryItem *item1 = NULL; - if(count == 0) - item1 = list_from->changeItem(from_i, NULL); - else - item1 = list_from->takeItem(from_i, count); - - // Try to add the item to destination list - InventoryItem *olditem = item1; - item1 = list_to->addItem(to_i, item1); + This performs the actual movement - // If something is returned, the item was not fully added - if(item1 != NULL) - { - // If olditem is returned, nothing was added. - bool nothing_added = (item1 == olditem); - - // If something else is returned, part of the item was left unadded. - // Add the other part back to the source item - list_from->addItem(from_i, item1); - - // If olditem is returned, nothing was added. - // Swap the items - if(nothing_added) - { - // Take item from source list - item1 = list_from->changeItem(from_i, NULL); - // Adding was not possible, swap the items. - InventoryItem *item2 = list_to->changeItem(to_i, item1); - // Put item from destination list to the source list - list_from->changeItem(from_i, item2); - } - } + If something is wrong (source item is empty, destination is the + same as source), nothing happens + */ + list_from->moveItem(from_i, list_to, to_i, count); - 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; } +void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) +{ + // Optional InventoryAction operation that is run on the client + // to make lag less apparent. + + Inventory *inv_from = mgr->getInventory(from_inv); + Inventory *inv_to = mgr->getInventory(to_inv); + if(!inv_from || !inv_to) + return; + + InventoryLocation current_player; + current_player.setCurrentPlayer(); + Inventory *inv_player = mgr->getInventory(current_player); + if(inv_from != inv_player || inv_to != inv_player) + return; + + InventoryList *list_from = inv_from->getList(from_list); + InventoryList *list_to = inv_to->getList(to_list); + if(!list_from || !list_to) + return; + + list_from->moveItem(from_i, list_to, to_i, count); + + mgr->setInventoryModified(from_inv); + if(inv_from != inv_to) + mgr->setInventoryModified(to_inv); +} + +/* + IDropAction +*/ + IDropAction::IDropAction(std::istream &is) { std::string ts; @@ -252,7 +259,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 +268,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, IGameDef *gamedef) { - 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 +285,199 @@ 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; + // Take item from source list + ItemStack item1; + if(count == 0) + item1 = list_from->changeItem(from_i, ItemStack()); + else + item1 = list_from->takeItem(from_i, count); - s16 count2 = count; - if(count2 == 0) - count2 = -1; + // Drop the item and apply the returned ItemStack + ItemStack item2 = item1; + if(scriptapi_item_on_drop(player->getEnv()->getLua(), item2, player, + player->getBasePosition() + v3f(0,1,0))) + { + if(g_settings->getBool("creative_mode") == true + && from_inv.type == InventoryLocation::PLAYER) + item2 = item1; // creative mode - /* - Drop the item - */ - bool remove = item->dropOrPlace(env, dropper, pos, false, count2); - if(remove) - list_from->deleteItem(from_i); + list_from->addItem(from_i, item2); - mgr->inventoryModified(c, from_inv); + // Unless we have put the same amount back as we took in the first place, + // set inventory modified flag + if(item2.count != item1.count) + 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; } +void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) +{ + // Optional InventoryAction operation that is run on the client + // to make lag less apparent. + + Inventory *inv_from = mgr->getInventory(from_inv); + if(!inv_from) + return; + + InventoryLocation current_player; + current_player.setCurrentPlayer(); + Inventory *inv_player = mgr->getInventory(current_player); + if(inv_from != inv_player) + return; + + InventoryList *list_from = inv_from->getList(from_list); + if(!list_from) + return; + + if(count == 0) + list_from->changeItem(from_i, ItemStack()); + else + list_from->takeItem(from_i, count); + + mgr->setInventoryModified(from_inv); +} + /* - Craft checking system + ICraftAction */ -bool ItemSpec::checkItem(const InventoryItem *item) const +ICraftAction::ICraftAction(std::istream &is) { - 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 ts; - std::string itemname = item->getName(); + std::getline(is, ts, ' '); + count = stoi(ts); - 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; + std::getline(is, ts, ' '); + craft_inv.deSerialize(ts); } -bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs) +void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { - 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; + Inventory *inv_craft = mgr->getInventory(craft_inv); - 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; + if(!inv_craft){ + infostream<<"ICraftAction::apply(): FAIL: inventory not found: " + <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl; + return; } - // 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; + InventoryList *list_craft = inv_craft->getList("craft"); + InventoryList *list_craftresult = inv_craft->getList("craftresult"); - // Different sizes - if(items_w != specs_w || items_h != specs_h) - return false; + /* + If a list doesn't exist or the source item doesn't exist + */ + if(!list_craft){ + infostream<<"ICraftAction::apply(): FAIL: craft list not found: " + <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl; + return; + } + if(!list_craftresult){ + infostream<<"ICraftAction::apply(): FAIL: craftresult list not found: " + <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl; + return; + } + if(list_craftresult->getSize() < 1){ + infostream<<"ICraftAction::apply(): FAIL: craftresult list too short: " + <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl; + return; + } - for(u16 y=0; y<specs_h; y++) - for(u16 x=0; x<specs_w; x++) + ItemStack crafted; + int count_remaining = count; + bool found = getCraftingResult(inv_craft, crafted, false, gamedef); + + while(found && list_craftresult->itemFits(0, crafted)) { - 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; + // Decrement input and add crafting output + getCraftingResult(inv_craft, crafted, true, gamedef); + list_craftresult->addItem(0, crafted); + mgr->setInventoryModified(craft_inv); + + actionstream<<player->getDescription() + <<" crafts " + <<crafted.getItemString() + <<std::endl; + + // Decrement counter + if(count_remaining == 1) + break; + else if(count_remaining > 1) + count_remaining--; + + // Get next crafting result + found = getCraftingResult(inv_craft, crafted, false, gamedef); } - return true; + infostream<<"ICraftAction::apply(): crafted " + <<" craft_inv=\""<<craft_inv.dump()<<"\"" + <<std::endl; } -bool checkItemCombination(const InventoryItem * const * items, - const InventoryItem * const * specs) +void ICraftAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) { - 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; + // Optional InventoryAction operation that is run on the client + // to make lag less apparent. +} + + +// Crafting helper +bool getCraftingResult(Inventory *inv, ItemStack& result, + bool decrementInput, IGameDef *gamedef) +{ + DSTACK(__FUNCTION_NAME); - 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; + result.clear(); - u16 specs_w = specs_max_x - specs_min_x + 1; - u16 specs_h = specs_max_y - specs_min_y + 1; + // TODO: Allow different sizes of crafting grids - // Different sizes - if(items_w != specs_w || items_h != specs_h) + // Get the InventoryList in which we will operate + InventoryList *clist = inv->getList("craft"); + if(!clist || clist->getSize() != 9) return false; - for(u16 y=0; y<specs_h; y++) - for(u16 x=0; x<specs_w; x++) + // 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 = gamedef->getCraftDefManager()->getCraftResult( + ci, co, decrementInput, gamedef); + if(found) + result.deSerialize(co.item, gamedef->getItemDefManager()); + + if(found && decrementInput) { - 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; + // CraftInput has been changed, apply changes in clist + for(u16 i=0; i<9; i++) + { + clist->changeItem(i, ci.items[i]); + } } - return true; + return found; } - diff --git a/src/inventorymanager.h b/src/inventorymanager.h index 4abe5e73d..52377f9a4 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,22 +83,16 @@ 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 #define IACTION_DROP 1 +#define IACTION_CRAFT 2 struct InventoryAction { @@ -91,18 +100,20 @@ 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, + IGameDef *gamedef) = 0; + virtual void clientApply(InventoryManager *mgr, IGameDef *gamedef) = 0; + virtual ~InventoryAction() {}; }; 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 +135,24 @@ 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, IGameDef *gamedef); + + void clientApply(InventoryManager *mgr, IGameDef *gamedef); }; 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 +173,49 @@ 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); -}; + void apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef); -/* - Craft checking system -*/ - -enum ItemSpecType -{ - ITEM_NONE, - ITEM_MATERIAL, - ITEM_CRAFT, - ITEM_TOOL, - ITEM_MBO + void clientApply(InventoryManager *mgr, IGameDef *gamedef); }; -struct ItemSpec +struct ICraftAction : public InventoryAction { - enum ItemSpecType type; - // Only other one of these is used - std::string name; - u16 num; - - ItemSpec(): - type(ITEM_NONE) + // count=0 means "everything" + u16 count; + InventoryLocation craft_inv; + + ICraftAction() { + count = 0; } - ItemSpec(enum ItemSpecType a_type, std::string a_name): - type(a_type), - name(a_name), - num(65535) + + ICraftAction(std::istream &is); + + u16 getType() const { + return IACTION_CRAFT; } - ItemSpec(enum ItemSpecType a_type, u16 a_num): - type(a_type), - name(""), - num(a_num) + + void serialize(std::ostream &os) const { + os<<"Craft "; + os<<count<<" "; + os<<craft_inv.dump()<<" "; } - bool checkItem(const InventoryItem *item) const; -}; + void apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef); -/* - 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); + void clientApply(InventoryManager *mgr, IGameDef *gamedef); +}; +// Crafting helper +bool getCraftingResult(Inventory *inv, ItemStack& result, + bool decrementInput, IGameDef *gamedef); #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/light.cpp b/src/light.cpp index f214d6ea0..93e498620 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -20,6 +20,56 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "light.h" #if 1 +/* +Made using this and adding 230 as the second last one: + +#!/usr/bin/python + +from math import * +from sys import stdout + +# We want 0 at light=0 and 255 at light=LIGHT_MAX +LIGHT_MAX = 14 +#FACTOR = 0.69 +#FACTOR = 0.75 +FACTOR = 0.83 +START_FROM_ZERO = False + +L = [] +if START_FROM_ZERO: + for i in range(1,LIGHT_MAX+1): + L.append(int(round(255.0 * FACTOR ** (i-1)))) + L.append(0) +else: + for i in range(1,LIGHT_MAX+1): + L.append(int(round(255.0 * FACTOR ** (i-1)))) + L.append(255) + +L.reverse() +for i in L: + stdout.write(str(i)+",\n") +*/ +u8 light_decode_table[LIGHT_MAX+1] = +{ +23, +27, +33, +40, +48, +57, +69, +83, +100, +121, +146, +176, +212, +230, +255, +}; +#endif + +#if 0 // This is good // a_n+1 = a_n * 0.786 // Length of LIGHT_MAX+1 means LIGHT_MAX is the last value. @@ -42,7 +92,9 @@ u8 light_decode_table[LIGHT_MAX+1] = 200, 255, }; -#else +#endif + +#if 0 // Use for debugging in dark u8 light_decode_table[LIGHT_MAX+1] = { diff --git a/src/light.h b/src/light.h index c1af7fa62..238acce43 100644 --- a/src/light.h +++ b/src/light.h @@ -75,16 +75,27 @@ inline u8 undiminish_light(u8 light) extern u8 light_decode_table[LIGHT_MAX+1]; +// 0 <= light <= LIGHT_SUN +// 0 <= return value <= 255 inline u8 decode_light(u8 light) { - if(light == LIGHT_SUN) - return light_decode_table[LIGHT_MAX]; - if(light > LIGHT_MAX) light = LIGHT_MAX; return light_decode_table[light]; } +// 0 <= daylight_factor <= 1000 +// 0 <= lightday, lightnight <= LIGHT_SUN +// 0 <= return value <= LIGHT_SUN +inline u8 blend_light(u32 daylight_factor, u8 lightday, u8 lightnight) +{ + u32 c = 1000; + u32 l = ((daylight_factor * lightday + (c-daylight_factor) * lightnight))/c; + if(l > LIGHT_SUN) + l = LIGHT_SUN; + return l; +} + #endif diff --git a/src/main.cpp b/src/main.cpp index b7c3ceffe..6cb9cf984 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,24 +54,6 @@ A list of "active blocks" in which stuff happens. (+=done) + This was left to be done by the old system and it sends only the nearest ones. -Vim conversion regexpes for moving to extended content type storage: -%s/\(\.\|->\)d \([!=]=\)/\1getContent() \2/g -%s/content_features(\([^.]*\)\.d)/content_features(\1)/g -%s/\(\.\|->\)d = \([^;]*\);/\1setContent(\2);/g -%s/\(getNodeNoExNoEmerge([^)]*)\)\.d/\1.getContent()/g -%s/\(getNodeNoExNoEmerge(.*)\)\.d/\1.getContent()/g -%s/\.d;/.getContent();/g -%s/\(content_liquid\|content_flowing_liquid\|make_liquid_flowing\|content_pointable\)(\([^.]*\).d)/\1(\2.getContent())/g -Other things to note: -- node.d = node.param0 (only in raw serialization; use getContent() otherwise) -- node.param = node.param1 -- node.dir = node.param2 -- content_walkable(node.d) etc should be changed to - content_features(node).walkable etc -- Also check for lines that store the result of getContent to a 8-bit - variable and fix them (result of getContent() must be stored in - content_t, which is 16-bit) - NOTE: Seeds in 1260:6c77e7dbfd29: 5721858502589302589: Spawns you on a small sand island with a surface dungeon @@ -351,8 +333,6 @@ TODO: Block cube placement around player's head TODO: Protocol version field TODO: Think about using same bits for material for fences and doors, for example -TODO: Move mineral to param2, increment map serialization version, add - conversion SUGG: Restart irrlicht completely when coming back to main menu from game. - This gets rid of everything that is stored in irrlicht's caches. @@ -419,7 +399,6 @@ Doing currently: #include "filesys.h" #include "config.h" #include "guiMainMenu.h" -#include "mineral.h" #include "materials.h" #include "game.h" #include "keycode.h" @@ -1261,16 +1240,6 @@ int main(int argc, char *argv[]) mysrand(time(0)); /* - Pre-initialize some stuff with a dummy irrlicht wrapper. - - These are needed for unit tests at least. - */ - - // Must be called before texturesource is created - // (for texture atlas making) - init_mineral(); - - /* Run unit tests */ diff --git a/src/map.cpp b/src/map.cpp index 8bdc2ad4c..c245242e8 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -915,7 +915,7 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks, /* */ void Map::addNodeAndUpdate(v3s16 p, MapNode n, - core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name) + core::map<v3s16, MapBlock*> &modified_blocks) { INodeDefManager *nodemgr = m_gamedef->ndef(); @@ -1011,7 +1011,6 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, errorstream<<"Failed to create node metadata \"" <<metadata_name<<"\""<<std::endl; } else { - meta->setOwner(player_name); setNodeMetadata(p, meta); } } @@ -1291,8 +1290,7 @@ bool Map::addNodeWithEvent(v3s16 p, MapNode n) bool succeeded = true; try{ core::map<v3s16, MapBlock*> modified_blocks; - std::string st = std::string(""); - addNodeAndUpdate(p, n, modified_blocks, st); + addNodeAndUpdate(p, n, modified_blocks); // Copy modified_blocks to event for(core::map<v3s16, MapBlock*>::Iterator @@ -3271,10 +3269,7 @@ void ServerMap::saveBlock(MapBlock *block) o.write((char*)&version, 1); // Write basic data - block->serialize(o, version); - - // Write extra data stored on disk - block->serializeDiskExtra(o, version); + block->serialize(o, version, true); // Write block to database @@ -3336,11 +3331,8 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto } // Read basic data - block->deSerialize(is, version); + block->deSerialize(is, version, true); - // Read extra data stored on disk - block->deSerializeDiskExtra(is, version); - // If it's a new block, insert it to the map if(created_new) sector->insertBlock(block); @@ -3406,10 +3398,7 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool } // Read basic data - block->deSerialize(is, version); - - // Read extra data stored on disk - block->deSerializeDiskExtra(is, version); + block->deSerialize(is, version, true); // If it's a new block, insert it to the map if(created_new) @@ -212,7 +212,7 @@ public: These handle lighting but not faces. */ void addNodeAndUpdate(v3s16 p, MapNode n, - core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name); + core::map<v3s16, MapBlock*> &modified_blocks); void removeNodeAndUpdate(v3s16 p, core::map<v3s16, MapBlock*> &modified_blocks); diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 016cb222e..b436378da 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 @@ -504,25 +507,41 @@ s16 MapBlock::getGroundLevel(v2s16 p2d) /* Serialization */ - // List relevant id-name pairs for ids in the block using nodedef -static void getBlockNodeIdMapping(NameIdMapping *nimap, MapBlock *block, +// Renumbers the content IDs (starting at 0 and incrementing +static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes, INodeDefManager *nodedef) { + std::map<content_t, content_t> mapping; std::set<content_t> unknown_contents; - for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) - for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) - for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) + content_t id_counter = 0; + for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++) { - v3s16 p(x0,y0,z0); - MapNode n = block->getNode(p); - content_t id = n.getContent(); - const ContentFeatures &f = nodedef->get(id); - const std::string &name = f.name; - if(name == "") - unknown_contents.insert(id); + content_t global_id = nodes[i].getContent(); + content_t id = CONTENT_IGNORE; + + // Try to find an existing mapping + std::map<content_t, content_t>::iterator j = mapping.find(global_id); + if(j != mapping.end()) + { + id = j->second; + } else - nimap->set(id, name); + { + // We have to assign a new mapping + id = id_counter++; + mapping.insert(std::make_pair(global_id, id)); + + const ContentFeatures &f = nodedef->get(global_id); + const std::string &name = f.name; + if(name == "") + unknown_contents.insert(global_id); + else + nimap->set(id, name); + } + + // Update the MapNode + nodes[i].setContent(id); } for(std::set<content_t>::const_iterator i = unknown_contents.begin(); @@ -534,7 +553,7 @@ static void getBlockNodeIdMapping(NameIdMapping *nimap, MapBlock *block, // Correct ids in the block to match nodedef based on names. // Unknown ones are added to nodedef. // Will not update itself to match id-name pairs in nodedef. -void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block, +static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes, IGameDef *gamedef) { INodeDefManager *nodedef = gamedef->ndef(); @@ -544,13 +563,9 @@ void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block, // correct ids. std::set<content_t> unnamed_contents; std::set<std::string> unallocatable_contents; - for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) - for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) - for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) + for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++) { - v3s16 p(x0,y0,z0); - MapNode n = block->getNode(p); - content_t local_id = n.getContent(); + content_t local_id = nodes[i].getContent(); std::string name; bool found = nimap->getName(local_id, name); if(!found){ @@ -566,8 +581,7 @@ void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block, continue; } } - n.setContent(global_id); - block->setNode(p, n); + nodes[i].setContent(global_id); } for(std::set<content_t>::const_iterator i = unnamed_contents.begin(); @@ -585,7 +599,7 @@ void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block, } } -void MapBlock::serialize(std::ostream &os, u8 version) +void MapBlock::serialize(std::ostream &os, u8 version, bool disk) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapBlock format not supported"); @@ -595,22 +609,226 @@ void MapBlock::serialize(std::ostream &os, u8 version) throw SerializationError("ERROR: Not writing dummy block."); } - // These have no compression - if(version <= 3 || version == 5 || version == 6) + if(version <= 21) { - u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; - - u32 buflen = 1 + nodecount * MapNode::serializedLength(version); - SharedBuffer<u8> dest(buflen); + serialize_pre22(os, version, disk); + return; + } - dest[0] = is_underground; + // First byte + u8 flags = 0; + if(is_underground) + flags |= 0x01; + if(m_day_night_differs) + flags |= 0x02; + if(m_lighting_expired) + flags |= 0x04; + if(m_generated == false) + flags |= 0x08; + writeU8(os, flags); + + /* + Bulk node data + */ + NameIdMapping nimap; + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + if(disk) + { + MapNode *tmp_nodes = new MapNode[nodecount]; for(u32 i=0; i<nodecount; i++) + tmp_nodes[i] = data[i]; + getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef()); + + u8 content_width = 1; + /*u8 content_width = (nimap.size() <= 255) ? 1 : 2;*/ + u8 params_width = 2; + writeU8(os, content_width); + writeU8(os, params_width); + MapNode::serializeBulk(os, version, tmp_nodes, nodecount, + content_width, params_width, true); + delete[] tmp_nodes; + } + else + { + u8 content_width = 1; + /*u8 content_width = 2;*/ + u8 params_width = 2; + writeU8(os, content_width); + writeU8(os, params_width); + MapNode::serializeBulk(os, version, data, nodecount, + content_width, params_width, true); + } + + /* + Node metadata + */ + std::ostringstream oss(std::ios_base::binary); + m_node_metadata->serialize(oss); + compressZlib(oss.str(), os); + + /* + Data that goes to disk, but not the network + */ + if(disk) + { + // Static objects + m_static_objects.serialize(os); + + // Timestamp + writeU32(os, getTimestamp()); + + // Write block-specific node definition id mapping + nimap.serialize(os); + } +} + + +void MapBlock::deSerialize(std::istream &is, u8 version, bool disk) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapBlock format not supported"); + + if(version <= 21) + { + deSerialize_pre22(is, version, disk); + return; + } + + u8 flags = readU8(is); + is_underground = (flags & 0x01) ? true : false; + m_day_night_differs = (flags & 0x02) ? true : false; + m_lighting_expired = (flags & 0x04) ? true : false; + m_generated = (flags & 0x08) ? false : true; + + /* + Bulk node data + */ + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + u8 content_width = readU8(is); + u8 params_width = readU8(is); + if(content_width != 1) + throw SerializationError("MapBlock::deSerialize(): invalid content_width"); + if(params_width != 2) + throw SerializationError("MapBlock::deSerialize(): invalid params_width"); + MapNode::deSerializeBulk(is, version, data, nodecount, + content_width, params_width, true); + + /* + NodeMetadata + */ + // Ignore errors + try{ + std::ostringstream oss(std::ios_base::binary); + decompressZlib(is, oss); + std::istringstream iss(oss.str(), std::ios_base::binary); + m_node_metadata->deSerialize(iss, m_gamedef); + } + catch(SerializationError &e) + { + errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error" + <<" while deserializing node metadata"<<std::endl; + } + + /* + Data that is only on disk + */ + if(disk) + { + // Static objects + m_static_objects.deSerialize(is); + + // Timestamp + setTimestamp(readU32(is)); + m_disk_timestamp = m_timestamp; + + // Dynamically re-set ids based on node names + NameIdMapping nimap; + nimap.deSerialize(is); + correctBlockNodeIds(&nimap, data, m_gamedef); + } +} + +/* + Legacy serialization +*/ + +// List relevant id-name pairs for ids in the block using nodedef +// Before serialization version 22 +static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes, + INodeDefManager *nodedef) +{ + std::set<content_t> unknown_contents; + for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++) + { + content_t id = nodes[i].getContent(); + const ContentFeatures &f = nodedef->get(id); + const std::string &name = f.name; + if(name == "") + unknown_contents.insert(id); + else + nimap->set(id, name); + } + for(std::set<content_t>::const_iterator + i = unknown_contents.begin(); + i != unknown_contents.end(); i++){ + errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: " + <<"Name for node id "<<(*i)<<" not known"<<std::endl; + } +} +void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk) +{ + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + + MapNode *tmp_data = new MapNode[nodecount]; + + // Legacy data changes + // This code has to change from post-22 to pre-22 format. + INodeDefManager *nodedef = m_gamedef->ndef(); + for(u32 i=0; i<nodecount; i++) + { + const ContentFeatures &f = nodedef->get(tmp_data[i].getContent()); + // Mineral + if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent()) { - u32 s = 1 + i * MapNode::serializedLength(version); - data[i].serialize(&dest[s], version); + tmp_data[i].setContent(nodedef->getId("default:stone")); + tmp_data[i].setParam1(1); // MINERAL_COAL } + else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent()) + { + tmp_data[i].setContent(nodedef->getId("default:stone")); + tmp_data[i].setParam1(2); // MINERAL_IRON + } + // facedir_simple + if(f.legacy_facedir_simple) + { + tmp_data[i].setParam1(tmp_data[i].getParam2()); + tmp_data[i].setParam2(0); + } + // wall_mounted + if(f.legacy_wallmounted) + { + u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0}; + u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits + u8 dir_old_format = wallmounted_new_to_old[dir_new_format]; + tmp_data[i].setParam2(dir_old_format); + } + } + + // Serialize nodes + u32 ser_length = MapNode::serializedLength(version); + SharedBuffer<u8> databuf_nodelist(nodecount * ser_length); + for(u32 i=0; i<nodecount; i++) + { + tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version); + } + + delete[] tmp_data; - os.write((char*)*dest, dest.getSize()); + // These have no compression + if(version <= 3 || version == 5 || version == 6) + { + writeU8(os, is_underground); + os.write((char*)*databuf_nodelist, databuf_nodelist.getSize()); } else if(version <= 10) { @@ -620,15 +838,13 @@ void MapBlock::serialize(std::ostream &os, u8 version) */ // First byte - os.write((char*)&is_underground, 1); - - u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + writeU8(os, is_underground); // Get and compress materials SharedBuffer<u8> materialdata(nodecount); for(u32 i=0; i<nodecount; i++) { - materialdata[i] = data[i].param0; + materialdata[i] = databuf_nodelist[i*ser_length]; } compress(materialdata, os, version); @@ -636,7 +852,7 @@ void MapBlock::serialize(std::ostream &os, u8 version) SharedBuffer<u8> lightdata(nodecount); for(u32 i=0; i<nodecount; i++) { - lightdata[i] = data[i].param1; + lightdata[i] = databuf_nodelist[i*ser_length+1]; } compress(lightdata, os, version); @@ -646,7 +862,7 @@ void MapBlock::serialize(std::ostream &os, u8 version) SharedBuffer<u8> param2data(nodecount); for(u32 i=0; i<nodecount; i++) { - param2data[i] = data[i].param2; + param2data[i] = databuf_nodelist[i*ser_length+2]; } compress(param2data, os, version); } @@ -667,28 +883,19 @@ void MapBlock::serialize(std::ostream &os, u8 version) if(m_generated == false) flags |= 0x08; } - os.write((char*)&flags, 1); + writeU8(os, flags); - u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; - /* Get data */ - // Serialize nodes - SharedBuffer<u8> databuf_nodelist(nodecount*3); - for(u32 i=0; i<nodecount; i++) - { - data[i].serialize(&databuf_nodelist[i*3], version); - } - // Create buffer with different parameters sorted SharedBuffer<u8> databuf(nodecount*3); for(u32 i=0; i<nodecount; i++) { - databuf[i] = databuf_nodelist[i*3]; - databuf[i+nodecount] = databuf_nodelist[i*3+1]; - databuf[i+nodecount*2] = databuf_nodelist[i*3+2]; + databuf[i] = databuf_nodelist[i*ser_length]; + databuf[i+nodecount] = databuf_nodelist[i*ser_length+1]; + databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2]; } /* @@ -725,50 +932,69 @@ void MapBlock::serialize(std::ostream &os, u8 version) } } } -} -void MapBlock::deSerialize(std::istream &is, u8 version) -{ - if(!ser_ver_supported(version)) - throw VersionMismatchException("ERROR: MapBlock format not supported"); - // These have no lighting info - if(version <= 1) + if(disk) { - setLightingExpired(true); - } + // Versions up from 9 have block objects. (DEPRECATED) + if(version >= 9) + { + // count=0 + writeU16(os, 0); + } - // These have no "generated" field - if(version < 18) - { - m_generated = true; + // Versions up from 15 have static objects. + if(version >= 15) + { + m_static_objects.serialize(os); + } + + // Timestamp + if(version >= 17) + { + writeU32(os, getTimestamp()); + } + + // Scan and write node definition id mapping + if(version >= 21) + { + NameIdMapping nimap; + getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef()); + nimap.serialize(os); + } } +} + +void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) +{ + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + + // Initialize default flags + is_underground = false; + m_day_night_differs = false; + m_lighting_expired = false; + m_generated = true; + + // Make a temporary buffer + u32 ser_length = MapNode::serializedLength(version); + SharedBuffer<u8> databuf_nodelist(nodecount * ser_length); // These have no compression if(version <= 3 || version == 5 || version == 6) { - u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; char tmp; is.read(&tmp, 1); if(is.gcount() != 1) throw SerializationError ("MapBlock::deSerialize: no enough input data"); is_underground = tmp; - for(u32 i=0; i<nodecount; i++) - { - s32 len = MapNode::serializedLength(version); - SharedBuffer<u8> d(len); - is.read((char*)*d, len); - if(is.gcount() != len) - throw SerializationError - ("MapBlock::deSerialize: no enough input data"); - data[i].deSerialize(*d, version); - } + is.read((char*)*databuf_nodelist, nodecount * ser_length); + if(is.gcount() != nodecount * ser_length) + throw SerializationError + ("MapBlock::deSerialize: no enough input data"); } else if(version <= 10) { - u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; - u8 t8; is.read((char*)&t8, 1); is_underground = t8; @@ -783,7 +1009,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version) ("MapBlock::deSerialize: invalid format"); for(u32 i=0; i<s.size(); i++) { - data[i].param0 = s[i]; + databuf_nodelist[i*ser_length] = s[i]; } } { @@ -796,7 +1022,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version) ("MapBlock::deSerialize: invalid format"); for(u32 i=0; i<s.size(); i++) { - data[i].param1 = s[i]; + databuf_nodelist[i*ser_length + 1] = s[i]; } } @@ -811,15 +1037,13 @@ void MapBlock::deSerialize(std::istream &is, u8 version) ("MapBlock::deSerialize: invalid format"); for(u32 i=0; i<s.size(); i++) { - data[i].param2 = s[i]; + databuf_nodelist[i*ser_length + 2] = s[i]; } } } // All other versions (newest) else { - u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; - u8 flags; is.read((char*)&flags, 1); is_underground = (flags & 0x01) ? true : false; @@ -840,11 +1064,9 @@ void MapBlock::deSerialize(std::istream &is, u8 version) // deserialize nodes from buffer for(u32 i=0; i<nodecount; i++) { - u8 buf[3]; - buf[0] = s[i]; - buf[1] = s[i+nodecount]; - buf[2] = s[i+nodecount*2]; - data[i].deSerialize(buf, version); + databuf_nodelist[i*ser_length] = s[i]; + databuf_nodelist[i*ser_length + 1] = s[i+nodecount]; + databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2]; } /* @@ -876,76 +1098,98 @@ void MapBlock::deSerialize(std::istream &is, u8 version) } } } -} -void MapBlock::serializeDiskExtra(std::ostream &os, u8 version) -{ - // Versions up from 9 have block objects. (DEPRECATED) - if(version >= 9) + // Deserialize node data + for(u32 i=0; i<nodecount; i++) { - // count=0 - writeU16(os, 0); - } - - // Versions up from 15 have static objects. - if(version >= 15) - { - m_static_objects.serialize(os); + data[i].deSerialize(&databuf_nodelist[i*ser_length], version); } - // Timestamp - if(version >= 17) + if(disk) { - writeU32(os, getTimestamp()); - } + /* + Versions up from 9 have block objects. (DEPRECATED) + */ + if(version >= 9){ + u16 count = readU16(is); + // Not supported and length not known if count is not 0 + if(count != 0){ + errorstream<<"WARNING: MapBlock::deSerialize_pre22(): " + <<"Ignoring stuff coming at and after MBOs"<<std::endl; + return; + } + } + + /* + Versions up from 15 have static objects. + */ + if(version >= 15) + m_static_objects.deSerialize(is); + + // Timestamp + if(version >= 17){ + setTimestamp(readU32(is)); + m_disk_timestamp = m_timestamp; + } else { + setTimestamp(BLOCK_TIMESTAMP_UNDEFINED); + } - // Scan and write node definition id mapping - if(version >= 21){ + // Dynamically re-set ids based on node names NameIdMapping nimap; - getBlockNodeIdMapping(&nimap, this, m_gamedef->ndef()); - nimap.serialize(os); + // If supported, read node definition id mapping + if(version >= 21){ + nimap.deSerialize(is); + // Else set the legacy mapping + } else { + content_mapnode_get_name_id_mapping(&nimap); + } + correctBlockNodeIds(&nimap, data, m_gamedef); } -} -void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version) -{ - /* - Versions up from 9 have block objects. (DEPRECATED) - */ - if(version >= 9){ - u16 count = readU16(is); - // Not supported and length not known if count is not 0 - if(count != 0){ - errorstream<<"WARNING: MapBlock::deSerializeDiskExtra(): " - <<"Ignoring stuff coming at and after MBOs"<<std::endl; - return; + + // Legacy data changes + // This code has to convert from pre-22 to post-22 format. + INodeDefManager *nodedef = m_gamedef->ndef(); + for(u32 i=0; i<nodecount; i++) + { + const ContentFeatures &f = nodedef->get(data[i].getContent()); + // Mineral + if(nodedef->getId("default:stone") == data[i].getContent() + && data[i].getParam1() == 1) + { + data[i].setContent(nodedef->getId("default:stone_with_coal")); + data[i].setParam1(0); + } + else if(nodedef->getId("default:stone") == data[i].getContent() + && data[i].getParam1() == 2) + { + data[i].setContent(nodedef->getId("default:stone_with_iron")); + data[i].setParam1(0); + } + // facedir_simple + if(f.legacy_facedir_simple) + { + data[i].setParam2(data[i].getParam1()); + data[i].setParam1(0); + } + // wall_mounted + if(f.legacy_wallmounted) + { + u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0}; + u8 dir_old_format = data[i].getParam2(); + u8 dir_new_format = 0; + for(u8 j=0; j<8; j++) + { + if((dir_old_format & wallmounted_new_to_old[j]) != 0) + { + dir_new_format = j; + break; + } + } + data[i].setParam2(dir_new_format); } } - /* - Versions up from 15 have static objects. - */ - if(version >= 15) - m_static_objects.deSerialize(is); - - // Timestamp - if(version >= 17){ - setTimestamp(readU32(is)); - m_disk_timestamp = m_timestamp; - } else { - setTimestamp(BLOCK_TIMESTAMP_UNDEFINED); - } - - // Dynamically re-set ids based on node names - NameIdMapping nimap; - // If supported, read node definition id mapping - if(version >= 21){ - nimap.deSerialize(is); - // Else set the legacy mapping - } else { - content_mapnode_get_name_id_mapping(&nimap); - } - correctBlockNodeIds(&nimap, this, m_gamedef); } /* diff --git a/src/mapblock.h b/src/mapblock.h index aa5aa43f7..c9ff36679 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; @@ -366,17 +363,8 @@ public: Graphics-related methods */ - /*// A quick version with nodes passed as parameters - u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, - v3s16 face_dir);*/ - /*// A more convenient version - u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir) - { - return getFaceLight(daynight_ratio, - getNodeParentNoEx(p), - getNodeParentNoEx(p + face_dir), - face_dir); - }*/ +#ifndef SERVER // Only on client + u8 getFaceLight2(u32 daynight_ratio, v3s16 p, v3s16 face_dir, INodeDefManager *nodemgr) { @@ -386,8 +374,6 @@ public: face_dir, nodemgr); } -#ifndef SERVER // Only on client - #if 1 /* Thread-safely updates the whole mesh of the mapblock. @@ -527,20 +513,20 @@ public: */ // These don't write or read version by itself - void serialize(std::ostream &os, u8 version); - void deSerialize(std::istream &is, u8 version); - - // Used after the basic ones when writing on disk (serverside) - void serializeDiskExtra(std::ostream &os, u8 version); - // In addition to doing other things, will add unknown blocks from - // id-name mapping to wndef - void deSerializeDiskExtra(std::istream &is, u8 version); + // Set disk to true for on-disk format, false for over-the-network format + void serialize(std::ostream &os, u8 version, bool disk); + // If disk == true: In addition to doing other things, will add + // unknown blocks from id-name mapping to wndef + void deSerialize(std::istream &is, u8 version, bool disk); private: /* Private methods */ + void serialize_pre22(std::ostream &os, u8 version, bool disk); + void deSerialize_pre22(std::istream &is, u8 version, bool disk); + /* Used only internally, because changes can't be tracked */ diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index c051dda50..b843448c4 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -25,10 +25,8 @@ 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 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block) { @@ -83,6 +81,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 +238,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 +261,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)); @@ -250,70 +281,48 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, dest.push_back(face); } -static TileSpec getTile(const MapNode &node, v3s16 dir, - ITextureSource *tsrc, INodeDefManager *nodemgr) +static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr) { - const ContentFeatures &f = nodemgr->get(node); - - if(f.param_type == CPT_FACEDIR_SIMPLE) - dir = facedir_rotate(node.param1, dir); - - TileSpec spec; - - s32 dir_i = -1; + // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0), + // (0,0,1), (0,0,-1) or (0,0,0) + assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1); + + // Convert direction to single integer for table lookup + // 0 = (0,0,0) + // 1 = (1,0,0) + // 2 = (0,1,0) + // 3 = (0,0,1) + // 4 = invalid, treat as (0,0,0) + // 5 = (0,0,-1) + // 6 = (0,-1,0) + // 7 = (-1,0,0) + u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7; + + // Get rotation for things like chests + u8 facedir = node.getFaceDir(nodemgr); + assert(facedir <= 3); - if(dir == v3s16(0,0,0)) - dir_i = -1; - else if(dir == v3s16(0,1,0)) - dir_i = 0; - else if(dir == v3s16(0,-1,0)) - dir_i = 1; - else if(dir == v3s16(1,0,0)) - dir_i = 2; - else if(dir == v3s16(-1,0,0)) - dir_i = 3; - else if(dir == v3s16(0,0,1)) - dir_i = 4; - else if(dir == v3s16(0,0,-1)) - dir_i = 5; - - if(dir_i == -1) - // Non-directional - spec = f.tiles[0]; - else - spec = f.tiles[dir_i]; - - /* - If it contains some mineral, change texture id - */ - if(f.param_type == CPT_MINERAL && tsrc) + static const u8 dir_to_tile[4 * 8] = { - u8 mineral = node.getMineral(nodemgr); - std::string mineral_texture_name = mineral_block_texture(mineral); - if(mineral_texture_name != "") - { - u32 orig_id = spec.texture.id; - std::string texture_name = tsrc->getTextureName(orig_id); - //texture_name += "^blit:"; - texture_name += "^"; - texture_name += mineral_texture_name; - u32 new_id = tsrc->getTextureId(texture_name); - spec.texture = tsrc->getTexture(new_id); - } - } - - return spec; + // 0 +X +Y +Z 0 -Z -Y -X + 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0 + 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1 + 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2 + 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3 + }; + + return nodemgr->get(node).tiles[dir_to_tile[facedir*8 + dir_i]]; } /* 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); + spec = getTile(mn, face_dir, ndef); /* Check temporary modifications on this node @@ -325,13 +334,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); + spec = getTile(mn2, face_dir, ndef); } + #endif if(mod.type == NODEMOD_CRACK) { /* @@ -361,19 +372,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 +401,7 @@ static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods) */ } } + #endif return mn.getContent(); } @@ -469,7 +476,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 +560,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 +756,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 +775,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 +794,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/mapgen.cpp b/src/mapgen.cpp index c2256cedb..fe2ce13f5 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "noise.h" #include "mapblock.h" #include "map.h" -#include "mineral.h" //#include "serverobject.h" #include "content_sao.h" #include "nodedef.h" @@ -1652,15 +1651,20 @@ void make_block(BlockMakeData *data) MapNode n_##name(c_##name); CONTENT_VARIABLE(ndef, stone); - CONTENT_VARIABLE(ndef, water_source); CONTENT_VARIABLE(ndef, air); + CONTENT_VARIABLE(ndef, water_source); CONTENT_VARIABLE(ndef, dirt); CONTENT_VARIABLE(ndef, sand); CONTENT_VARIABLE(ndef, gravel); + CONTENT_VARIABLE(ndef, clay); CONTENT_VARIABLE(ndef, lava_source); CONTENT_VARIABLE(ndef, cobble); CONTENT_VARIABLE(ndef, mossycobble); CONTENT_VARIABLE(ndef, dirt_with_grass); + CONTENT_VARIABLE(ndef, junglegrass); + CONTENT_VARIABLE(ndef, stone_with_coal); + CONTENT_VARIABLE(ndef, stone_with_iron); + CONTENT_VARIABLE(ndef, mese); /* Make base ground level @@ -1703,139 +1707,6 @@ void make_block(BlockMakeData *data) } /* - Add minerals - */ - - { - PseudoRandom mineralrandom(blockseed); - - /* - Add meseblocks - */ - for(s16 i=0; i<approx_ground_depth/4; i++) - { - if(mineralrandom.next()%50 == 0) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == c_stone) - if(mineralrandom.next()%8 == 0) - vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_MESE")); - } - - } - } - /* - Add others - */ - { - u16 a = mineralrandom.range(0,15); - a = a*a*a; - u16 amount = 20 * a/1000; - for(s16 i=0; i<amount; i++) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - - u8 base_content = LEGN(ndef, "CONTENT_STONE"); - MapNode new_content(CONTENT_IGNORE); - u32 sparseness = 6; - - if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1) - { - new_content = MapNode(LEGN(ndef, "CONTENT_STONE"), MINERAL_COAL); - } - else - { - if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0) - new_content = MapNode(LEGN(ndef, "CONTENT_STONE"), MINERAL_IRON); - /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0) - vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_MUD")); - else - vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_SAND"));*/ - } - /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1) - { - }*/ - - if(new_content.getContent() != CONTENT_IGNORE) - { - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == base_content) - { - if(mineralrandom.next()%sparseness == 0) - vmanip.m_data[vi] = new_content; - } - } - } - } - } - /* - Add coal - */ - //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++) - //for(s16 i=0; i<50; i++) - u16 coal_amount = 30; - u16 coal_rareness = 60 / coal_amount; - if(coal_rareness == 0) - coal_rareness = 1; - if(mineralrandom.next()%coal_rareness == 0) - { - u16 a = mineralrandom.next() % 16; - u16 amount = coal_amount * a*a*a / 1000; - for(s16 i=0; i<amount; i++) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == c_stone) - if(mineralrandom.next()%8 == 0) - vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_STONE"), MINERAL_COAL); - } - } - } - /* - Add iron - */ - u16 iron_amount = 8; - u16 iron_rareness = 60 / iron_amount; - if(iron_rareness == 0) - iron_rareness = 1; - if(mineralrandom.next()%iron_rareness == 0) - { - u16 a = mineralrandom.next() % 16; - u16 amount = iron_amount * a*a*a / 1000; - for(s16 i=0; i<amount; i++) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == c_stone) - if(mineralrandom.next()%8 == 0) - vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_STONE"), MINERAL_IRON); - } - } - } - } - - /* Add mud and sand and others underground (in place of stone) */ @@ -1955,7 +1826,7 @@ void make_block(BlockMakeData *data) /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE) { if(wetness > 1.2) - vmanip.m_data[i].setContent(LEGN(ndef, "CONTENT_MUD")); + vmanip.m_data[i].setContent(c_dirt); }*/ data->vmanip->m_area.add_y(em, i, -1); } @@ -2077,23 +1948,23 @@ void make_block(BlockMakeData *data) ((claynoise > 0) && (claynoise < 0.12) && (current_depth == 1)) ); if (have_clay) - vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_CLAY")); + vmanip.m_data[i] = MapNode(c_clay); else - vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_SAND")); + vmanip.m_data[i] = MapNode(c_sand); } #if 1 else if(current_depth==0 && !water_detected && y >= WATER_LEVEL && air_detected) - vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_GRASS")); + vmanip.m_data[i] = MapNode(c_dirt_with_grass); #endif else - vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_MUD")); + vmanip.m_data[i] = MapNode(c_dirt); } else { - if(vmanip.m_data[i].getContent() == LEGN(ndef, "CONTENT_MUD") - || vmanip.m_data[i].getContent() == LEGN(ndef, "CONTENT_GRASS")) - vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_STONE")); + if(vmanip.m_data[i].getContent() == c_dirt + || vmanip.m_data[i].getContent() == c_dirt_with_grass) + vmanip.m_data[i] = MapNode(c_stone); } current_depth++; @@ -2171,7 +2042,7 @@ void make_block(BlockMakeData *data) make_papyrus(vmanip, p, ndef); } // Trees grow only on mud and grass, on land - else if((n->getContent() == c_dirt || n->getContent() == LEGN(ndef, "CONTENT_GRASS")) && y > WATER_LEVEL + 2) + else if((n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) && y > WATER_LEVEL + 2) { p.Y++; //if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5) @@ -2238,10 +2109,10 @@ void make_block(BlockMakeData *data) continue; /*p.Y--; if(vmanip.m_area.contains(p)) - vmanip.m_data[vmanip.m_area.index(p)] = LEGN(ndef, "CONTENT_MUD"); + vmanip.m_data[vmanip.m_area.index(p)] = c_dirt; p.Y++;*/ if(vmanip.m_area.contains(p)) - vmanip.m_data[vmanip.m_area.index(p)] = LEGN(ndef, "CONTENT_JUNGLEGRASS"); + vmanip.m_data[vmanip.m_area.index(p)] = c_junglegrass; } } @@ -2269,7 +2140,7 @@ void make_block(BlockMakeData *data) /*{ u32 i = data->vmanip->m_area.index(v3s16(p)); MapNode *n = &data->vmanip->m_data[i]; - if(n->getContent() != LEGN(ndef, "CONTENT_MUD") && n->getContent() != LEGN(ndef, "CONTENT_GRASS")) + if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass) continue; }*/ // Will be placed one higher @@ -2304,7 +2175,7 @@ void make_block(BlockMakeData *data) /*{ u32 i = data->vmanip->m_area.index(v3s16(p)); MapNode *n = &data->vmanip->m_data[i]; - if(n->getContent() != LEGN(ndef, "CONTENT_MUD") && n->getContent() != LEGN(ndef, "CONTENT_GRASS")) + if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass) continue; }*/ // Will be placed one lower @@ -2315,6 +2186,139 @@ void make_block(BlockMakeData *data) #endif } + /* + Add minerals + */ + + { + PseudoRandom mineralrandom(blockseed); + + /* + Add meseblocks + */ + for(s16 i=0; i<approx_ground_depth/4; i++) + { + if(mineralrandom.next()%50 == 0) + { + s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); + s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); + s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); + for(u16 i=0; i<27; i++) + { + v3s16 p = v3s16(x,y,z) + g_27dirs[i]; + u32 vi = vmanip.m_area.index(p); + if(vmanip.m_data[vi].getContent() == c_stone) + if(mineralrandom.next()%8 == 0) + vmanip.m_data[vi] = MapNode(c_mese); + } + + } + } + /* + Add others + */ + { + u16 a = mineralrandom.range(0,15); + a = a*a*a; + u16 amount = 20 * a/1000; + for(s16 i=0; i<amount; i++) + { + s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); + s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); + s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); + + u8 base_content = c_stone; + MapNode new_content(CONTENT_IGNORE); + u32 sparseness = 6; + + if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1) + { + new_content = MapNode(c_stone_with_coal); + } + else + { + if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0) + new_content = MapNode(c_stone_with_iron); + /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0) + vmanip.m_data[i] = MapNode(c_dirt); + else + vmanip.m_data[i] = MapNode(c_sand);*/ + } + /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1) + { + }*/ + + if(new_content.getContent() != CONTENT_IGNORE) + { + for(u16 i=0; i<27; i++) + { + v3s16 p = v3s16(x,y,z) + g_27dirs[i]; + u32 vi = vmanip.m_area.index(p); + if(vmanip.m_data[vi].getContent() == base_content) + { + if(mineralrandom.next()%sparseness == 0) + vmanip.m_data[vi] = new_content; + } + } + } + } + } + /* + Add coal + */ + //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++) + //for(s16 i=0; i<50; i++) + u16 coal_amount = 30; + u16 coal_rareness = 60 / coal_amount; + if(coal_rareness == 0) + coal_rareness = 1; + if(mineralrandom.next()%coal_rareness == 0) + { + u16 a = mineralrandom.next() % 16; + u16 amount = coal_amount * a*a*a / 1000; + for(s16 i=0; i<amount; i++) + { + s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); + s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); + s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); + for(u16 i=0; i<27; i++) + { + v3s16 p = v3s16(x,y,z) + g_27dirs[i]; + u32 vi = vmanip.m_area.index(p); + if(vmanip.m_data[vi].getContent() == c_stone) + if(mineralrandom.next()%8 == 0) + vmanip.m_data[vi] = MapNode(c_stone_with_coal); + } + } + } + /* + Add iron + */ + u16 iron_amount = 8; + u16 iron_rareness = 60 / iron_amount; + if(iron_rareness == 0) + iron_rareness = 1; + if(mineralrandom.next()%iron_rareness == 0) + { + u16 a = mineralrandom.next() % 16; + u16 amount = iron_amount * a*a*a / 1000; + for(s16 i=0; i<amount; i++) + { + s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); + s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); + s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); + for(u16 i=0; i<27; i++) + { + v3s16 p = v3s16(x,y,z) + g_27dirs[i]; + u32 vi = vmanip.m_area.index(p); + if(vmanip.m_data[vi].getContent() == c_stone) + if(mineralrandom.next()%8 == 0) + vmanip.m_data[vi] = MapNode(c_stone_with_iron); + } + } + } + } + } BlockMakeData::BlockMakeData(): diff --git a/src/mapnode.cpp b/src/mapnode.cpp index a757149b1..6cb9671b5 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -21,136 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapnode.h" #include "porting.h" #include <string> -#include "mineral.h" #include "main.h" // For g_settings #include "nodedef.h" #include "content_mapnode.h" // For mapnode_translate_*_internal #include "serialization.h" // For ser_ver_supported -#ifndef SERVER -/* - Nodes make a face if contents differ and solidness differs. - Return value: - 0: No face - 1: Face uses m1's content - 2: Face uses m2's content - equivalent: Whether the blocks share the same face (eg. water and glass) - - TODO: Add 3: Both faces drawn with backface culling, remove equivalent -*/ -u8 face_contents(content_t m1, content_t m2, bool *equivalent, - INodeDefManager *nodemgr) -{ - *equivalent = false; - - if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE) - return 0; - - bool contents_differ = (m1 != m2); - - const ContentFeatures &f1 = nodemgr->get(m1); - const ContentFeatures &f2 = nodemgr->get(m2); - - // Contents don't differ for different forms of same liquid - if(f1.sameLiquid(f2)) - contents_differ = false; - - u8 c1 = f1.solidness; - u8 c2 = f2.solidness; - - bool solidness_differs = (c1 != c2); - bool makes_face = contents_differ && solidness_differs; - - if(makes_face == false) - return 0; - - if(c1 == 0) - c1 = f1.visual_solidness; - if(c2 == 0) - c2 = f2.visual_solidness; - - if(c1 == c2){ - *equivalent = true; - // If same solidness, liquid takes precense - if(f1.isLiquid()) - return 1; - if(f2.isLiquid()) - return 2; - } - - if(c1 > c2) - return 1; - else - return 2; -} -#endif - -v3s16 facedir_rotate(u8 facedir, v3s16 dir) -{ - /* - Face 2 (normally Z-) direction: - facedir=0: Z- - facedir=1: X- - facedir=2: Z+ - facedir=3: X+ - */ - v3s16 newdir; - if(facedir==0) // Same - newdir = v3s16(dir.X, dir.Y, dir.Z); - else if(facedir == 1) // Face is taken from rotXZccv(-90) - newdir = v3s16(-dir.Z, dir.Y, dir.X); - else if(facedir == 2) // Face is taken from rotXZccv(180) - newdir = v3s16(-dir.X, dir.Y, -dir.Z); - else if(facedir == 3) // Face is taken from rotXZccv(90) - newdir = v3s16(dir.Z, dir.Y, -dir.X); - else - newdir = dir; - return newdir; -} - -u8 packDir(v3s16 dir) -{ - u8 b = 0; - - if(dir.X > 0) - b |= (1<<0); - else if(dir.X < 0) - b |= (1<<1); - - if(dir.Y > 0) - b |= (1<<2); - else if(dir.Y < 0) - b |= (1<<3); - - if(dir.Z > 0) - b |= (1<<4); - else if(dir.Z < 0) - b |= (1<<5); - - return b; -} -v3s16 unpackDir(u8 b) -{ - v3s16 d(0,0,0); - - if(b & (1<<0)) - d.X = 1; - else if(b & (1<<1)) - d.X = -1; - - if(b & (1<<2)) - d.Y = 1; - else if(b & (1<<3)) - d.Y = -1; - - if(b & (1<<4)) - d.Z = 1; - else if(b & (1<<5)) - d.Z = -1; - - return d; -} - /* MapNode */ @@ -191,8 +66,9 @@ void MapNode::setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr u8 MapNode::getLight(enum LightBank bank, INodeDefManager *nodemgr) const { // Select the brightest of [light source, propagated light] + const ContentFeatures &f = nodemgr->get(*this); u8 light = 0; - if(nodemgr->get(*this).param_type == CPT_LIGHT) + if(f.param_type == CPT_LIGHT) { if(bank == LIGHTBANK_DAY) light = param1 & 0x0f; @@ -201,38 +77,63 @@ u8 MapNode::getLight(enum LightBank bank, INodeDefManager *nodemgr) const else assert(0); } - if(nodemgr->get(*this).light_source > light) - light = nodemgr->get(*this).light_source; + if(f.light_source > light) + light = f.light_source; return light; } -u8 MapNode::getLightBanksWithSource(INodeDefManager *nodemgr) const +bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodemgr) const { // Select the brightest of [light source, propagated light] - u8 lightday = 0; - u8 lightnight = 0; - if(nodemgr->get(*this).param_type == CPT_LIGHT) + const ContentFeatures &f = nodemgr->get(*this); + if(f.param_type == CPT_LIGHT) { lightday = param1 & 0x0f; lightnight = (param1>>4)&0x0f; } - if(nodemgr->get(*this).light_source > lightday) - lightday = nodemgr->get(*this).light_source; - if(nodemgr->get(*this).light_source > lightnight) - lightnight = nodemgr->get(*this).light_source; - return (lightday&0x0f) | ((lightnight<<4)&0xf0); + else + { + lightday = 0; + lightnight = 0; + } + if(f.light_source > lightday) + lightday = f.light_source; + if(f.light_source > lightnight) + lightnight = f.light_source; + return f.param_type == CPT_LIGHT || f.light_source != 0; } -u8 MapNode::getMineral(INodeDefManager *nodemgr) const +u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const { - if(nodemgr->get(*this).param_type == CPT_MINERAL) + const ContentFeatures &f = nodemgr->get(*this); + if(f.param_type_2 == CPT2_FACEDIR) + return getParam2() & 0x03; + return 0; +} + +u8 MapNode::getWallMounted(INodeDefManager *nodemgr) const +{ + const ContentFeatures &f = nodemgr->get(*this); + if(f.param_type_2 == CPT2_WALLMOUNTED) + return getParam2() & 0x07; + return 0; +} + +v3s16 MapNode::getWallMountedDir(INodeDefManager *nodemgr) const +{ + switch(getWallMounted(nodemgr)) { - return param1 & 0x0f; + case 0: default: return v3s16(0,1,0); + case 1: return v3s16(0,-1,0); + case 2: return v3s16(1,0,0); + case 3: return v3s16(-1,0,0); + case 4: return v3s16(0,0,1); + case 5: return v3s16(0,0,-1); } - - return MINERAL_NONE; } + + u32 MapNode::serializedLength(u8 version) { if(!ser_ver_supported(version)) @@ -249,7 +150,144 @@ void MapNode::serialize(u8 *dest, u8 version) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapNode format not supported"); + + if(version <= 21) + { + serialize_pre22(dest, version); + return; + } + + writeU8(dest+0, param0); + writeU8(dest+1, param1); + writeU8(dest+2, param2); +} +void MapNode::deSerialize(u8 *source, u8 version) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapNode format not supported"); + if(version <= 21) + { + deSerialize_pre22(source, version); + return; + } + + param0 = readU8(source+0); + param1 = readU8(source+1); + param2 = readU8(source+2); +} +void MapNode::serializeBulk(std::ostream &os, int version, + const MapNode *nodes, u32 nodecount, + u8 content_width, u8 params_width, bool compressed) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapNode format not supported"); + + assert(version >= 22); + assert(content_width == 1); + assert(params_width == 2); + + SharedBuffer<u8> databuf(nodecount * (content_width + params_width)); + + // Serialize content + if(content_width == 1) + { + for(u32 i=0; i<nodecount; i++) + writeU8(&databuf[i], nodes[i].param0); + } + /* If param0 is extended to two bytes, use something like this: */ + /*else if(content_width == 2) + { + for(u32 i=0; i<nodecount; i++) + writeU16(&databuf[i*2], nodes[i].param0); + }*/ + + // Serialize param1 + u32 start1 = content_width * nodecount; + for(u32 i=0; i<nodecount; i++) + writeU8(&databuf[start1 + i], nodes[i].param1); + + // Serialize param2 + u32 start2 = (content_width + 1) * nodecount; + for(u32 i=0; i<nodecount; i++) + writeU8(&databuf[start2 + i], nodes[i].param2); + + /* + Compress data to output stream + */ + + if(compressed) + { + compressZlib(databuf, os); + } + else + { + os.write((const char*) &databuf[0], databuf.getSize()); + } +} + +// Deserialize bulk node data +void MapNode::deSerializeBulk(std::istream &is, int version, + MapNode *nodes, u32 nodecount, + u8 content_width, u8 params_width, bool compressed) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapNode format not supported"); + + assert(version >= 22); + assert(content_width == 1); + assert(params_width == 2); + + // Uncompress or read data + u32 len = nodecount * (content_width + params_width); + SharedBuffer<u8> databuf(len); + if(compressed) + { + std::ostringstream os(std::ios_base::binary); + decompressZlib(is, os); + std::string s = os.str(); + if(s.size() != len) + throw SerializationError("deSerializeBulkNodes: " + "decompress resulted in invalid size"); + memcpy(&databuf[0], s.c_str(), len); + } + else + { + is.read((char*) &databuf[0], len); + if(is.eof() || is.fail()) + throw SerializationError("deSerializeBulkNodes: " + "failed to read bulk node data"); + } + + // Deserialize content + if(content_width == 1) + { + for(u32 i=0; i<nodecount; i++) + nodes[i].param0 = readU8(&databuf[i]); + } + /* If param0 is extended to two bytes, use something like this: */ + /*else if(content_width == 2) + { + for(u32 i=0; i<nodecount; i++) + nodes[i].param0 = readU16(&databuf[i*2]); + }*/ + + // Deserialize param1 + u32 start1 = content_width * nodecount; + for(u32 i=0; i<nodecount; i++) + nodes[i].param1 = readU8(&databuf[start1 + i]); + + // Deserialize param2 + u32 start2 = (content_width + 1) * nodecount; + for(u32 i=0; i<nodecount; i++) + nodes[i].param2 = readU8(&databuf[start2 + i]); +} + +/* + Legacy serialization +*/ +void MapNode::serialize_pre22(u8 *dest, u8 version) +{ // Translate to wanted version MapNode n_foreign = mapnode_translate_from_internal(*this, version); @@ -282,16 +320,9 @@ void MapNode::serialize(u8 *dest, u8 version) dest[2] = n_foreign.param2; } } -void MapNode::deSerialize(u8 *source, u8 version) +void MapNode::deSerialize_pre22(u8 *source, u8 version) { - if(!ser_ver_supported(version)) - throw VersionMismatchException("ERROR: MapNode format not supported"); - - if(version == 0) - { - param0 = source[0]; - } - else if(version == 1) + if(version <= 1) { param0 = source[0]; } @@ -308,18 +339,11 @@ void MapNode::deSerialize(u8 *source, u8 version) } // Convert special values from old version to new - if(version <= 18) + if(version <= 19) { // In these versions, CONTENT_IGNORE and CONTENT_AIR // are 255 and 254 - if(param0 == 255) - param0 = CONTENT_IGNORE; - else if(param0 == 254) - param0 = CONTENT_AIR; - } - // version 19 is fucked up with sometimes the old values and sometimes not - if(version == 19) - { + // Version 19 is fucked up with sometimes the old values and sometimes not if(param0 == 255) param0 = CONTENT_IGNORE; else if(param0 == 254) @@ -330,6 +354,65 @@ void MapNode::deSerialize(u8 *source, u8 version) *this = mapnode_translate_to_internal(*this, version); } + +#ifndef SERVER + +/* + Nodes make a face if contents differ and solidness differs. + Return value: + 0: No face + 1: Face uses m1's content + 2: Face uses m2's content + equivalent: Whether the blocks share the same face (eg. water and glass) + + TODO: Add 3: Both faces drawn with backface culling, remove equivalent +*/ +u8 face_contents(content_t m1, content_t m2, bool *equivalent, + INodeDefManager *nodemgr) +{ + *equivalent = false; + + if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE) + return 0; + + bool contents_differ = (m1 != m2); + + const ContentFeatures &f1 = nodemgr->get(m1); + const ContentFeatures &f2 = nodemgr->get(m2); + + // Contents don't differ for different forms of same liquid + if(f1.sameLiquid(f2)) + contents_differ = false; + + u8 c1 = f1.solidness; + u8 c2 = f2.solidness; + + bool solidness_differs = (c1 != c2); + bool makes_face = contents_differ && solidness_differs; + + if(makes_face == false) + return 0; + + if(c1 == 0) + c1 = f1.visual_solidness; + if(c2 == 0) + c2 = f2.visual_solidness; + + if(c1 == c2){ + *equivalent = true; + // If same solidness, liquid takes precense + if(f1.isLiquid()) + return 1; + if(f2.isLiquid()) + return 2; + } + + if(c1 > c2) + return 1; + else + return 2; +} + /* Gets lighting value at face of node @@ -380,4 +463,5 @@ u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, } } +#endif diff --git a/src/mapnode.h b/src/mapnode.h index 65fc3b3e2..5e066604b 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -32,8 +32,8 @@ class INodeDefManager; - Tile = TileSpec at some side of a node of some content type Content ranges: - 0x000...0x07f: param2 is fully usable - 0x800...0xfff: param2 lower 4 bytes are free + 0x000...0x07f: param2 is fully usable + 0x800...0xfff: param2 lower 4 bits are free */ typedef u16 content_t; #define MAX_CONTENT 0xfff @@ -56,36 +56,6 @@ typedef u16 content_t; */ #define CONTENT_AIR 126 -#ifndef SERVER -/* - Nodes make a face if contents differ and solidness differs. - Return value: - 0: No face - 1: Face uses m1's content - 2: Face uses m2's content - equivalent: Whether the blocks share the same face (eg. water and glass) -*/ -u8 face_contents(content_t m1, content_t m2, bool *equivalent, - INodeDefManager *nodemgr); -#endif - -/* - Packs directions like (1,0,0), (1,-1,0) in six bits. - NOTE: This wastes way too much space for most purposes. -*/ -u8 packDir(v3s16 dir); -v3s16 unpackDir(u8 b); - -/* - facedir: CPT_FACEDIR_SIMPLE param1 value - dir: The face for which stuff is wanted - return value: The face from which the stuff is actually found - - NOTE: Currently this uses 2 bits for Z-,X-,Z+,X+, should there be Y+ - and Y- too? -*/ -v3s16 facedir_rotate(u8 facedir, v3s16 dir); - enum LightBank { LIGHTBANK_DAY, @@ -122,7 +92,6 @@ struct MapNode stored logarithmically from 0 to LIGHT_MAX. Sunlight is LIGHT_SUN, which is LIGHT_MAX+1. - Contains 2 values, day- and night lighting. Each takes 4 bits. - - Mineral content (should be removed from here) - Uhh... well, most blocks have light or nothing in here. */ u8 param1; @@ -210,40 +179,22 @@ struct MapNode void setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr); u8 getLight(enum LightBank bank, INodeDefManager *nodemgr) const; - u8 getLightBanksWithSource(INodeDefManager *nodemgr) const; + bool getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodemgr) const; // 0 <= daylight_factor <= 1000 // 0 <= return value <= LIGHT_SUN u8 getLightBlend(u32 daylight_factor, INodeDefManager *nodemgr) const { - u8 l = ((daylight_factor * getLight(LIGHTBANK_DAY, nodemgr) - + (1000-daylight_factor) * getLight(LIGHTBANK_NIGHT, nodemgr)) - )/1000; - u8 max = LIGHT_MAX; - if(getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN) - max = LIGHT_SUN; - if(l > max) - l = max; - return l; + u8 lightday = 0; + u8 lightnight = 0; + getLightBanks(lightday, lightnight, nodemgr); + return blend_light(daylight_factor, lightday, lightnight); } - /*// 0 <= daylight_factor <= 1000 - // 0 <= return value <= 255 - u8 getLightBlend(u32 daylight_factor, INodeDefManager *nodemgr) - { - u8 daylight = decode_light(getLight(LIGHTBANK_DAY, nodemgr)); - u8 nightlight = decode_light(getLight(LIGHTBANK_NIGHT, nodemgr)); - u8 mix = ((daylight_factor * daylight - + (1000-daylight_factor) * nightlight) - )/1000; - return mix; - }*/ - /* - Gets mineral content of node, if there is any. - MINERAL_NONE if doesn't contain or isn't able to contain mineral. - */ - u8 getMineral(INodeDefManager *nodemgr) const; - + u8 getFaceDir(INodeDefManager *nodemgr) const; + u8 getWallMounted(INodeDefManager *nodemgr) const; + v3s16 getWallMountedDir(INodeDefManager *nodemgr) const; + /* Serialization functions */ @@ -252,8 +203,44 @@ struct MapNode void serialize(u8 *dest, u8 version); void deSerialize(u8 *source, u8 version); + // Serializes or deserializes a list of nodes in bulk format (first the + // content of all nodes, then the param1 of all nodes, then the param2 + // of all nodes). + // version = serialization version. Must be >= 22 + // content_width = the number of bytes of content per node + // params_width = the number of bytes of params per node + // compressed = true to zlib-compress output + static void serializeBulk(std::ostream &os, int version, + const MapNode *nodes, u32 nodecount, + u8 content_width, u8 params_width, bool compressed); + static void deSerializeBulk(std::istream &is, int version, + MapNode *nodes, u32 nodecount, + u8 content_width, u8 params_width, bool compressed); + +private: + // Deprecated serialization methods + void serialize_pre22(u8 *dest, u8 version); + void deSerialize_pre22(u8 *source, u8 version); }; + +/* + MapNode helpers for mesh making stuff +*/ + +#ifndef SERVER + +/* + Nodes make a face if contents differ and solidness differs. + Return value: + 0: No face + 1: Face uses m1's content + 2: Face uses m2's content + equivalent: Whether the blocks share the same face (eg. water and glass) +*/ +u8 face_contents(content_t m1, content_t m2, bool *equivalent, + INodeDefManager *nodemgr); + /* Gets lighting value at face of node @@ -275,3 +262,6 @@ u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, #endif + +#endif + diff --git a/src/materials.cpp b/src/materials.cpp index d2d37ebf4..c37b7c505 100644 --- a/src/materials.cpp +++ b/src/materials.cpp @@ -18,9 +18,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 +46,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) { @@ -90,13 +137,6 @@ DiggingProperties getDiggingProperties(const MaterialProperties *mp, return getDiggingProperties(mp, tp, 1000000); } -DiggingProperties getDiggingProperties(u16 content, - const ToolDiggingProperties *tp, INodeDefManager *nodemgr) -{ - const MaterialProperties &mp = nodemgr->get(content).material; - return getDiggingProperties(&mp, tp); -} - HittingProperties getHittingProperties(const MaterialProperties *mp, const ToolDiggingProperties *tp, float time_from_last_punch) { @@ -111,3 +151,9 @@ HittingProperties getHittingProperties(const MaterialProperties *mp, return HittingProperties(hp, wear); } +HittingProperties getHittingProperties(const MaterialProperties *mp, + const ToolDiggingProperties *tp) +{ + return getHittingProperties(mp, tp, 1000000); +} + diff --git a/src/materials.h b/src/materials.h index b25d047be..058b2ab85 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,18 +110,12 @@ struct DiggingProperties {} }; -struct ToolDiggingProperties; -class INodeDefManager; - DiggingProperties getDiggingProperties(const MaterialProperties *mp, const ToolDiggingProperties *tp, float time_from_last_punch); DiggingProperties getDiggingProperties(const MaterialProperties *mp, const ToolDiggingProperties *tp); -DiggingProperties getDiggingProperties(u16 content, - const ToolDiggingProperties *tp, INodeDefManager *nodemgr); - struct HittingProperties { s16 hp; @@ -113,5 +130,8 @@ struct HittingProperties HittingProperties getHittingProperties(const MaterialProperties *mp, const ToolDiggingProperties *tp, float time_from_last_punch); +HittingProperties getHittingProperties(const MaterialProperties *mp, + const ToolDiggingProperties *tp); + #endif diff --git a/src/mesh.cpp b/src/mesh.cpp index c0b1419d4..44b3b9bbb 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 @@ -379,6 +389,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; } @@ -412,6 +429,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) @@ -459,3 +505,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 b22ccc318..631dc0cca 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. @@ -67,6 +68,11 @@ scene::IAnimatedMesh* createPlantMesh(v3f scale); 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); @@ -82,4 +88,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 deleted file mode 100644 index 038251fa3..000000000 --- a/src/mineral.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2010 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 "mineral.h" - - -const char *mineral_filenames[MINERAL_COUNT] = -{ - NULL, - "mineral_coal.png", - "mineral_iron.png" -}; - -std::string mineral_textures[MINERAL_COUNT]; - -void init_mineral() -{ - for(u32 i=0; i<MINERAL_COUNT; i++) - { - if(mineral_filenames[i] == NULL) - continue; - mineral_textures[i] = mineral_filenames[i]; - } -} - -std::string mineral_block_texture(u8 mineral) -{ - if(mineral >= MINERAL_COUNT) - return ""; - - return mineral_textures[mineral]; -} - - - diff --git a/src/mineral.h b/src/mineral.h deleted file mode 100644 index 4949fe07e..000000000 --- a/src/mineral.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2010 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 MINERAL_HEADER -#define MINERAL_HEADER - -#include "inventory.h" - -/* - Minerals - - Value is stored in the lowest 5 bits of a MapNode's CPT_MINERAL - type param. -*/ - -// Caches textures -void init_mineral(); - -#define MINERAL_NONE 0 -#define MINERAL_COAL 1 -#define MINERAL_IRON 2 - -#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; -} - -#endif - diff --git a/src/nameidmapping.h b/src/nameidmapping.h index 071599e10..238deb451 100644 --- a/src/nameidmapping.h +++ b/src/nameidmapping.h @@ -70,6 +70,9 @@ public: result = i->second; return true; } + u16 size() const{ + return m_id_to_name.size(); + } private: std::map<u16, std::string> m_id_to_name; std::map<std::string, u16> m_name_to_id; diff --git a/src/nodedef.cpp b/src/nodedef.cpp index d7769700b..0c2793a0e 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,10 +124,10 @@ 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; + param_type_2 = CPT2_NONE; is_ground_content = false; light_propagates = false; sunlight_propagates = false; @@ -139,11 +136,6 @@ void ContentFeatures::reset() diggable = true; climbable = false; buildable_to = false; - wall_mounted = false; - often_contains_mineral = false; - dug_item = ""; - extra_dug_item = ""; - extra_dug_item_rarity = 2; metadata_name = ""; liquid_type = LIQUID_NONE; liquid_alternative_flowing = ""; @@ -153,21 +145,22 @@ 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; + legacy_facedir_simple = false; + legacy_wallmounted = false; } 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); @@ -178,6 +171,7 @@ void ContentFeatures::serialize(std::ostream &os) writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); + writeU8(os, param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); @@ -186,11 +180,6 @@ void ContentFeatures::serialize(std::ostream &os) writeU8(os, diggable); 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); os<<serializeString(metadata_name); writeU8(os, liquid_type); os<<serializeString(liquid_alternative_flowing); @@ -200,15 +189,14 @@ 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); + writeU8(os, legacy_facedir_simple); + writeU8(os, legacy_wallmounted); } -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 +204,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++){ @@ -230,6 +216,7 @@ void ContentFeatures::deSerialize(std::istream &is, IGameDef *gamedef) post_effect_color.setGreen(readU8(is)); post_effect_color.setBlue(readU8(is)); param_type = (enum ContentParamType)readU8(is); + param_type_2 = (enum ContentParamType2)readU8(is); is_ground_content = readU8(is); light_propagates = readU8(is); sunlight_propagates = readU8(is); @@ -238,11 +225,6 @@ void ContentFeatures::deSerialize(std::istream &is, IGameDef *gamedef) diggable = readU8(is); 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); metadata_name = deSerializeString(is); liquid_type = (enum LiquidType)readU8(is); liquid_alternative_flowing = deSerializeString(is); @@ -252,53 +234,8 @@ 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; + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); } /* @@ -311,14 +248,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 +271,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,13 +289,13 @@ 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 content_t getFreeId(bool require_full_param2) { - // If allowed, first search in the large 4-byte-param2 pool + // If allowed, first search in the large 4-bit-param2 pool if(!require_full_param2){ for(u16 i=0x800; i<=0xfff; i++){ const ContentFeatures &f = m_content_features[i]; @@ -368,7 +303,7 @@ public: return i; } } - // Then search from the small 8-byte-param2 pool + // Then search from the small 8-bit-param2 pool for(u16 i=0; i<=125; i++){ const ContentFeatures &f = m_content_features[i]; if(f.name == "") @@ -401,12 +336,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 +357,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,37 +380,29 @@ 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 = ( - def.liquid_type == LIQUID_FLOWING + def.param_type_2 == CPT2_FULL || - def.drawtype == NDT_FLOWINGLIQUID + def.param_type_2 == CPT2_FLOWINGLIQUID || - def.drawtype == NDT_TORCHLIKE - || - def.drawtype == NDT_SIGNLIKE + def.legacy_wallmounted ); // Get some id id = getFreeId(require_full_param2); 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 +412,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 +448,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 +500,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 +513,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 +574,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 +591,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..9524385cf 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; @@ -36,9 +37,19 @@ enum ContentParamType { CPT_NONE, CPT_LIGHT, - CPT_MINERAL, +}; + +enum ContentParamType2 +{ + CPT2_NONE, + // Need 8-bit param2 + CPT2_FULL, + // Flowing liquid properties + CPT2_FLOWINGLIQUID, // Direction for chests and furnaces and such - CPT_FACEDIR_SIMPLE + CPT2_FACEDIR, + // Direction for signs, torches and such + CPT2_WALLMOUNTED, }; enum LiquidType @@ -52,7 +63,7 @@ enum NodeBoxType { NODEBOX_REGULAR, // Regular block; allows buildable_to NODEBOX_FIXED, // Static separately defined box - NODEBOX_WALLMOUNTED, // Box for wall_mounted nodes; (top, bottom, side) + NODEBOX_WALLMOUNTED, // Box for wall mounted nodes; (top, bottom, side) }; struct NodeBox @@ -124,7 +135,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 +143,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 +154,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; @@ -156,6 +161,8 @@ struct ContentFeatures video::SColor post_effect_color; // Type of MapNode::param1 ContentParamType param_type; + // Type of MapNode::param2 + ContentParamType2 param_type_2; // True for all ground-like things like stone and mud, false for eg. trees bool is_ground_content; bool light_propagates; @@ -171,20 +178,6 @@ struct ContentFeatures bool climbable; // Player can build on these bool 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 - 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; - // Extra dug item and its rarity - std::string extra_dug_item; - // Usual get interval for extra dug item - s32 extra_dug_item_rarity; // Metadata name of node (eg. "furnace") std::string metadata_name; // Whether the node is non-liquid, source liquid or flowing liquid @@ -202,9 +195,11 @@ struct ContentFeatures u32 damage_per_second; NodeBox selection_box; MaterialProperties material; - std::string cookresult_item; - float furnace_cooktime; - float furnace_burntime; + // Compatibility with old maps + // Set to true if paramtype used to be 'facedir_simple' + bool legacy_facedir_simple; + // Set to true if wall_mounted used to be set to true + bool legacy_wallmounted; /* Methods @@ -214,23 +209,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 +234,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 +251,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 +260,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 +274,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..6506c43c3 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,12 @@ 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,16 +58,12 @@ Player::~Player() delete inventory_backup; } -void Player::wieldItem(u16 item) -{ - m_selected_item = item; -} - void Player::resetInventory() { inventory.clear(); inventory.addList("main", PLAYER_INVENTORY_SIZE); inventory.addList("craft", 9); + inventory.addList("craftpreview", 1); inventory.addList("craftresult", 1); } @@ -123,7 +119,6 @@ void Player::serialize(std::ostream &os) args.setFloat("pitch", m_pitch); args.setFloat("yaw", m_yaw); args.setV3F("position", m_position); - args.setBool("craftresult_is_preview", craftresult_is_preview); args.setS32("hp", hp); args.writeLines(os); @@ -162,17 +157,27 @@ void Player::deSerialize(std::istream &is) setYaw(args.getFloat("yaw")); setPosition(args.getV3F("position")); try{ - craftresult_is_preview = args.getBool("craftresult_is_preview"); - }catch(SettingNotFoundException &e){ - craftresult_is_preview = true; - } - try{ hp = args.getS32("hp"); }catch(SettingNotFoundException &e){ hp = 20; } - inventory.deSerialize(is, m_gamedef); + inventory.deSerialize(is); + + if(inventory.getList("craftpreview") == NULL) + { + // Convert players without craftpreview + inventory.addList("craftpreview", 1); + + bool craftresult_is_preview = true; + if(args.exists("craftresult_is_preview")) + craftresult_is_preview = args.getBool("craftresult_is_preview"); + if(craftresult_is_preview) + { + // Clear craftresult + inventory.getList("craftresult")->changeItem(0, ItemStack()); + } + } } #ifndef SERVER diff --git a/src/player.h b/src/player.h index 1c9dde7e0..d62fb6111 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; +struct 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; } @@ -164,8 +155,6 @@ public: // Actual inventory is backed up here when creative mode is used Inventory *inventory_backup; - bool craftresult_is_preview; - u16 hp; u16 peer_id; @@ -174,7 +163,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/profiler.h b/src/profiler.h index 129118ef6..7bb3b3750 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -100,11 +100,30 @@ public: void print(std::ostream &o) { + printPage(o, 1, 1); + } + + void printPage(std::ostream &o, u32 page, u32 pagecount) + { JMutexAutoLock lock(m_mutex); + + u32 minindex, maxindex; + paging(m_data.size(), page, pagecount, minindex, maxindex); + for(core::map<std::string, float>::Iterator i = m_data.getIterator(); i.atEnd() == false; i++) { + if(maxindex == 0) + break; + maxindex--; + + if(minindex != 0) + { + minindex--; + continue; + } + std::string name = i.getNode()->getKey(); int avgcount = 1; core::map<std::string, int>::Node *n = m_avgcounts.find(name); diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 39a3b06ff..23063a3c8 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" @@ -128,46 +127,308 @@ 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 setintfield(lua_State *L, int table, + const char *fieldname, int value) +{ + lua_pushinteger(L, value); + if(table < 0) + table -= 1; + lua_setfield(L, table, fieldname); +} + +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 setboolfield(lua_State *L, int table, + const char *fieldname, bool value) +{ + lua_pushboolean(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"}, + {0, NULL}, +}; + +struct EnumString es_ContentParamType2[] = +{ + {CPT2_NONE, "none"}, + {CPT2_FULL, "full"}, + {CPT2_FLOWINGLIQUID, "flowingliquid"}, + {CPT2_FACEDIR, "facedir"}, + {CPT2_WALLMOUNTED, "wallmounted"}, + {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); @@ -205,20 +466,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; @@ -366,170 +613,376 @@ 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) +/* + MaterialProperties +*/ + +static MaterialProperties read_material_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; + MaterialProperties prop; + prop.diggability = (Diggability)getenumfield(L, -1, "diggability", + es_Diggability, DIGGABLE_NORMAL); + getfloatfield(L, -1, "constant_time", prop.constant_time); + getfloatfield(L, -1, "weight", prop.weight); + getfloatfield(L, -1, "crackiness", prop.crackiness); + getfloatfield(L, -1, "crumbliness", prop.crumbliness); + getfloatfield(L, -1, "cuttability", prop.cuttability); + getfloatfield(L, -1, "flammability", prop.flammability); + return prop; } -static bool getintfield(lua_State *L, int table, - const char *fieldname, int &result) +/* + ToolDiggingProperties +*/ + +static ToolDiggingProperties read_tool_digging_properties( + lua_State *L, int table) { - 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; + 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 getfloatfield(lua_State *L, int table, - const char *fieldname, float &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 getboolfield(lua_State *L, int table, - const char *fieldname, bool &result) +static void push_tool_digging_properties(lua_State *L, + const ToolDiggingProperties &prop) { - 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; + lua_newtable(L); + set_tool_digging_properties(L, -1, prop); } -static std::string checkstringfield(lua_State *L, int table, - const char *fieldname) +/* + DiggingProperties +*/ + +static void set_digging_properties(lua_State *L, int table, + const DiggingProperties &prop) { - lua_getfield(L, table, fieldname); - std::string s = luaL_checkstring(L, -1); - lua_pop(L, 1); - return s; + setboolfield(L, table, "diggable", prop.diggable); + setfloatfield(L, table, "time", prop.time); + setintfield(L, table, "wear", prop.wear); } -static std::string getstringfield_default(lua_State *L, int table, - const char *fieldname, const std::string &default_) +static void push_digging_properties(lua_State *L, + const DiggingProperties &prop) { - std::string result = default_; - getstringfield(L, table, fieldname, result); - return result; + lua_newtable(L); + set_digging_properties(L, -1, prop); } -static int getintfield_default(lua_State *L, int table, - const char *fieldname, int default_) +/* + HittingProperties +*/ + +static void set_hitting_properties(lua_State *L, int table, + const HittingProperties &prop) { - int result = default_; - getintfield(L, table, fieldname, result); - return result; + setintfield(L, table, "hp", prop.hp); + setintfield(L, table, "wear", prop.wear); } -/*static float getfloatfield_default(lua_State *L, int table, - const char *fieldname, float default_) +static void push_hitting_properties(lua_State *L, + const HittingProperties &prop) { - float result = default_; - getfloatfield(L, table, fieldname, result); - return result; -}*/ + lua_newtable(L); + set_hitting_properties(L, -1, prop); +} -static bool getboolfield_default(lua_State *L, int table, - const char *fieldname, bool default_) +/* + PointedThing +*/ + +static void push_pointed_thing(lua_State *L, const PointedThing& pointed) { - bool result = default_; - getboolfield(L, table, fieldname, result); - return result; + 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"); + } } -struct EnumString -{ - int num; - const char *str; -}; +/* + ItemDefinition +*/ -static bool string_to_enum(const EnumString *spec, int &result, - const std::string &str) +static ItemDefinition read_item_definition(lua_State *L, int index) { - const EnumString *esp = spec; - while(esp->str){ - if(str == std::string(esp->str)){ - result = esp->num; - return true; - } - esp++; + 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); } - return false; + lua_pop(L, 1); + + def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max); + if(def.stack_max == 0) + def.stack_max = 1; + + lua_getfield(L, index, "on_use"); + def.usable = lua_isfunction(L, -1); + lua_pop(L, 1); + + getboolfield(L, index, "liquids_pointable", def.liquids_pointable); + + lua_getfield(L, index, "tool_digging_properties"); + if(lua_istable(L, -1)){ + def.tool_digging_properties = new ToolDiggingProperties( + read_tool_digging_properties(L, -1)); + } + lua_pop(L, 1); + + // If name is "" (hand), ensure there are ToolDiggingProperties + // because it will be looked up there whenever any other item has + // no ToolDiggingProperties + if(def.name == "" && def.tool_digging_properties == NULL){ + def.tool_digging_properties = new ToolDiggingProperties(); + } + + return def; } -/*static bool enum_to_string(const EnumString *spec, std::string &result, - int num) +/* + ContentFeatures +*/ + +static ContentFeatures read_content_features(lua_State *L, int index) { - const EnumString *esp = spec; - while(esp){ - if(num == esp->num){ - result = esp->str; - 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 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; -} + lua_getfield(L, index, "special_materials"); + if(lua_istable(L, -1)){ + int table = lua_gettop(L); + lua_pushnil(L); + int i = 0; + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + int smtable = lua_gettop(L); + std::string tname = getstringfield_default( + L, smtable, "image", ""); + bool backface_culling = getboolfield_default( + L, smtable, "backface_culling", true); + MaterialSpec mspec(tname, backface_culling); + f.mspec_special[i] = mspec; + // removes value, keeps key for next iteration + lua_pop(L, 1); + i++; + if(i==6){ + lua_pop(L, 1); + break; + } + } + } + lua_pop(L, 1); -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); -} + f.alpha = getintfield_default(L, index, "alpha", 255); -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; + /* Other stuff */ + + lua_getfield(L, index, "post_effect_color"); + if(!lua_isnil(L, -1)) + f.post_effect_color = readARGB8(L, -1); + lua_pop(L, 1); + + f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", + es_ContentParamType, CPT_NONE); + f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", + es_ContentParamType2, CPT2_NONE); + + // Warn about some deprecated fields + warn_if_field_exists(L, index, "wall_mounted", + "deprecated: use paramtype2 = 'wallmounted'"); + warn_if_field_exists(L, index, "light_propagates", + "deprecated: determined from paramtype"); + warn_if_field_exists(L, index, "dug_item", + "deprecated: use 'drop' field"); + warn_if_field_exists(L, index, "extra_dug_item", + "deprecated: use 'drop' field"); + warn_if_field_exists(L, index, "extra_dug_item_rarity", + "deprecated: use 'drop' field"); + + // 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); + 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); + // 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 = read_material_properties(L, -1); + } + lua_pop(L, 1); + + // Set to true if paramtype used to be 'facedir_simple' + getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); + // Set to true if wall_mounted used to be set to true + getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); + + 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 @@ -538,39 +991,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, @@ -589,375 +1033,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); @@ -982,19 +1368,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 */ @@ -1029,15 +1479,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 @@ -1089,39 +1530,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; } @@ -1144,57 +1581,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; } @@ -1267,8 +1726,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} }; @@ -1395,59 +1856,56 @@ private: return 1; } - /* IGenericNodeMetadata interface */ - - // set_infotext(self, text) - static int l_set_infotext(lua_State *L) + // set_owner(self, string) + static int l_set_owner(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); NodeMetadata *meta = getmeta(ref); if(meta == NULL) return 0; // Do it - std::string text = luaL_checkstring(L, 2); - meta->setInfoText(text); + std::string owner = luaL_checkstring(L, 2); + meta->setOwner(owner); reportMetadataChange(ref); - return 0; + return 1; } - // get_inventory(self) - static int l_get_inventory(lua_State *L) + // get_allow_removal(self) + static int l_get_allow_removal(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); NodeMetadata *meta = getmeta(ref); - if(meta == NULL) return 0; + if(meta == NULL){ + lua_pushboolean(L, true); + return 1; + } // Do it - InvRef::createNodeMeta(L, ref->m_p); + lua_pushboolean(L, !meta->nodeRemovalDisabled()); return 1; } - // deprecated: inventory_set_list(self, name, {item1, item2, ...}) - static int l_inventory_set_list(lua_State *L) + /* IGenericNodeMetadata interface */ + + // set_infotext(self, text) + static int l_set_infotext(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()); + std::string text = luaL_checkstring(L, 2); + meta->setInfoText(text); reportMetadataChange(ref); return 0; } - // deprecated: inventory_get_list(self, name) - static int l_inventory_get_list(lua_State *L) + // get_inventory(self) + static int l_get_inventory(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); + InvRef::createNodeMeta(L, ref->m_p); return 1; } @@ -1635,10 +2093,10 @@ const luaL_reg NodeMetaRef::methods[] = { method(NodeMetaRef, set_text), method(NodeMetaRef, get_text), method(NodeMetaRef, get_owner), + method(NodeMetaRef, set_owner), + method(NodeMetaRef, get_allow_removal), 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), @@ -1799,122 +2257,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; } @@ -2071,73 +2505,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) { @@ -2300,12 +2667,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), @@ -2319,11 +2687,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), @@ -2467,7 +2830,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) { @@ -2490,22 +2853,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) @@ -2631,6 +3001,38 @@ private: return 1; } + // EnvRef:set_timeofday(val) + // val = 0...1 + static int l_set_timeofday(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // Do it + float timeofday_f = luaL_checknumber(L, 2); + assert(timeofday_f >= 0.0 && timeofday_f <= 1.0); + int timeofday_mh = (int)(timeofday_f * 24000.0); + // This should be set directly in the environment but currently + // such changes aren't immediately sent to the clients, so call + // the server instead. + //env->setTimeOfDay(timeofday_mh); + get_server(L)->setTimeOfDay(timeofday_mh); + return 0; + } + + // EnvRef:get_timeofday() -> 0...1 + static int l_get_timeofday(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // Do it + int timeofday_mh = env->getTimeOfDay(); + float timeofday_f = (float)timeofday_mh / 24000.0; + lua_pushnumber(L, timeofday_f); + return 1; + } + static int gc_object(lua_State *L) { EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1)); delete o; @@ -2709,6 +3111,8 @@ const luaL_reg EnvRef::methods[] = { method(EnvRef, get_player_by_name), method(EnvRef, get_objects_inside_radius), method(EnvRef, get_nodes_inside_radius), + method(EnvRef, set_timeofday), + method(EnvRef, get_timeofday), {0,0} }; @@ -2716,55 +3120,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: @@ -2838,454 +3193,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 */ + return width != 0; } -// alias_node(name, convert_to_name) -static int l_alias_node(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); - - // 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 */ -} - -// alias_tool(name, convert_to_name) -static int l_alias_tool(lua_State *L) -{ - 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 */ } @@ -3383,15 +3602,45 @@ static int l_get_inventory(lua_State *L) return 1; } +// get_digging_properties(material_properties, tool_digging_properties[, time_from_last_punch]) +static int l_get_digging_properties(lua_State *L) +{ + MaterialProperties mp = read_material_properties(L, 1); + ToolDiggingProperties tp = read_tool_digging_properties(L, 2); + if(lua_isnoneornil(L, 3)) + push_digging_properties(L, getDiggingProperties(&mp, &tp)); + else + push_digging_properties(L, getDiggingProperties(&mp, &tp, + luaL_checknumber(L, 3))); + return 1; +} + +// get_hitting_properties(material_properties, tool_digging_properties[, time_from_last_punch]) +static int l_get_hitting_properties(lua_State *L) +{ + MaterialProperties mp = read_material_properties(L, 1); + ToolDiggingProperties tp = read_tool_digging_properties(L, 2); + if(lua_isnoneornil(L, 3)) + push_hitting_properties(L, getHittingProperties(&mp, &tp)); + else + push_hitting_properties(L, getHittingProperties(&mp, &tp, + luaL_checknumber(L, 3))); + 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; @@ -3401,35 +3650,25 @@ 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_digging_properties", l_get_digging_properties}, + {"get_hitting_properties", l_get_hitting_properties}, + {"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 */ @@ -3444,10 +3683,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); @@ -3459,30 +3694,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); @@ -3763,6 +3980,8 @@ bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player) assert(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); + dstream<<"player: "<<player<<" id: "<<player->getId()<<std::endl; + bool positioning_handled_by_some = false; // Get minetest.registered_on_respawnplayers @@ -3794,62 +4013,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 and node 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); @@ -3857,7 +4036,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; } @@ -3875,193 +4054,137 @@ 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; -/* - environment -*/ + // 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; +} -void scriptapi_environment_step(lua_State *L, float dtime) +bool scriptapi_node_on_punch(lua_State *L, v3s16 pos, MapNode node, + ServerActiveObject *puncher) { realitycheck(L); assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_environment_step"<<std::endl; StackUnroller stack_unroller(L); - // Get minetest.registered_globalsteps - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_globalsteps"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - lua_pushnumber(L, dtime); - if(lua_pcall(L, 1, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - // value removed, keep key for next iteration - } -} + INodeDefManager *ndef = get_server(L)->ndef(); -void scriptapi_environment_on_placenode(lua_State *L, v3s16 p, MapNode newnode, - ServerActiveObject *placer) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_environment_on_placenode"<<std::endl; - StackUnroller stack_unroller(L); + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_punch")) + return false; - // Get the writable node definition manager from the server - IWritableNodeDefManager *ndef = - get_server(L)->getWritableNodeDefManager(); - - // Get minetest.registered_on_placenodes - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_placenodes"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - push_v3s16(L, p); - pushnode(L, newnode, ndef); - objectref_get_or_create(L, placer); - if(lua_pcall(L, 3, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - // value removed, keep key for next iteration - } + // Call function + push_v3s16(L, pos); + pushnode(L, node, ndef); + objectref_get_or_create(L, puncher); + if(lua_pcall(L, 3, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return true; } -void scriptapi_environment_on_dignode(lua_State *L, v3s16 p, MapNode oldnode, +bool scriptapi_node_on_dig(lua_State *L, v3s16 pos, MapNode node, ServerActiveObject *digger) { realitycheck(L); assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_environment_on_dignode"<<std::endl; StackUnroller stack_unroller(L); - // Get the writable node definition manager from the server - IWritableNodeDefManager *ndef = - get_server(L)->getWritableNodeDefManager(); - - // Get minetest.registered_on_dignodes - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_dignodes"); - luaL_checktype(L, -1, LUA_TTABLE); - int table = lua_gettop(L); - // Foreach - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - push_v3s16(L, p); - pushnode(L, oldnode, ndef); - objectref_get_or_create(L, digger); - if(lua_pcall(L, 3, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - // value removed, keep key for next iteration - } + INodeDefManager *ndef = get_server(L)->ndef(); + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_dig")) + return false; + + // Call function + push_v3s16(L, pos); + pushnode(L, node, ndef); + objectref_get_or_create(L, digger); + if(lua_pcall(L, 3, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return true; } -void scriptapi_environment_on_punchnode(lua_State *L, v3s16 p, MapNode node, - ServerActiveObject *puncher) +/* + environment +*/ + +void scriptapi_environment_step(lua_State *L, float dtime) { realitycheck(L); assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_environment_on_punchnode"<<std::endl; + //infostream<<"scriptapi_environment_step"<<std::endl; StackUnroller stack_unroller(L); - // Get the writable node definition manager from the server - IWritableNodeDefManager *ndef = - get_server(L)->getWritableNodeDefManager(); - - // Get minetest.registered_on_punchnodes + // Get minetest.registered_globalsteps lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_punchnodes"); + lua_getfield(L, -1, "registered_globalsteps"); luaL_checktype(L, -1, LUA_TTABLE); int table = lua_gettop(L); // Foreach @@ -4070,10 +4193,8 @@ void scriptapi_environment_on_punchnode(lua_State *L, v3s16 p, MapNode node, // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TFUNCTION); // Call function - push_v3s16(L, p); - pushnode(L, node, ndef); - objectref_get_or_create(L, puncher); - if(lua_pcall(L, 3, 0, 0)) + lua_pushnumber(L, dtime); + if(lua_pcall(L, 1, 0, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); // value removed, keep key for next iteration } diff --git a/src/scriptapi.h b/src/scriptapi.h index af8afa3d9..df8ae344e 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; +struct 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, @@ -48,15 +49,6 @@ bool scriptapi_on_chat_message(lua_State *L, const std::string &name, /* environment */ // On environment step void scriptapi_environment_step(lua_State *L, float dtime); -// After adding node -void scriptapi_environment_on_placenode(lua_State *L, v3s16 p, MapNode newnode, - ServerActiveObject *placer); -// After removing node -void scriptapi_environment_on_dignode(lua_State *L, v3s16 p, MapNode oldnode, - ServerActiveObject *digger); -// When punching node -void scriptapi_environment_on_punchnode(lua_State *L, v3s16 p, MapNode node, - ServerActiveObject *puncher); // After generating a piece of map void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp); @@ -66,17 +58,19 @@ 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); + +/* node callbacks */ +bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node, + ServerActiveObject *puncher); +bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node, + ServerActiveObject *digger); /* luaentity */ // Returns true if succesfully added into Lua; false otherwise. diff --git a/src/serialization.h b/src/serialization.h index cc4381155..bcfea451a 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -57,11 +57,12 @@ with this program; if not, write to the Free Software Foundation, Inc., 19: new content type handling 20: many existing content types translated to extended ones 21: dynamic content type allocation + 22: full 16-bit content types, minerals removed, facedir & wallmounted changed */ // This represents an uninitialized or invalid format #define SER_FMT_VER_INVALID 255 // Highest supported serialization version -#define SER_FMT_VER_HIGHEST 21 +#define SER_FMT_VER_HIGHEST 22 // Lowest supported serialization version #define SER_FMT_VER_LOWEST 0 diff --git a/src/server.cpp b/src/server.cpp index d704bf861..a0c8a0092 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "voxel.h" #include "materials.h" -#include "mineral.h" #include "config.h" #include "servercommand.h" #include "filesys.h" @@ -42,9 +41,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 +851,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 +931,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 +1042,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 +2105,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 +2358,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,179 +2371,92 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<std::endl; return; } - // Create context - InventoryContext c; - c.current_player = player; + + /* + Note: Always set inventory not sent, to repair cases + where the client made a bad prediction. + */ /* 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); - InventoryList *clist = player->inventory.getList("craft"); - assert(clist); - InventoryList *mlist = player->inventory.getList("main"); - assert(mlist); - IMoveAction *ma = (IMoveAction*)a; + ma->from_inv.applyCurrentPlayer(player->getName()); + ma->to_inv.applyCurrentPlayer(player->getName()); + + setInventoryModified(ma->from_inv); + setInventoryModified(ma->to_inv); + + 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 + Disable moving items out of craftpreview */ - if(ma->to_inv == "current_player" - && ma->to_list == "craftresult" - && (ma->from_inv != "current_player" - || ma->from_list != "craftresult")) + if(ma->from_list == "craftpreview") { infostream<<"Ignoring IMoveAction from " - <<ma->from_inv<<":"<<ma->from_list - <<" to "<<ma->to_inv<<":"<<ma->to_list - <<" because dst is craftresult" - <<" and src isn't craftresult"<<std::endl; + <<(ma->from_inv.dump())<<":"<<ma->from_list + <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list + <<" because src is "<<ma->from_list<<std::endl; delete a; return; } /* - Handle crafting (source is craftresult, which is preview) + Disable moving items into craftresult and craftpreview */ - if(ma->from_inv == "current_player" - && ma->from_list == "craftresult" - && player->craftresult_is_preview) + if(ma->to_list == "craftpreview" || ma->to_list == "craftresult") { - /* - If the craftresult is placed on itself, crafting takes - place and result is moved into main list - */ - if(ma->to_inv == "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); - - 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. - */ - 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; - } - }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); - + infostream<<"Ignoring IMoveAction from " + <<(ma->from_inv.dump())<<":"<<ma->from_list + <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list + <<" because dst is "<<ma->to_list<<std::endl; delete a; return; } - /* - Non-crafting move - */ - // 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 +2466,72 @@ 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()); + + setInventoryModified(da->from_inv); + + // 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; + } + } + } + /* + Handle restrictions and special cases of the craft action + */ + else if(a->getType() == IACTION_CRAFT) + { + ICraftAction *ca = (ICraftAction*)a; + + ca->craft_inv.applyCurrentPlayer(player->getName()); + + setInventoryModified(ca->craft_inv); + + //bool craft_inv_is_current_player = + // (ca->craft_inv.type == InventoryLocation::PLAYER) && + // (ca->craft_inv.name == player->getName()); + + // Disallow crafting if not allowed to interact + if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) + { + infostream<<"Cannot craft: " + <<"No interact privilege"<<std::endl; + delete a; + return; + } + + // If player is not an admin, check for ownership of inventory + if((getPlayerPrivs(player) & PRIV_SERVER) == 0) + { + std::string owner_craft = getInventoryOwner(ca->craft_inv); + if(owner_craft != "" && owner_craft != player->getName()) + { + infostream<<"WARNING: "<<player->getName() + <<" tried to access an inventory that" + <<" belongs to "<<owner_craft<<std::endl; + delete a; + return; } } } // Do the action - a->apply(&c, this, m_env); + a->apply(this, srp, this); // Eat the action delete a; } @@ -2809,8 +2743,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 +2814,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 +2838,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 +2867,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,13 +2877,12 @@ 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) + if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) { infostream<<"Ignoring interaction from player "<<player->getName() <<" because privileges are "<<getPlayerPrivs(player) <<std::endl; - // NOTE: no return; here, fall through + return; } /* @@ -2956,10 +2896,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; - MapNode n(CONTENT_IGNORE); - try { n = m_env->getMap().getNode(p_under); @@ -2971,22 +2908,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<std::endl; m_emerge_queue.addBlock(peer_id, getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK); - cannot_punch_node = true; } - - if(cannot_punch_node) - return; - - /* - Run script hook - */ - scriptapi_environment_on_punchnode(m_lua, p_under, n, srp); + if(n.getContent() != CONTENT_IGNORE) + scriptapi_node_on_punch(m_lua, p_under, n, srp); } else if(pointed.type == POINTEDTHING_OBJECT) { - if(!build_priv) - return; - // Skip if object has been removed if(pointed_object->m_removed) return; @@ -3014,211 +2941,24 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) else if(action == 2) { // Only complete digging of nodes - if(pointed.type != POINTEDTHING_NODE) - return; - - // Mandatory parameter; actually used for nothing - core::map<v3s16, MapBlock*> modified_blocks; - - content_t material = CONTENT_IGNORE; - u8 mineral = MINERAL_NONE; - - bool cannot_remove_node = !build_priv; - - MapNode n(CONTENT_IGNORE); - try - { - n = m_env->getMap().getNode(p_under); - // Get mineral - mineral = n.getMineral(m_nodedef); - // Get material at position - material = n.getContent(); - // If not yet cancelled - if(cannot_remove_node == false) - { - // If it's not diggable, do nothing - if(m_nodedef->get(material).diggable == false) - { - infostream<<"Server: Not finishing digging: " - <<"Node not diggable" - <<std::endl; - cannot_remove_node = true; - } - } - // If not yet cancelled - if(cannot_remove_node == false) - { - // Get node metadata - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under); - if(meta && meta->nodeRemovalDisabled() == true) - { - infostream<<"Server: Not finishing digging: " - <<"Node metadata disables removal" - <<std::endl; - cannot_remove_node = true; - } - } - } - catch(InvalidPositionException &e) - { - infostream<<"Server: Not finishing digging: Node not found." - <<" Adding block to emerge queue." - <<std::endl; - m_emerge_queue.addBlock(peer_id, - getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK); - cannot_remove_node = true; - } - - /* - If node can't be removed, set block to be re-sent to - client and quit. - */ - if(cannot_remove_node) - { - infostream<<"Server: Not finishing digging."<<std::endl; - - // Client probably has wrong data. - // Set block not sent, so that client will get - // a valid one. - infostream<<"Client "<<peer_id<<" tried to dig " - <<"node; but node cannot be removed." - <<" setting MapBlock not sent."<<std::endl; - RemoteClient *client = getClient(peer_id); - v3s16 blockpos = getNodeBlockPos(p_under); - client->SetBlockNotSent(blockpos); - - return; - } - - actionstream<<player->getName()<<" digs "<<PP(p_under) - <<", gets material "<<(int)material<<", mineral " - <<(int)mineral<<std::endl; - - /* - Send the removal to all close-by players. - - If other player is close, send REMOVENODE - - Otherwise set blocks not sent - */ - core::list<u16> far_players; - sendRemoveNode(p_under, peer_id, &far_players, 30); - - /* - Update and send inventory - */ - - if(g_settings->getBool("creative_mode") == false) + if(pointed.type == POINTEDTHING_NODE) { - /* - Wear out tool - */ - 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; - } - } - - /* - Add dug item to inventory - */ - - InventoryItem *item = NULL; - - if(mineral != MINERAL_NONE) - item = getDiggedMineralItem(mineral, this); - - // If not mineral - if(item == NULL) - { - 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); - } - } - - if(item != NULL) - { - // Add a item to inventory - player->inventory.addItem("main", item); - srp->m_inventory_not_sent = true; - } - - item = NULL; - + MapNode n(CONTENT_IGNORE); + try { - const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item; - s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity; - 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); - } + n = m_env->getMap().getNode(p_under); } - - if(item != NULL) + catch(InvalidPositionException &e) { - // Add a item to inventory - player->inventory.addItem("main", item); - srp->m_inventory_not_sent = true; + infostream<<"Server: Not finishing digging: Node not found." + <<" Adding block to emerge queue." + <<std::endl; + m_emerge_queue.addBlock(peer_id, + getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK); } + if(n.getContent() != CONTENT_IGNORE) + scriptapi_node_on_dig(m_lua, p_under, n, srp); } - - /* - Remove the node - (this takes some time so it is done after the quick stuff) - */ - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); - - m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks); - } - /* - 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_dignode(m_lua, p_under, n, srp); } // action == 2 /* @@ -3226,233 +2966,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ else if(action == 3) { - if(pointed.type == POINTEDTHING_NODE) - { - 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; - - /* - 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; - } + // Reset build time counter + if(pointed.type == POINTEDTHING_NODE && + item.getDefinition(m_itemdef).type == ITEM_NODE) + getClient(peer_id)->m_time_from_building = 0.0; - // 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); - - // 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; - } - } - - /* - 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. - - 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); - - 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); - - /* - 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 - { - if(!build_priv) - { - infostream<<"Not allowing player to place item: " - "no build privileges"<<std::endl; - return; - } - - // 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(); - - // 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; - } - - //pos.Y -= BS*0.45; - //pos.Y -= BS*0.25; // let it drop a bit - - /* - 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 "<<item->getName() - <<" at "<<PP(pos)<<std::endl; - - /* - 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; - } - } - } - } - else if(pointed.type == POINTEDTHING_OBJECT) + if(pointed.type == POINTEDTHING_OBJECT) { // Right click object - if(!build_priv) - return; - // Skip if object has been removed if(pointed_object->m_removed) return; @@ -3463,6 +2987,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Do stuff pointed_object->rightClick(srp); } + else if(scriptapi_item_on_place(m_lua, + item, srp, pointed)) + { + // Placement was handled in lua + + // Apply returned ItemStack + if(g_settings->getBool("creative_mode") == false) + srp->setWieldedItem(item); + } } // action == 3 @@ -3471,38 +3004,17 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ else if(action == 4) { - InventoryList *ilist = player->inventory.getList("main"); - if(ilist == NULL) - return; + ItemStack item = srp->getWieldedItem(); - // 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) - { - infostream<<"Not allowing player to use item: " - "no build privileges"<<std::endl; - return; - } - - actionstream<<player->getName()<<" uses "<<item->getName() + 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 +3027,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 +3058,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 +3082,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 +3143,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 +3264,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 +3273,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 +3300,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 +3318,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 +3351,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 +3384,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 @@ -4167,7 +3619,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) */ std::ostringstream os(std::ios_base::binary); - block->serialize(os, ver); + block->serialize(os, ver, false); std::string s = os.str(); SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size()); @@ -4582,63 +4034,18 @@ void Server::UpdateCrafting(u16 peer_id) Player* player = m_env->getPlayer(peer_id); assert(player); - ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player); + // Get a preview for crafting + ItemStack preview; // No crafting in creative mode - if(g_settings->getBool("creative_mode")) - return; - - // Get the InventoryLists of the player in which we will operate - InventoryList *clist = player->inventory.getList("craft"); - assert(clist); - InventoryList *rlist = player->inventory.getList("craftresult"); - assert(rlist); - InventoryList *mlist = player->inventory.getList("main"); - assert(mlist); - - // If the result list is not a preview and is not empty, try to - // throw the item into main list - if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0) - { - // Grab item out of craftresult - InventoryItem *item = rlist->changeItem(0, NULL); - // 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); - // Inventory was modified - srp->m_inventory_not_sent = true; - } - - // If result list is empty, we will make it preview what would be - // crafted - 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); - - // Find out what is crafted and add it to result item slot - InventoryItem *result = m_craftdef->getCraftResult(cpi, this); - if(result) - rlist->addItem(result); - } + if(g_settings->getBool("creative_mode") == false) + getCraftingResult(&player->inventory, preview, false, this); + + // Put the new preview in + InventoryList *plist = player->inventory.getList("craftpreview"); + assert(plist); + assert(plist->getSize() >= 1); + plist->changeItem(0, preview); } RemoteClient* Server::getClient(u16 peer_id) @@ -4734,9 +4141,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 +4153,6 @@ ICraftDefManager* Server::getCraftDefManager() { return m_craftdef; } -ICraftItemDefManager* Server::getCraftItemDefManager() -{ - return m_craftitemdef; -} ITextureSource* Server::getTextureSource() { return NULL; @@ -4759,9 +4162,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 +4174,6 @@ IWritableCraftDefManager* Server::getWritableCraftDefManager() { return m_craftdef; } -IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager() -{ - return m_craftitemdef; -} const ModSpec* Server::getModSpec(const std::string &modname) { @@ -4874,7 +4273,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 +4318,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..4fdb60065 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); @@ -664,8 +660,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 +669,6 @@ private: // Craft definition manager IWritableCraftDefManager *m_craftdef; - // CraftItem definition manager - IWritableCraftItemDefManager *m_craftitemdef; - // Mods core::list<ModSpec> m_mods; @@ -740,7 +733,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/servermain.cpp b/src/servermain.cpp index e8a54512e..3ef1d9479 100644 --- a/src/servermain.cpp +++ b/src/servermain.cpp @@ -68,7 +68,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "materials.h" #include "config.h" -#include "mineral.h" #include "filesys.h" #include "defaultsettings.h" #include "settings.h" @@ -301,10 +300,6 @@ int main(int argc, char *argv[]) srand(time(0)); mysrand(time(0)); - // Initialize stuff - - init_mineral(); - /* Run unit tests */ diff --git a/src/serverobject.cpp b/src/serverobject.cpp index b5fd6fc3a..76a70ca5b 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(u8 type, Factory f) ServerActiveObject::getTypes().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 d2627c87e..844bfd2ff 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; +struct 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..b4dbbdb1b 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), @@ -45,6 +46,7 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 pee ServerActiveObject(env, pos_), 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_is_in_environment(false), @@ -57,7 +59,6 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 pee } ServerRemotePlayer::~ServerRemotePlayer() { - clearAddToInventoryLater(); } void ServerRemotePlayer::setPosition(const v3f &position) @@ -67,12 +68,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 +186,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 +199,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 +234,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..4226df544 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -47,6 +47,72 @@ 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.is_ground_content = true; + 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.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 +162,120 @@ 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); + // MSVC fails when directly using "\\\\" + std::string backslash = "\\"; + 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) + + backslash + backslash + 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 +462,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 +480,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))); } }; @@ -1022,7 +1201,7 @@ struct TestConnection server.Send(peer_id_client, 0, data1, true); - sleep_ms(3000); + //sleep_ms(3000); SharedBuffer<u8> recvdata; infostream<<"** running client.Receive()"<<std::endl; @@ -1031,7 +1210,7 @@ struct TestConnection bool received = false; u32 timems0 = porting::getTimeMs(); for(;;){ - if(porting::getTimeMs() - timems0 > 5000) + if(porting::getTimeMs() - timems0 > 5000 || received) break; try{ size = client.Receive(peer_id, recvdata); @@ -1086,16 +1265,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..bc4c49cb1 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <ICameraSceneNode.h> #include "log.h" #include "mapnode.h" // For texture atlas making -#include "mineral.h" // For texture atlas making #include "nodedef.h" // For texture atlas making #include "gamedef.h" @@ -299,8 +298,8 @@ public: Example names: "stone.png" "stone.png^crack2" - "stone.png^blit:mineral_coal.png" - "stone.png^blit:mineral_coal.png^crack1" + "stone.png^mineral_coal.png" + "stone.png^mineral_coal.png^crack1" - If texture specified by name is found from cache, return the cached id. @@ -337,6 +336,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 +474,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,20 +819,10 @@ 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){ - for(int k=1; k<MINERAL_COUNT; k++){ - std::string mineraltexture = mineral_block_texture(k); - std::string fulltexture = name + "^" + mineraltexture; - sourcelist[fulltexture] = true; - } - } } } @@ -1317,23 +1310,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 +1418,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 +1441,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 +1507,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, image->copyTo(baseimg); image->drop(); } -#endif } else { @@ -1579,38 +1518,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..f4c7c3017 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) @@ -1751,6 +1757,50 @@ protected: float m_accumulator; }; +/* + Splits a list into "pages". For example, the list [1,2,3,4,5] split + into two pages would be [1,2,3],[4,5]. This function computes the + minimum and maximum indices of a single page. + + length: Length of the list that should be split + page: Page number, 1 <= page <= pagecount + pagecount: The number of pages, >= 1 + minindex: Receives the minimum index (inclusive). + maxindex: Receives the maximum index (exclusive). + + Ensures 0 <= minindex <= maxindex <= length. +*/ +inline void paging(u32 length, u32 page, u32 pagecount, u32 &minindex, u32 &maxindex) +{ + if(length < 1 || pagecount < 1 || page < 1 || page > pagecount) + { + // Special cases or invalid parameters + minindex = maxindex = 0; + } + else if(pagecount <= length) + { + // Less pages than entries in the list: + // Each page contains at least one entry + minindex = (length * (page-1) + (pagecount-1)) / pagecount; + maxindex = (length * page + (pagecount-1)) / pagecount; + } + else + { + // More pages than entries in the list: + // Make sure the empty pages are at the end + if(page < length) + { + minindex = page-1; + maxindex = page; + } + else + { + minindex = 0; + maxindex = 0; + } + } +} + std::string translatePassword(std::string playername, std::wstring password); enum PointedThingType |