From 4e2ac43b2db011252c3c7b8f1280d8cd60391eed Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Sat, 17 Aug 2019 23:12:12 -0600 Subject: [PATCH] can now create detached inventory. May be very buggy yet. --- controller.lua | 98 +++++----------------- functions.lua | 186 +++++++++++++++++++++++++++++++++++++++++ init.lua | 1 + nodes/node_storage.lua | 3 +- 4 files changed, 212 insertions(+), 76 deletions(-) create mode 100644 functions.lua diff --git a/controller.lua b/controller.lua index 3c7c966..8035bbe 100644 --- a/controller.lua +++ b/controller.lua @@ -23,7 +23,10 @@ local get_controller_unconstructed_formspec = function(pos, player_name) end local get_controller_constructed_formspec = function(pos, digtron_id, player_name) + local digtron_id_name = digtron.get_digtron_id_name(digtron_id) return "size[9,9]button[1,1;1,1;deconstruct;Deconstruct]" + .. "list[detached:" .. digtron_id_name .. ";main;1,2;8,2]" -- TODO: paging system for inventory + .. "list[detached:" .. digtron_id_name .. ";fuel;1,7;8,2]" -- TODO: paging system for inventory end minetest.register_node("digtron:controller", { @@ -60,9 +63,9 @@ minetest.register_node("digtron:controller", { on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) local meta = minetest.get_meta(pos) - local digtron_id = meta:get("digtron_id") + local digtron_id = meta:get_int("digtron_id") local player_name = clicker:get_player_name() - if digtron_id ~= "" then + if digtron_id == 0 then minetest.show_formspec(player_name, "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..player_name, get_controller_unconstructed_formspec(pos, player_name)) @@ -70,7 +73,7 @@ minetest.register_node("digtron:controller", { -- initialized minetest.show_formspec(player_name, "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..player_name..":"..digtron_id, - get_controller_construted_formspec(pos, digtron_id, player_name)) + get_controller_constructed_formspec(pos, digtron_id, player_name)) end end, @@ -78,75 +81,6 @@ minetest.register_node("digtron:controller", { end, }) -local cardinal_directions = { - {x=1,y=0,z=0}, - {x=-1,y=0,z=0}, - {x=0,y=1,z=0}, - {x=0,y=-1,z=0}, - {x=0,y=0,z=1}, - {x=0,y=0,z=-1}, -} -local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) - -local get_all_adjacent_digtron_nodes -get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, not_digtron) - for _, dir in ipairs(cardinal_directions) do - local test_pos = vector.add(pos, dir) - local test_hash = minetest.hash_node_position(test_pos) - if not (digtron_nodes[test_hash] or not_digtron[test_hash]) then -- don't test twice - local test_node = minetest.get_node(test_pos) - local group_value = minetest.get_item_group(test_node.name, "digtron") - if group_value > 0 then - digtron_nodes[test_hash] = test_node - get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, not_digtron) -- recurse - else - not_digtron[test_hash] = test_node - end - end - end -end - - -digtron.construct = function(pos, player_name) - local node = minetest.get_node(pos) - if node.name ~= "digtron:controller" then - -- Called on an incorrect node - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) .. " but the node at this location was " .. node.name) - return nil - end - local meta = minetest.get_meta(pos) - if meta:get("digtron_id") ~= nil then - -- Already constructed. TODO: validate that the digtron_id actually exists as well - minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) .. " but the controller at this location was already part of a constructed Digtron.") - return nil - end - local root_hash = minetest.hash_node_position(pos) - local digtron_nodes = {[root_hash] = node} - local not_digtron = {} - get_all_adjacent_digtron_nodes(pos, digtron_nodes, not_digtron) - for hash, node in pairs(digtron_nodes) do - local relative_hash = hash - root_hash + origin_hash - minetest.chat_send_all("constructing " .. minetest.pos_to_string(minetest.get_position_from_hash(relative_hash))) - local digtron_meta - if hash == root_hash then - digtron_meta = meta -- we're processing the controller, we already have a reference to its meta - else - digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) - end - - local meta_table = digtron_meta:to_table() - meta_table.node = node - -- Process inventories specially - -- Builder inventory gets turned into an itemname in a special key in the builder's meta - -- fuel and main get added to corresponding detached inventory lists - -- then wipe them from the meta_table. They'll be re-added in digtron.deconstruct. - --meta_table.inventory = nil - node.param1 = nil -- we don't care about param1, wipe it to save space - minetest.chat_send_all(dump(meta_table)) - end - - -end -- Dealing with an unconstructed Digtron controller @@ -168,12 +102,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.construct then local digtron_id = digtron.construct(pos, name) if digtron_id then + local meta = minetest.get_meta(pos) + meta:set_int("digtron_id", digtron_id) minetest.show_formspec(name, "digtron_controller_constructed:"..minetest.pos_to_string(pos)..":"..name..":"..digtron_id, - get_controller_construted_formspec(pos, digtron_id, name)) + get_controller_constructed_formspec(pos, digtron_id, name)) end end - + end) -- Controlling a fully armed and operational Digtron @@ -191,10 +127,22 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if player:get_player_name() ~= name then return end - local digtron_id = formname_splot[4] + local digtron_id = formname_split[4] if fields.deconstruct then minetest.chat_send_all("Deconstructing " .. digtron_id) + + local meta = minetest.get_meta(pos) + local digtron_id = meta:get_int("digtron_id") + if digtron_id == 0 then + minetest.log("error", "[Digtron] tried to deconstruct Digtron at pos " + .. minetest.pos_to_string(pos) .. " but it had no digtron_id in the node's metadata") + else + digtron.deconstruct(digtron_id, pos, name) + minetest.show_formspec(name, + "digtron_controller_unconstructed:"..minetest.pos_to_string(pos)..":"..name, + get_controller_unconstructed_formspec(pos, name)) + end end end) diff --git a/functions.lua b/functions.lua new file mode 100644 index 0000000..fffda03 --- /dev/null +++ b/functions.lua @@ -0,0 +1,186 @@ +local mod_meta = minetest.get_mod_storage() + +local detached_inventory_callbacks = { + -- Called when a player wants to move items inside the inventory. + -- Return value: number of items allowed to move. + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + --allow anything in "main" + if to_list == "main" then + return count + end + + --only allow fuel items in "fuel" + local stack = inv:get_stack(from_list, from_index) + if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then + return stack:get_count() + end + return 0 + end, + + -- Called when a player wants to put something into the inventory. + -- Return value: number of items allowed to put. + -- Return value -1: Allow and don't modify item count in inventory. + allow_put = function(inv, listname, index, stack, player) + -- Only allow fuel items to be placed in fuel + if listname == "fuel" then + if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then + return stack:get_count() + else + return 0 + end + end + return stack:get_count() -- otherwise, allow all drops + end, + + -- Called when a player wants to take something out of the inventory. + -- Return value: number of items allowed to take. + -- Return value -1: Allow and don't modify item count in inventory. + allow_take = function(inv, listname, index, stack, player) + return stack:get_count() + end, + + -- Called after the actual action has happened, according to what was + -- allowed. + -- No return value. +-- on_move = function(inv, from_list, from_index, to_list, to_index, count, player), +-- on_put = function(inv, listname, index, stack, player), +-- on_take = function(inv, listname, index, stack, player), + } + +digtron.get_digtron_id_name = function(id) + return "digtron_id_" .. tostring(id) +end + +local create_new_id = function(pos) + local last_id = mod_meta:get_int("last_id") -- returns 0 when uninitialized, so 0 will never be a valid digtron_id. + local new_id = last_id + 1 + mod_meta:set_int("last_id", new_id) -- ensure each call to this method gets a unique number + + local digtron_id_name = digtron.get_digtron_id_name(new_id) + + mod_meta:set_string(digtron_id_name, minetest.pos_to_string(pos)) -- record that this digtron exists + local inv = minetest.create_detached_inventory(digtron_id_name, detached_inventory_callbacks) + + return new_id, inv +end + +-- Deletes a Digtron record. Note: throws everything away, this is not digtron.deconstruct. +local dispose_id = function(id) + local digtron_id_name = digtron.get_digtron_id_name(id) + minetest.remove_detached_inventory(digtron_id_name) + mod_meta:set_string(digtron_id_name, "") +end + + +local cardinal_directions = { + {x=1,y=0,z=0}, + {x=-1,y=0,z=0}, + {x=0,y=1,z=0}, + {x=0,y=-1,z=0}, + {x=0,y=0,z=1}, + {x=0,y=0,z=-1}, +} +local origin_hash = minetest.hash_node_position({x=0,y=0,z=0}) + +-- recursive function searches out all connected unassigned digtron nodes +local get_all_adjacent_digtron_nodes +get_all_adjacent_digtron_nodes = function(pos, digtron_nodes, not_digtron, player_name) + for _, dir in ipairs(cardinal_directions) do + local test_pos = vector.add(pos, dir) + local test_hash = minetest.hash_node_position(test_pos) + if not (digtron_nodes[test_hash] or not_digtron[test_hash]) then -- don't test twice + local test_node = minetest.get_node(test_pos) + local group_value = minetest.get_item_group(test_node.name, "digtron") + if group_value > 0 then + local meta = minetest.get_meta(test_pos) + if meta:contains("digtron_id") then + -- Node is part of an existing digtron, don't incorporate it + not_digtron[test_hash] = true + --elseif TODO test for protected node status using player_name + else + --test_node.group_value = group_value -- for later ease of reference + digtron_nodes[test_hash] = test_node + get_all_adjacent_digtron_nodes(test_pos, digtron_nodes, not_digtron, player_name) -- recurse + end + else + -- don't record details, the content of this node will change as the digtron moves + not_digtron[test_hash] = true + end + end + end +end + +-- Returns the id of the new Digtron record, or nil on failure +digtron.construct = function(pos, player_name) + local node = minetest.get_node(pos) + -- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell + if node.name ~= "digtron:controller" then + -- Called on an incorrect node + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) + .. " but the node at this location was " .. node.name) + return nil + end + local meta = minetest.get_meta(pos) + if meta:contains("digtron_id") then + -- Already constructed. TODO: validate that the digtron_id actually exists as well + minetest.log("error", "[Digtron] digtron.construct called with pos " .. minetest.pos_to_string(pos) + .. " but the controller at this location was already part of a constructed Digtron.") + return nil + end + local root_hash = minetest.hash_node_position(pos) + local digtron_nodes = {[root_hash] = node} -- Nodes that are part of Digtron + local not_digtron = {} -- Nodes that are adjacent to Digtron but not a part of it + get_all_adjacent_digtron_nodes(pos, digtron_nodes, not_digtron, player_name) + + local digtron_id, digtron_inv = create_new_id(pos) + + local layout = {} + + for hash, node in pairs(digtron_nodes) do + local relative_hash = hash - root_hash + origin_hash + minetest.chat_send_all("constructing " .. minetest.pos_to_string(minetest.get_position_from_hash(relative_hash))) + local digtron_meta + if hash == root_hash then + digtron_meta = meta -- we're processing the controller, we already have a reference to its meta + else + digtron_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) + end + + local meta_table = digtron_meta:to_table() + + if meta_table.fields.digtron_id then + -- Trying to incorporate part of an existing digtron, should be impossible. + minetest.log("error", "[Digtron] digtron.construct tried to incorporate a Digtron node of type " + .. node.name .. " at " .. minetest.pos_to_string(minetest.get_position_from_hash(hash)) + .. " that was already assigned to digtron id " .. meta_table.fields.digtron_id) + dispose_id(digtron_id) + return nil + end + -- Process inventories specially + -- TODO Builder inventory gets turned into an itemname in a special key in the builder's meta + -- fuel and main get added to corresponding detached inventory lists + -- then wipe them from the meta_table. They'll be re-added in digtron.deconstruct. + for listname, items in pairs(meta_table.inventory) do + local count = #items + -- increase the corresponding detached inventory size + minetest.chat_send_all("adding " .. count .. " to size of " .. listname) + digtron_inv:set_size(listname, digtron_inv:get_size(listname) + count) + for _, stack in ipairs(items) do + digtron_inv:add_item(listname, stack) + end + end + + node.param1 = nil -- we don't care about param1, wipe it to save space + layout[relative_hash] = {meta = meta_table.fields, node = node} + end + + minetest.debug("constructed id " .. digtron_id .. ": " .. minetest.serialize(layout)) + return digtron_id +end + +-- TODO: skeletal! +digtron.deconstruct = function(digtron_id, pos, name) + dispose_id(digtron_id) + local meta = minetest.get_meta(pos) + meta:set_string("digtron_id", "") +end \ No newline at end of file diff --git a/init.lua b/init.lua index f543b1c..c7c5765 100644 --- a/init.lua +++ b/init.lua @@ -3,6 +3,7 @@ digtron.doc = {} local modpath = minetest.get_modpath(minetest.get_current_modname()) +dofile(modpath.."/functions.lua") dofile(modpath.."/controller.lua") dofile(modpath.."/nodes/node_misc.lua") dofile(modpath.."/nodes/node_storage.lua") \ No newline at end of file diff --git a/nodes/node_storage.lua b/nodes/node_storage.lua index 6aca684..2cb2bbb 100644 --- a/nodes/node_storage.lua +++ b/nodes/node_storage.lua @@ -245,8 +245,9 @@ minetest.register_node("digtron:combined_storage", { inv:set_size("fuel", 8*1) end, - -- Only allow fuel items to be placed in fuel + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + -- Only allow fuel items to be placed in fuel if listname == "fuel" then if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then return stack:get_count()