From e5076465197e1642067b8e62786b0553b063dce4 Mon Sep 17 00:00:00 2001 From: loosewheel <76670709+loosewheel@users.noreply.github.com> Date: Tue, 22 Feb 2022 14:04:04 +1000 Subject: [PATCH] Add files via upload --- change.log | 4 + crafting.lua | 38 ++ init.lua | 3 +- license.txt | 3 + readme.txt | 3 +- settings.lua | 5 +- storage.lua | 1344 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1397 insertions(+), 3 deletions(-) create mode 100644 storage.lua diff --git a/change.log b/change.log index 712f563..11a9b8c 100644 --- a/change.log +++ b/change.log @@ -114,3 +114,7 @@ v0.1.21 * Minor bug fix, movefloor global. * Fixed movefloor so player doesn't fall through floor. * Transfer timer in moved nodes for pistons. + + +v0.1.22 +* Added storage. diff --git a/crafting.lua b/crafting.lua index d210082..6a1d92a 100644 --- a/crafting.lua +++ b/crafting.lua @@ -23,6 +23,44 @@ minetest.register_craft( { }) +minetest.register_craft( { + output = "lwcomponents:storage_unit 2", + recipe = { + { "default:steel_ingot", "group:wood", "group:wood" }, + { "group:wood", "", "group:wood" }, + { "group:wood", "group:wood", "default:chest" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:storage_unit_locked 2", + recipe = { + { "default:steel_ingot", "group:wood", "group:wood" }, + { "group:wood", "", "group:wood" }, + { "group:wood", "group:wood", "default:chest_locked" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:storage_indexer", + recipe = { + { "default:steel_ingot", "group:wood" }, + { "group:wood", "default:chest" } + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:storage_indexer_locked", + recipe = { + { "default:steel_ingot", "group:wood" }, + { "group:wood", "default:chest_locked" } + }, +}) + + minetest.register_craft( { output = "lwcomponents:cannon_shell 10", recipe = { diff --git a/init.lua b/init.lua index ba56b76..389c170 100644 --- a/init.lua +++ b/init.lua @@ -1,4 +1,4 @@ -local version = "0.1.21" +local version = "0.1.22" local mod_storage = minetest.get_mod_storage () @@ -37,6 +37,7 @@ loadfile (modpath.."/cannon_shell.lua") (utils) loadfile (modpath.."/pistons.lua") (utils) loadfile (modpath.."/through_wire.lua") (utils) loadfile (modpath.."/camera.lua") (utils) +loadfile (modpath.."/storage.lua") (utils) loadfile (modpath.."/extras.lua") (utils) loadfile (modpath.."/digiswitch.lua") (utils) loadfile (modpath.."/movefloor.lua") (utils) diff --git a/license.txt b/license.txt index 2897723..39dbd48 100644 --- a/license.txt +++ b/license.txt @@ -82,6 +82,9 @@ public domain. camera lens images derived from images from https://openclipart.org, which is public domain. +storage nodes images derived from images from https://openclipart.org, which +is public domain. + player button images derived from mesecons button image. cannon firing and explosion sound from tnt (TumeniNodes/steveygos93), diff --git a/readme.txt b/readme.txt index aa82cb0..323ff78 100644 --- a/readme.txt +++ b/readme.txt @@ -13,7 +13,7 @@ CC BY-SA 3.0 Version ======= -0.1.21 +0.1.22 Minetest Version @@ -67,6 +67,7 @@ Various components for mesecons and digilines. * Digiswitch, digilines controlled mesecons power. * Movefloor, similar to vertical mesecons movestone. * Camera, takes a representative image. +* Storage, indexed storage units. * Mesecons Through Wire, transmits through 1 to 2 solid blocks. * Solid color conductor blocks, same as Solid Color Block but also mesecons and digilines conductor. diff --git a/settings.lua b/settings.lua index 81652a7..7cd11f3 100644 --- a/settings.lua +++ b/settings.lua @@ -10,7 +10,10 @@ utils.settings.alert_handler_errors = minetest.settings:get_bool ("lwcomponents_alert_handler_errors", true) utils.settings.max_piston_nodes = - tonumber(minetest.settings:get("lwcomponents_max_piston_nodes") or 15) + tonumber (minetest.settings:get ("lwcomponents_max_piston_nodes") or 15) + +utils.settings.default_stack_max = + tonumber (minetest.settings:get ("default_stack_max")) or 99 diff --git a/storage.lua b/storage.lua new file mode 100644 index 0000000..170265b --- /dev/null +++ b/storage.lua @@ -0,0 +1,1344 @@ +local utils = ... +local S = utils.S + + + +local function unit_after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local spec = + "formspec_version[3]".. + "size[11.75,12.25,false]".. + "list[context;main;1.0,1.0;8,4;]".. + "list[current_player;main;1.0,6.5;8,4;]".. + "listring[]" + + meta:set_string ("inventory", "{ main = { } }") + meta:set_string ("formspec", spec) + + local inv = meta:get_inventory () + + inv:set_size ("main", 32) + inv:set_width ("main", 8) + + -- If return true no item is taken from itemstack + return false +end + + + +local function unit_after_place_node_locked (pos, placer, itemstack, pointed_thing) + unit_after_place_node (pos, placer, itemstack, pointed_thing) + + if placer and placer:is_player () then + local meta = minetest.get_meta (pos) + + meta:set_string ("owner", placer:get_player_name ()) + meta:set_string ("infotext", "Storage Unit (owned by "..placer:get_player_name ()..")") + end + + -- If return true no item is taken from itemstack + return false +end + + + +local function unit_can_dig (pos, player) + if not utils.can_interact_with_node (pos, player) then + return false + end + + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + if not inv:is_empty ("main") then + return false + end + end + end + + return true +end + + + +local function unit_on_blast (pos, intensity) + local meta = minetest.get_meta (pos) + + if meta then + if intensity >= 1.0 then + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("main") + + for slot = 1, slots do + local stack = inv:get_stack ("main", slot) + + if stack and not stack:is_empty () then + if math.floor (math.random (0, 5)) == 3 then + utils.item_drop (stack, nil, pos) + else + utils.on_destroy (stack) + end + end + end + end + + on_destruct (pos) + minetest.remove_node (pos) + + else -- intensity < 1.0 + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("main") + + for slot = 1, slots do + local stack = inv:get_stack ("main", slot) + + if stack and not stack:is_empty () then + utils.item_drop (stack, nil, pos) + end + end + end + + local node = minetest.get_node_or_nil (pos) + if node then + local items = minetest.get_node_drops (node, nil) + + if items and #items > 0 then + local stack = ItemStack (items[1]) + + if stack then + preserve_metadata (pos, node, meta, { stack }) + utils.item_drop (stack, nil, pos) + on_destruct (pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function unit_on_rightclick (pos, node, clicker, itemstack, pointed_thing) + if not utils.can_interact_with_node (pos, clicker) then + if clicker and clicker:is_player () then + local owner = "" + local meta = minetest.get_meta (pos) + + if meta then + owner = meta:get_string ("owner") + end + + local spec = + "formspec_version[3]".. + "size[8.0,4.0,false]".. + "label[1.0,1.0;Owned by "..minetest.formspec_escape (owner).."]".. + "button_exit[3.0,2.0;2.0,1.0;close;Close]" + + minetest.show_formspec (clicker:get_player_name (), + "lwcomponents:component_privately_owned", + spec) + end + end + + return itemstack +end + + + + +minetest.register_node("lwcomponents:storage_unit", { + description = S("Storage Unit"), + drawtype = "glasslike_framed", + tiles = { "lwcomponents_storage_framed.png", "lwcomponents_storage.png" }, + is_ground_content = false, + groups = { choppy = 2 }, + sounds = default.node_sound_wood_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + + after_place_node = unit_after_place_node, + can_dig = unit_can_dig, + on_blast = unit_on_blast, + on_rightclick = unit_on_rightclick +}) + + + +minetest.register_node("lwcomponents:storage_unit_locked", { + description = S("Storage Unit (locked)"), + drawtype = "glasslike_framed", + tiles = { "lwcomponents_storage_framed.png", "lwcomponents_storage.png" }, + is_ground_content = false, + groups = { choppy = 2 }, + sounds = default.node_sound_wood_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + + after_place_node = unit_after_place_node_locked, + can_dig = unit_can_dig, + on_blast = unit_on_blast, + on_rightclick = unit_on_rightclick +}) + + + +local consolidation_interval = 20 + + + +local function unescape_description (description) + if description:sub (1, 4) == string.char (27, 40, 84, 64) then + local last = description:find (")", 5, true) + + if last then + description = description:sub (last + 1, -1) + end + end + + return description:gsub (string.char (27, 70), ""):gsub (string.char (27, 69), "") +end + + + +local function unit_inventory (pos, owner, inv_list) + local node = utils.get_far_node (pos) + + if node and (node.name == "lwcomponents:storage_unit" or + node.name == "lwcomponents:storage_unit_locked") then + + local meta = minetest.get_meta (pos) + local uowner = meta:get_string ("owner") + + if meta and (owner == uowner or uowner == "") then + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("main") + + for slot = 1, slots, 1 do + local stack = inv:get_stack ("main", slot) + + if stack and not stack:is_empty () then + local copy = ItemStack (stack) + copy:set_count (1) + local name = copy:to_string () + local item = inv_list[name] + + if not item then + inv_list[name] = { count = 0 } + item = inv_list[name] + end + + item[#item + 1] = + { + pos = vector.new (pos), + count = stack:get_count (), + slot = slot + } + + item.count = item.count + stack:get_count () + end + end + end + + return true + end + end + + return false +end + + + +local function inventory_searcher (pos, owner, coords, inv_list, check_list) + local spos = minetest.pos_to_string (pos, 0) + + if not check_list[spos] then + check_list[spos] = true + + if unit_inventory (pos, owner, inv_list) then + for _, c in ipairs (coords) do + inventory_searcher (vector.add (pos, c), owner, coords, inv_list, check_list) + end + end + end +end + + + +local function get_inventory_list (pos) + local inv_list = { } + local meta = minetest.get_meta (pos) + + if meta then + local owner = meta:get_string ("owner") + local check_list = { [minetest.pos_to_string (pos, 0)] = true } + local coords = + { + { 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 } + } + + for _, c in ipairs (coords) do + inventory_searcher (vector.add (pos, c), owner, coords, inv_list, check_list) + end + end + + return inv_list +end + + + +local function count_table_keys (t) + local count = 0 + + for k, v in pairs (t) do + count = count + 1 + end + + return count +end + + + +local function get_stock_list (pos) + local inv_list = get_inventory_list (pos) + local list = { } + + for k, v in pairs (inv_list) do + local stack = ItemStack (k) + local name = stack:get_name () + local description = nil + local custom = false + local pallet_index = nil + local tstack = stack:to_table () + + if tstack and tstack.meta and count_table_keys (tstack.meta) > 0 then + custom = true + + if stack:get_meta ():get_string ("description") ~= "" then + description = stack:get_meta ():get_string ("description") + end + + pallet_index = tstack.meta.palette_index + end + + if not description then + description = name + + local def = utils.find_item_def (name) + + if def then + if def.short_description then + description = def.short_description + elseif def.description then + description = def.description + end + end + end + + list[#list + 1] = + { + name = stack:get_name (), + description = unescape_description (description), + id = k, + count = v.count, + custom = custom, + pallet_index = pallet_index, + } + end + + return list +end + + + +local function is_same_item (item1, item2) + local copy1 = ItemStack (item1) + local copy2 = ItemStack (item2) + + copy1:set_count (1) + copy2:set_count (1) + + return copy1:to_string () == copy2:to_string () +end + + + +local function output_items (pos, name, count) + if count < 1 then + return 0 + end + + local meta = minetest.get_meta (pos) + + if not meta then + return 0 + end + + local inv = meta:get_inventory () + + if not inv then + return 0 + end + + local stack = ItemStack (name) + stack:set_count (count) + + while stack:get_count () > 0 do + if inv:room_for_item ("output", stack) then + break + else + stack:set_count (stack:get_count () - 1) + end + end + + if stack:get_count () < 1 then + return 0 + end + + local inv_list = get_inventory_list (pos) + local item = inv_list[name] + local left = stack:get_count () + + if item then + for i = #item, 1, -1 do + local tmeta = minetest.get_meta (item[i].pos) + local tinv = (tmeta and tmeta:get_inventory ()) or nil + + if tinv then + local s = tinv:get_stack ("main", item[i].slot) + + if is_same_item (name, s) then + if s:get_count () > left then + left = 0 + s:set_count (s:get_count () - left) + tinv:set_stack ("main", item[i].slot, s) + else + left = left - s:get_count () + tinv:set_stack ("main", item[i].slot, nil) + end + end + + if left == 0 then + break + end + + end + end + end + + if left < count then + local output = ItemStack (name) + + output:set_count (count - left) + inv:add_item ("output", output) + + return count - left + end + + return 0 +end + + + +local function consolidate_itemstacks (item1, item2) + local copy1 = ItemStack (item1) + local copy2 = ItemStack (item2) + + if is_same_item (copy1, copy2) then + local count = copy1:get_stack_max () - copy1:get_count () + + if count > copy2:get_count () then + count = copy2:get_count () + end + + if count > 0 then + copy1:set_count (copy1:get_count () + count) + copy2:set_count (copy2:get_count () - count) + + if copy2:get_count () < 1 then + copy2 = nil + end + end + end + + return copy1, copy2 +end + + + +local function consolidate_stock (pos) + local inv_list = get_inventory_list (pos) + + for k, v in pairs (inv_list) do + if #v > 1 then + for i = #v, 2, -1 do + local smeta = minetest.get_meta (v[i].pos) + local sinv = (smeta and smeta:get_inventory ()) or nil + + if sinv then + local src = sinv:get_stack ("main", v[i].slot) + + if src and not src:is_empty () then + for j = 1, i - 1, 1 do + local dmeta = minetest.get_meta (v[j].pos) + local dinv = (dmeta and dmeta:get_inventory ()) or nil + + if dinv then + local dest = dinv:get_stack ("main", v[j].slot) + + dest, src = consolidate_itemstacks (dest, src) + dinv:set_stack ("main", v[j].slot, dest) + end + + if not src or src:is_empty () then + break + end + end + end + + sinv:set_stack ("main", v[i].slot, src) + end + end + end + end +end + + + +local function check_consolidation (pos) + local meta = minetest.get_meta (pos) + + if meta then + local count = meta:get_int ("input_count") + 1 + + if count >= consolidation_interval then + meta:set_int ("input_count", 0) + + minetest.after (0.1, consolidate_stock, vector.new (pos)) + else + meta:set_int ("input_count", count) + end + end +end + + + +local function check_filter (pos, itemstack) + local stack = ItemStack (itemstack) + + if stack and not stack:is_empty () then + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + if inv:is_empty ("filter") then + return true + end + + local slots = inv:get_size ("filter") + for i = 1, slots, 1 do + local s = inv:get_stack ("filter", i) + + if s and not s:is_empty () and + s:get_name () == stack:get_name () then + + return true + end + end + end + end + end + + return false +end + + + +local function unit_placer (pos, owner, stack) + local node = utils.get_far_node (pos) + + if node and (node.name == "lwcomponents:storage_unit" or + node.name == "lwcomponents:storage_unit_locked") then + + local meta = minetest.get_meta (pos) + local uowner = meta:get_string ("owner") + + if meta and (owner == uowner or uowner == "") then + local inv = meta:get_inventory () + + if inv then + local left = inv:add_item ("main", stack) + + if not left or left:is_empty () or left:get_count () == 0 then + return false, nil + end + + return true, left + end + end + end + + return false, stack +end + + + +local function inventory_placer (pos, owner, coords, stack, check_list) + local spos = minetest.pos_to_string (pos, 0) + + if not check_list[spos] then + check_list[spos] = true + + local continue, left = unit_placer (pos, owner, stack) + + if continue and left then + for _, c in ipairs (coords) do + left = inventory_placer (vector.add (pos, c), owner, coords, left, check_list) + + if not left then + break + end + end + end + + return left + end + + return stack +end + + + +local function input_item (pos, itemstack) + local stack = ItemStack (itemstack) + + if stack and not stack:is_empty () and + check_filter (pos, itemstack) then + + local meta = minetest.get_meta (pos) + + if meta then + local owner = meta:get_string ("owner") + local check_list = { [minetest.pos_to_string (pos, 0)] = true } + local coords = + { + { 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 } + } + + for _, c in ipairs (coords) do + stack = inventory_placer (vector.add (pos, c), owner, coords, stack, check_list) + + if not stack then + break + end + end + end + + check_consolidation (pos) + end + + return stack +end + + + +local function store_input (pos) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("input") + + for slot = 1, slots, 1 do + local stack = inv:get_stack ("input", slot) + + if stack and not stack:is_empty () then + local left = input_item (pos, stack) + + if left then + left = inv:add_item ("output", left) + end + + inv:set_stack ("input", slot, left) + end + end + end + end +end + + + +local function store_input_delayed (pos) + minetest.after (0.1, store_input, pos) +end + + + +local function search_filter (name, terms) + if terms then + for _, t in ipairs (terms) do + if (name:lower ():find (t, 1, true)) then + return true + end + end + + return false + end + + return true +end + + + +local function get_formspec_list (pos) + local inv_list = get_inventory_list (pos) + local list = { } + + for k, v in pairs (inv_list) do + local description = k + local stack = ItemStack (k) + local smeta = stack:get_meta () + + if smeta and smeta:get_string ("description") ~= "" then + description = smeta:get_string ("description") + else + local def = utils.find_item_def (stack:get_name ()) + + if def then + if def.short_description then + description = def.short_description + elseif def.description then + description = def.description + end + end + end + + list[#list + 1] = + { + item = k, + description = unescape_description (description), + count = v.count + } + end + + local foo = table.sort (list , function (e1, e2) + return (e1.description:lower () < e2.description:lower ()) + end) + + return list +end + + + +local function indexer_get_formspec (pos, search) + local inv_list = get_formspec_list (pos) + + search = search or "" + + local terms = { } + for w in string.gmatch(search, "[^%s]+") do + terms[#terms + 1] = string.lower (w) + end + + if #terms < 1 then + terms = nil + end + + local index = "" + local count = 0 + local top = 0 + for _, v in ipairs (inv_list) do + if search_filter (v.description, terms) then + local stack = ItemStack (v.item) + local max_stack = stack:get_stack_max () + local descr_esc = minetest.formspec_escape (v.description) + local item = + string.format ("item_image_button[0.0,%0.2f;1.0,1.0;%s;01_%s;]", + top, v.item, v.item) + + if max_stack >= 10 then + item = item.. + string.format ("button[1.0,%0.2f;1.0,1.0;10_%s;10]", + top, v.item) + end + + if max_stack > 1 then + item = item.. + string.format ("button[2.0,%0.2f;1.0,1.0;ST_%d_%s;%d]", + top, max_stack, v.item, max_stack) + end + + item = item.. + string.format ("label[3.1,%0.2f;%d]".. + "label[4.4,%0.2f;%s]", + top + 0.5, v.count, + top + 0.5, descr_esc) + + index = index..item + + top = top + 1.0 + count = count + 1 + end + end + + local scroll_height = ((count < 12 and 0) or (count - 11)) * 10 + local thumb_size = (count < 12 and 11) or (scroll_height * (11 / count)) + + if thumb_size < (scroll_height / 10) then + thumb_size = scroll_height / 10 + end + + local spec = + string.format ("formspec_version[3]".. + "size[21.75,14.5,false]".. + "field[1.0,1.21;7.5,0.8;search_field;;%s]\n".. + "field_close_on_enter[search_field;false]".. + "button[8.5,1.21;2.0,0.8;search;Search]\n".. + "scrollbaroptions[min=0;max=%d;smallstep=10;largestep=100;thumbsize=%d;arrows=default]".. + "scrollbar[10.0,2.5;0.5,11.0;vertical;index_scrollbar;0-%d]".. + "scroll_container[1.0,2.5;9.0,11.0;index_scrollbar;vertical;0.1]".. + "%s".. + "scroll_container_end[]".. + "field[11.0,1.21;3.0,0.8;channel;Channel;${channel}]\n".. + "field_close_on_enter[channel;false]".. + "button[14.0,1.21;1.5,0.8;setchannel;Set]\n".. + "label[11.0,3.6;Input]".. + "list[context;input;11.0,3.8;2,2;]".. + "listring[context;output]".. + "label[16.0,1.0;Output]".. + "list[context;output;16.0,1.25;4,4;]".. + "listring[current_player;main]".. + "label[11.0,6.75;Filter]".. + "list[context;filter;11.0,7.0;8,1;]".. + "list[current_player;main;11.0,8.75;8,4;]".. + "listring[context;input]", + search, + scroll_height, + thumb_size, + scroll_height, + index) + + return spec +end + + + +local function indexer_after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + + meta:set_string ("inventory", "{ input = { }, output = { }, filter = { } }") + meta:set_string ("formspec", indexer_get_formspec (pos)) + + local inv = meta:get_inventory () + + inv:set_size ("input", 4) + inv:set_width ("input", 2) + inv:set_size ("output", 16) + inv:set_width ("output", 4) + inv:set_size ("filter", 8) + inv:set_width ("filter", 2) + + -- If return true no item is taken from itemstack + return false +end + + + +local function indexer_after_place_node_locked (pos, placer, itemstack, pointed_thing) + indexer_after_place_node (pos, placer, itemstack, pointed_thing) + + if placer and placer:is_player () then + local meta = minetest.get_meta (pos) + + meta:set_string ("owner", placer:get_player_name ()) + meta:set_string ("infotext", "Storage Indexer (owned by "..placer:get_player_name ()..")") + end + + -- If return true no item is taken from itemstack + return false +end + + + +local function indexer_can_dig (pos, player) + if not utils.can_interact_with_node (pos, player) then + return false + end + + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + if not inv:is_empty ("input") then + return false + end + + if not inv:is_empty ("output") then + return false + end + end + end + + return true +end + + + +local function indexer_on_blast (pos, intensity) + local meta = minetest.get_meta (pos) + + if meta then + if intensity >= 1.0 then + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("input") + + for slot = 1, slots do + local stack = inv:get_stack ("input", slot) + + if stack and not stack:is_empty () then + if math.floor (math.random (0, 5)) == 3 then + utils.item_drop (stack, nil, pos) + else + utils.on_destroy (stack) + end + end + end + + slots = inv:get_size ("output") + + for slot = 1, slots do + local stack = inv:get_stack ("output", slot) + + if stack and not stack:is_empty () then + if math.floor (math.random (0, 5)) == 3 then + utils.item_drop (stack, nil, pos) + else + utils.on_destroy (stack) + end + end + end + end + + on_destruct (pos) + minetest.remove_node (pos) + + else -- intensity < 1.0 + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("input") + + for slot = 1, slots do + local stack = inv:get_stack ("input", slot) + + if stack and not stack:is_empty () then + utils.item_drop (stack, nil, pos) + end + end + + slots = inv:get_size ("output") + + for slot = 1, slots do + local stack = inv:get_stack ("output", slot) + + if stack and not stack:is_empty () then + utils.item_drop (stack, nil, pos) + end + end + end + + local node = minetest.get_node_or_nil (pos) + if node then + local items = minetest.get_node_drops (node, nil) + + if items and #items > 0 then + local stack = ItemStack (items[1]) + + if stack then + preserve_metadata (pos, node, meta, { stack }) + utils.item_drop (stack, nil, pos) + on_destruct (pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function indexer_on_rightclick (pos, node, clicker, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + + if meta then + if not utils.can_interact_with_node (pos, clicker) then + if clicker and clicker:is_player () then + local owner = meta:get_string ("owner") + + local spec = + "formspec_version[3]".. + "size[8.0,4.0,false]".. + "label[1.0,1.0;Owned by "..minetest.formspec_escape (owner).."]".. + "button_exit[3.0,2.0;2.0,1.0;close;Close]" + + minetest.show_formspec (clicker:get_player_name (), + "lwcomponents:component_privately_owned", + spec) + end + + return itemstack + end + + meta:set_string ("formspec", indexer_get_formspec (pos)) + end + + return itemstack +end + + + +local function indexer_on_receive_fields (pos, formname, fields, sender) + if not utils.can_interact_with_node (pos, sender) then + return + end + + if fields.setchannel or (fields.key_enter_field and + fields.key_enter_field == "channel") then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("channel", fields.channel) + end + + elseif fields.search or (fields.key_enter_field and + fields.key_enter_field == "search_field") then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("formspec", indexer_get_formspec (pos, fields.search_field)) + end + + else + for k, v in pairs (fields) do + if k:sub (1, 3) == "01_" then + local item = k:sub (4, -1) + output_items (pos, item, 1) + + break + elseif k:sub (1, 3) == "10_" then + local item = k:sub (4, -1) + output_items (pos, item, 10) + + break + elseif k:sub (1, 3) == "ST_" then + local marker = k:find ("_", 4, true) + + if marker then + local qty = tonumber (k:sub (4, marker - 1) or 1) + local item = k:sub (marker + 1, -1) + + output_items (pos, item, qty) + end + end + end + end +end + + + +local function indexer_on_metadata_inventory_put (pos, listname, index, stack, player) + if listname == "input" then + store_input_delayed (pos) + end +end + + + +local function indexer_on_metadata_inventory_move (pos, from_list, from_index, + to_list, to_index, count, player) + if from_list == "output" and to_list == "input" then + store_input_delayed (pos) + end +end + + + +local function indexer_allow_metadata_inventory_put (pos, listname, index, stack, player) + if listname == "filter" then + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + inv:set_stack ("filter", index, ItemStack (stack:get_name ())) + end + end + + return 0 + end + + return stack:get_stack_max () +end + + + +local function indexer_allow_metadata_inventory_take (pos, listname, index, stack, player) + if listname == "filter" then + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + inv:set_stack ("filter", index, nil) + end + end + + return 0 + end + + return stack:get_stack_max () +end + + + +local function indexer_allow_metadata_inventory_move (pos, from_list, from_index, + to_list, to_index, count, player) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + if from_list == "filter" then + if to_list == "filter" then + return 1 + end + + inv:set_stack ("filter", from_index, nil) + + return 0 + + elseif to_list == "filter" then + local stack = inv:get_stack (from_list, from_index) + + if stack and not stack:is_empty () then + inv:set_stack ("filter", to_index, ItemStack (stack:get_name ())) + end + + return 0 + + else + local stack = inv:get_stack (from_list, from_index) + + if stack and not stack:is_empty () then + return stack:get_stack_max () + end + end + end + end + + return utils.settings.default_stack_max +end + + + +local function send_stock_message (pos) + if utils.digilines_supported then + local meta = minetest.get_meta (pos) + + if meta then + local channel = meta:get_string ("channel") + + if channel:len () > 0 then + local msg = + { + action = "inventory", + inventory = get_stock_list (pos) + } + + utils.digilines_receptor_send (pos, + utils.digilines_default_rules, + channel, + msg) + end + end + end +end + + + +local function digilines_support () + if utils.digilines_supported then + return + { + wire = + { + rules = utils.digilines_default_rules, + }, + + effector = + { + action = function (pos, node, channel, msg) + local meta = minetest.get_meta(pos) + + if meta then + local this_channel = meta:get_string ("channel") + + if this_channel ~= "" then + if type (msg) == "string" then + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if this_channel == channel then + if m[1] == "output" then + if m[2] then + output_items (pos, m[2], tonumber (m[3] or 1) or 1) + end + + elseif m[1] == "inventory" then + send_stock_message (pos) + + end + end + + elseif type (msg) == "table" then + if this_channel == channel then + if msg.action and msg.action == "output" and + type (msg.item) == "string" then + + output_items (pos, msg.item, tonumber (msg.count or 1) or 1) + end + end + + end + end + end + end, + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:storage_indexer", { + description = S("Storage Indexer"), + drawtype = "normal", + tiles = { "lwcomponents_storage_framed.png", "lwcomponents_storage_framed.png", + "lwcomponents_storage_indexer.png", "lwcomponents_storage_indexer.png", + "lwcomponents_storage_indexer.png", "lwcomponents_storage_indexer.png",}, + is_ground_content = false, + groups = { choppy = 2 }, + sounds = default.node_sound_wood_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_receive_fields = indexer_on_receive_fields, + after_place_node = indexer_after_place_node, + can_dig = indexer_can_dig, + on_blast = indexer_on_blast, + on_rightclick = indexer_on_rightclick, + on_metadata_inventory_put = indexer_on_metadata_inventory_put, + on_metadata_inventory_move = indexer_on_metadata_inventory_move, + allow_metadata_inventory_take = indexer_allow_metadata_inventory_take, + allow_metadata_inventory_put = indexer_allow_metadata_inventory_put, + allow_metadata_inventory_move = indexer_allow_metadata_inventory_move +}) + + + +minetest.register_node("lwcomponents:storage_indexer_locked", { + description = S("Storage Indexer (locked)"), + drawtype = "normal", + tiles = { "lwcomponents_storage_framed.png", "lwcomponents_storage_framed.png", + "lwcomponents_storage_indexer.png", "lwcomponents_storage_indexer.png", + "lwcomponents_storage_indexer.png", "lwcomponents_storage_indexer.png",}, + is_ground_content = false, + groups = { choppy = 2 }, + sounds = default.node_sound_wood_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_receive_fields = indexer_on_receive_fields, + after_place_node = indexer_after_place_node_locked, + can_dig = indexer_can_dig, + on_blast = indexer_on_blast, + on_rightclick = indexer_on_rightclick, + on_metadata_inventory_put = indexer_on_metadata_inventory_put, + on_metadata_inventory_move = indexer_on_metadata_inventory_move, + allow_metadata_inventory_take = indexer_allow_metadata_inventory_take, + allow_metadata_inventory_put = indexer_allow_metadata_inventory_put, + allow_metadata_inventory_move = indexer_allow_metadata_inventory_move +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:storage_indexer", "output"}, -- take items from above into hopper below + {"bottom", "lwcomponents:storage_indexer", "input"}, -- insert items below from hopper above + {"side", "lwcomponents:storage_indexer", "input"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:storage_indexer_locked", "output"}, -- take items from above into hopper below + {"bottom", "lwcomponents:storage_indexer_locked", "input"}, -- insert items below from hopper above + {"side", "lwcomponents:storage_indexer_locked", "input"}, -- insert items from hopper at side +}) + + + +--