diff --git a/lwcomponents/change.log b/lwcomponents/change.log new file mode 100644 index 0000000..665548c --- /dev/null +++ b/lwcomponents/change.log @@ -0,0 +1,211 @@ + +v 0.1.0 +* initial release + + +v0.1.1 +* Fixed receiving digilines message on "" channel. +* Added siren. +* Made digilines optional for solid conductor blocks. +* Fixed lighting +* Added puncher + + +v0.1.2 +* Added support for hopper as optional dependency for droppers, dispensers + and collectors. +* Added digilines message to punchers when something is punched. + + +v0.1.3 +* Added hp and height info from detector. +* Added dispensers spawn if spawner with optional dependency on mobs mod. + If mobs:egg is dispensed 10% change a chicken is dispensed instead. +* Added player_button. + + +v0.1.4 +* Bug fix to spawning owned mobs. + + +v0.1.5 +* Added setting Spawn mobs. +* Added lwcomponents.register_spawner api. + + +v0.1.6 +* Added holograms. +* Added breakers. +* Added fans. + + +v0.1.7 +* Fixed fan description. +* Breakers can break nodes up to 5 forward with digilines message. +* Added deployers. + + +v0.1.8 +* Changed detector digilines message to single message with list of + detected items. +* Added conduits. +* Fixed fans not blowing upward. +* Made changes to lwcomponents.register_spawner api - called function + must now set velocity, can use force parameter. +* Removed spawning from this mod. Created lwcomponents_spawners to + register spawners. + + +v0.1.9 +* Fixed infotext on various nodes. + + +v0.1.10 +* Added cannons. + + +v0.1.11 +* Fix to breakers (?). +* Added position aiming to cannons. + + +v0.1.12 +* Added sensitivity option for game controller in cannons. +* Added cannon shells. +* Fixed bug in utils.is_creative. +* Increased cannon pitch to -20 to 70. + + +v0.1.13 +* Removed optional dependency lwdrops. + + +v0.1.14 +* Calls on_drop when item is dropped. + + +v0.1.15 +* Fixed bug call to clear_map in fan on_blast. +* Added pistons. + + +v0.1.16 +* Fixed piston interaction with non-walkable nodes. + + +v0.1.17 +* Fixed unintended global variable in pistons.lua. + + +v0.1.18 +* Added mesecons through wire. + + +v0.1.19 +* Added camera. + + +v0.1.20 +* Valid distance and resolution for camera set by digilines message. +* Imposed maximum resolution of 128 for cameras. + + +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. + + +v0.1.23 +* Fixed storage indexer not taking items from storage properly. +* Added pipeworks support for: + Storage Indexer + Dropper + Collector + Dispenser + Breaker + Deployer + Cannon + Conduit +* Conduits now work in unloaded blocks. +* Fixed conduits sending items in groups. +* Fixed player attached to controller moving forever when they get blown up. +* Fixed pistons being powered from the pusher side. + + +v0.1.24 +* Limited requested count from storage indexer from digilines message to max stack. +* Fixed receptor state in detector. +* Fixed receptor state in digiswitch. +* Fixed bug in utils.is_same_item (). +* Added force field generators. +* Removed immortal from cannon shells (shells test for same type of shell and ignore). + + +v0.1.25 +* Added hoppers. +* Added "inventory" message to conduits. +* Removed digilines or mesecons requirement for conduits. +* Improved unescaping item description. +* Fixed custom item output from storage. +* Added filtering to conduit forms. + + +v0.1.26 +* Added support for stickblocks to pistons. +* Changed description of hoppers to avoid confusion. +* Added destroyer. +* Cleaned up hopper code. +* Fixed recipe for solid conductor blocks. + + +v0.1.27 +* Bug fixes and code cleanup. +* Fixed receptor state in detector. +* Improved receptor state in digiswitch. + + +v0.1.28 +* Added support for lwwires. Wires will not connect to conduits. + + +v0.1.29 +* Fixed piston moving piston blanks. + + +v0.1.30 +* Added quantity field to droppers. +* Added 'Use player when placing' setting. + + +v0.1.31 +* Added crafter. + + +v0.1.32 +* Fixed crafter not return replacement items properly. + + +v0.1.33 +* Added recipe list when crafting by item to UI in crafters. +* Added results message for crafters digilines messages "craft" and "craftitem". +* Added "can_craft" digilines message to crafters. +* Fixed crafter not outputing all items when output has full stack. +* Improved crafter crafting. + + +v0.1.34 +* Made crafter's gaining craftable list granular in execution to minimise server burden. +* Fixed bug in crafter crashing when runs out of material to craft by item. + + +v0.1.35 +* Fixed bug in storage indexers when dealing with items with meta date (thanks to Kimapr for suggestions). + + +v0.1.36 +* Further improvements to storage indexer forms. diff --git a/lwcomponents/collector.lua b/lwcomponents/collector.lua new file mode 100644 index 0000000..21bf73d --- /dev/null +++ b/lwcomponents/collector.lua @@ -0,0 +1,694 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported then + + + +local collect_interval = 0.5 + + + +local function send_collect_message (pos, name, count) + 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 + utils.digilines_receptor_send (pos, + utils.digilines_default_rules, + channel, + { action = "collect", + name = name, + count = count }) + end + end + end +end + + + +local function filter_item (pos, item) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv:is_empty ("filter") then + return true + end + + local slots = inv:get_size ("filter") + for i = 1, slots do + local stack = inv:get_stack ("filter", i) + + if stack and not stack:is_empty () and + stack:get_name () == item then + + return true + end + end + end + + return false +end + + + +local function get_form_spec (is_off) + return + "formspec_version[3]\n".. + "size[11.75,13.75;true]\n".. + "field[1.0,1.0;4.0,0.8;channel;Channel;${channel}]\n".. + "button[5.5,1.0;2.0,0.8;setchannel;Set]\n".. + "button[8.25,1.0;2.5,0.8;"..((is_off and "start;Start") or "stop;Stop").."]\n".. + "list[context;filter;8.5,2.5;2,4;]\n".. + "list[context;main;1.0,2.5;4,4;]\n".. + "list[current_player;main;1.0,8.0;8,4;]\n".. + "listring[]" +end + + + +local function start_collector (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:collector" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:collector_on" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (collect_interval) + + meta:set_string ("formspec", get_form_spec (false)) + end + + elseif node.name == "lwcomponents:collector_locked" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:collector_locked_on" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (collect_interval) + + meta:set_string ("formspec", get_form_spec (false)) + end + + end + end +end + + + +local function stop_collector (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:collector_on" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:collector" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):stop () + + meta:set_string ("formspec", get_form_spec (true)) + end + + elseif node.name == "lwcomponents:collector_locked_on" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:collector_locked" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):stop () + + meta:set_string ("formspec", get_form_spec (true)) + end + + end + end +end + + + +local function on_destruct (pos) + minetest.get_node_timer (pos):stop () +end + + + +local function after_place_base (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local is_off = itemstack and (itemstack:get_name () == "lwcomponents:collector" or + itemstack:get_name () == "lwcomponents:collector_locked") + + meta:set_string ("inventory", "{ main = { }, filter = { } }") + meta:set_string ("formspec", get_form_spec (is_off)) + + local inv = meta:get_inventory () + + inv:set_size ("main", 16) + inv:set_width ("main", 4) + inv:set_size ("filter", 8) + inv:set_width ("filter", 2) +end + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + after_place_base (pos, placer, itemstack, pointed_thing) + utils.pipeworks_after_place (pos) + + -- If return true no item is taken from itemstack + return false +end + + + +local function after_place_node_locked (pos, placer, itemstack, pointed_thing) + after_place_base (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", "Collector (owned by "..placer:get_player_name ()..")") + end + + utils.pipeworks_after_place (pos) + + -- If return true no item is taken from itemstack + return false +end + + + +local function on_receive_fields (pos, formname, fields, sender) + if not utils.can_interact_with_node (pos, sender) then + return + end + + if fields.setchannel then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("channel", fields.channel) + end + + elseif fields.start then + start_collector (pos) + + elseif fields.stop then + stop_collector (pos) + + end +end + + + +local function 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 + + if not inv:is_empty ("filter") then + return false + end + end + end + + return true +end + + + +local function 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 + + slots = inv:get_size ("filter") + + for slot = 1, slots do + local stack = inv:get_stack ("filter", 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 + + slots = inv:get_size ("filter") + + for slot = 1, slots do + local stack = inv:get_stack ("filter", 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 + utils.item_drop (stack, nil, pos) + on_destruct (pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function 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 + + + +local function on_timer (pos, elapsed) + local list = minetest.get_objects_inside_radius (pos, 2) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + for i = 1, #list do + if list[i].get_luaentity and list[i]:get_luaentity () and + list[i]:get_luaentity ().name and + list[i]:get_luaentity ().name == "__builtin:item" then + + local stack = utils.item_pickup (list[i]:get_luaentity (), false) + + if stack and inv:room_for_item ("main", stack) and + filter_item (pos, stack:get_name ()) then + + local name = stack:get_name () + local count = stack:get_count () + + inv:add_item ("main", stack) + utils.item_pickup (list[i]:get_luaentity ()) + + send_collect_message (pos, name, count) + end + end + end + end + end + + return true +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 ~= "" and this_channel == channel and + type (msg) == "string" then + + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if m[1] == "start" then + start_collector (pos) + + elseif m[1] == "stop" then + stop_collector (pos) + + end + end + end + end, + } + } + end + + return nil +end + + + +local function pipeworks_support () + if utils.pipeworks_supported then + return + { + priority = 100, + input_inventory = "main", + connect_sides = { left = 1, right = 1, front = 1, back = 1, bottom = 1, top = 1 }, + + insert_object = function (pos, node, stack, direction) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + return inv:add_item ("main", stack) + end + + return stack + end, + + can_insert = function (pos, node, stack, direction) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + return inv:room_for_item ("main", stack) + end + + return false + end, + + can_remove = function (pos, node, stack, dir) + -- returns the maximum number of items of that stack that can be removed + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + local slots = inv:get_size ("main") + + for i = 1, slots, 1 do + local s = inv:get_stack ("main", i) + + if s and not s:is_empty () and utils.is_same_item (stack, s) then + return s:get_count () + end + end + end + + return 0 + end, + + remove_items = function (pos, node, stack, dir, count) + -- removes count items and returns them + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + local left = count + + if inv then + local slots = inv:get_size ("main") + + for i = 1, slots, 1 do + local s = inv:get_stack ("main", i) + + if s and not s:is_empty () and utils.is_same_item (s, stack) then + if s:get_count () > left then + s:set_count (s:get_count () - left) + inv:set_stack ("main", i, s) + left = 0 + else + left = left - s:get_count () + inv:set_stack ("main", i, nil) + end + end + + if left == 0 then + break + end + end + end + + local result = ItemStack (stack) + result:set_count (count - left) + + return result + end + } + end + + return nil +end + + + +local collector_groups = { cracky = 3 } +if utils.pipeworks_supported then + collector_groups.tubedevice = 1 + collector_groups.tubedevice_receiver = 1 +end + + + +local collector_on_groups = { cracky = 3, not_in_creative_inventory = 1 } +if utils.pipeworks_supported then + collector_on_groups.tubedevice = 1 + collector_on_groups.tubedevice_receiver = 1 +end + + + +minetest.register_node("lwcomponents:collector", { + description = S("Collector"), + tiles = { "lwcollector.png", "lwcollector.png", "lwcollector.png", + "lwcollector.png", "lwcollector.png", "lwcollector.png"}, + is_ground_content = false, + groups = table.copy (collector_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + can_dig = can_dig, + after_dig_node = utils.pipeworks_after_dig, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:collector_locked", { + description = S("Collector (locked)"), + tiles = { "lwcollector.png", "lwcollector.png", "lwcollector.png", + "lwcollector.png", "lwcollector.png", "lwcollector.png"}, + is_ground_content = false, + groups = table.copy (collector_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector_locked", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node_locked, + can_dig = can_dig, + after_dig_node = utils.pipeworks_after_dig, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:collector_on", { + description = S("Collector"), + tiles = { "lwcollector_on.png", "lwcollector_on.png", "lwcollector_on.png", + "lwcollector_on.png", "lwcollector_on.png", "lwcollector_on.png"}, + is_ground_content = false, + groups = table.copy (collector_on_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + can_dig = can_dig, + after_dig_node = utils.pipeworks_after_dig, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:collector_locked_on", { + description = S("Collector (locked)"), + tiles = { "lwcollector_on.png", "lwcollector_on.png", "lwcollector_on.png", + "lwcollector_on.png", "lwcollector_on.png", "lwcollector_on.png"}, + is_ground_content = false, + groups = table.copy (collector_on_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector_locked", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node_locked, + can_dig = can_dig, + after_dig_node = utils.pipeworks_after_dig, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:collector", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:collector", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:collector", "main"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:collector_locked", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:collector_locked", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:collector_locked", "main"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:collector_on", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:collector_on", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:collector_on", "main"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:collector_locked_on", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:collector_locked_on", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:collector_locked_on", "main"}, -- insert items from hopper at side +}) + + + +end -- utils.digilines_supported + + + +-- diff --git a/lwcomponents/conduit.lua b/lwcomponents/conduit.lua new file mode 100644 index 0000000..580ec88 --- /dev/null +++ b/lwcomponents/conduit.lua @@ -0,0 +1,1267 @@ +local utils, mod_storage = ... +local S = utils.S + + + +local transfer_rate = 0.1 +local conduit_interval = 1.0 +local conduit_connections = utils.connections:new (mod_storage, "conduit_connections") + + +-- forward declare +local run_initialize = nil + + + +local function get_target_list (pos) + local tlist = conduit_connections:get_connected_ids (pos) + local list = { } + + for i = 1, #tlist do + if tlist[i].pos.x ~= pos.x or + tlist[i].pos.y ~= pos.y or + tlist[i].pos.y ~= pos.y then + + list[#list + 1] = tlist[i].id + end + end + + return list +end + + + +local function send_targets_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 + utils.digilines_receptor_send (pos, + utils.digilines_default_rules, + channel, + { action = "targets", + targets = get_target_list (pos) }) + end + end + end +end + + + +local function get_target_for_item (meta, inv, stack) + if meta then + if inv then + local slots = inv:get_size ("filter") + + for slot = 1, slots, 1 do + local s = inv:get_stack ("filter", slot) + + if s and not s:is_empty () and utils.is_same_item (s, stack) then + local target = meta:get_string ("target"..slot) + + if target ~= "" then + return target + end + end + end + end + + if meta:get_string ("target") ~= "" then + return meta:get_string ("target") + end + end + + return nil +end + + + +local function deliver_slot (pos, slot) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + local transfer_data = minetest.deserialize (meta:get_string ("transfer_data")) + + if inv then + local item = inv:get_stack ("transfer", slot) + + if transfer_data[slot] and item and not item:is_empty () then + local tnode = utils.get_far_node (transfer_data[slot].pos) + + if tnode and (tnode.name == "lwcomponents:conduit" or + tnode.name == "lwcomponents:conduit_locked") then + local tmeta = minetest.get_meta (transfer_data[slot].pos) + + if tmeta then + local tinv = tmeta:get_inventory () + + if tinv then + tinv:add_item ("main", item) + end + end + end + end + + transfer_data[slot] = nil + meta:set_string ("transfer_data", minetest.serialize (transfer_data)) + inv:set_stack ("transfer", slot, nil) + end + end +end + + + +local function run_deliveries (pos) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + local transfer_data = minetest.deserialize (meta:get_string ("transfer_data")) or { } + + if inv then + local slots = inv:get_size ("transfer") + local tm = minetest.get_us_time () + + for i = 1, slots do + if transfer_data[i] and + (transfer_data[i].due <= tm or + tm < (transfer_data[i].due - 1000000000)) then + + deliver_slot (pos, i) + end + end + end + + return not inv:is_empty ("transfer") + end + + return false +end + + + +local function deliver_all (pos) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("transfer") + + for i = 1, slots do + local item = inv:get_stack ("transfer", i) + + if item and not item:is_empty () then + deliver_slot (pos, i) + end + end + end + + meta:set_string ("transfer_data", minetest.serialize({ })) + inv:set_list ("transfer", { }) + end +end + + + +local function deliver_earliest (pos) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + local transfer_data = minetest.deserialize (meta:get_string ("transfer_data")) or { } + + if inv then + local slots = inv:get_size ("transfer") + local slot = 0 + local tm = 0 + + for i = 1, slots do + if transfer_data[i] and transfer_data[i].due < tm then + slot = i + tm = transfer_data[i].due + end + end + + if slot > 0 then + deliver_slot (pos, slot) + end + end + end +end + + + +local function get_transfer_free_slot (pos) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("transfer") + + for i = 1, slots do + local item = inv:get_stack ("transfer", i) + + if not item or item:is_empty () then + return i + end + end + end + end + + return 0 +end + + + +local function add_to_send_list (pos, item, destpos, distance) + local slot = get_transfer_free_slot (pos) + + while slot < 1 do + deliver_earliest (pos) + slot = get_transfer_free_slot (pos) + end + + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + local transfer_data = minetest.deserialize (meta:get_string ("transfer_data")) or { } + + if inv then + transfer_data[slot] = + { + pos = destpos, + due = minetest.get_us_time () + (transfer_rate * 1000000 * distance) + } + + inv:set_stack ("transfer", slot, item) + + meta:set_string ("transfer_data", minetest.serialize (transfer_data)) + + run_initialize (pos) + end + end +end + + + +local function send_to_target (pos, target, slot) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + if not slot then + local slots = inv:get_size ("main") + + for i = 1, slots do + local stack = inv:get_stack ("main", i) + + if not stack:is_empty () and stack:get_count () > 0 then + if target or get_target_for_item (meta, inv, stack) then + slot = i + break + end + end + end + + elseif type (slot) == "string" then + local name = slot + slot = nil + + local slots = inv:get_size ("main") + + for i = 1, slots do + local stack = inv:get_stack ("main", i) + + if not stack:is_empty () and stack:get_count () > 0 then + if name == stack:get_name () then + slot = i + break + end + end + end + + else + slot = tonumber (slot) + + end + + if slot then + local stack = inv:get_stack ("main", slot) + + if stack and not stack:is_empty () then + local item = ItemStack (stack) + + target = (target and tostring (target)) or + get_target_for_item (meta, inv, stack) + + if item and target then + item:set_count (1) + + local tpos, distance = conduit_connections:is_connected (pos, target) + + if tpos then + local tmeta = minetest.get_meta (tpos) + + if tmeta then + local tinv = tmeta:get_inventory () + + if tinv and tinv:room_for_item ("main", item) then + add_to_send_list (pos, item, tpos, distance) + + stack:set_count (stack:get_count () - 1) + inv:set_stack ("main", slot, stack) + + return true, target, slot + end + end + end + end + end + end + end + end + + return false +end + + + +local function get_formspec (pos) + local meta = minetest.get_meta (pos) + local automatic = "false" + local filters = "" + + if meta then + automatic = meta:get_string ("automatic") + end + + for i = 1, 8, 1 do + filters = string.format ("%sfield[12.7,%0.2f;3.0,0.8;target%d;;${target%d}]".. + "button[15.9,%0.2f;1.5,0.8;settarget%d;Set]", + filters, + (i * 1.25) + 0.35, + i, + i, + (i * 1.25) + 0.35, + i) + end + + return + "formspec_version[3]".. + "size[18.4,12.25;true]".. + "field[1.0,1.5;3.0,0.8;channel;Channel;${channel}]".. + "button[4.2,1.5;1.5,0.8;setchannel;Set]".. + "field[1.0,3.0;3.0,0.8;target;Target;${target}]".. + "button[4.2,3.0;1.5,0.8;settarget;Set]".. + "checkbox[1.0,4.5;automatic;Automatic;"..automatic.."]".. + "list[context;main;6.0,1.0;4,4;]".. + "list[current_player;main;1.0,6.5;8,4;]".. + "listring[]".. + "label[11.5,1.25;Filter]".. + "list[context;filter;11.5,1.5;1,8;]".. + filters +end + + + +local function run_conduit (pos, id) + local node = utils.get_far_node (pos) + + if node then + if node.name == "lwcomponents:conduit" or + node.name == "lwcomponents:conduit_locked" then + local meta = minetest.get_meta (pos) + + if meta and id == meta:get_int ("conduit_id") then + local automatic = meta:get_string ("automatic") == "true" + + if automatic then + send_to_target (pos) + end + + if run_deliveries (pos) or automatic then + minetest.after (conduit_interval, run_conduit, pos, id) + else + meta:set_int ("run_active", 0) + end + end + else + conduit_connections:remove_node (pos) + end + end +end + + + +run_initialize = function (pos) + local meta = minetest.get_meta (pos) + + if meta then + if meta:get_int ("run_active") == 0 then + meta:set_int ("run_active", 1) + minetest.after (conduit_interval, run_conduit, pos, meta:get_int ("conduit_id")) + end + end +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 send_inventory_message (pos) + if utils.digilines_supported then + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if meta and inv then + local channel = meta:get_string ("channel") + + if channel:len () > 0 then + local inventory = { } + 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 name = stack:get_name () + local description + 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 + + pallet_index = tstack.meta.palette_index + end + + if stack:get_short_description () ~= "" then + description = stack:get_short_description () + elseif stack:get_description () ~= "" then + description = stack:get_description () + else + 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 + + inventory[slot] = + { + name = stack:get_name (), + description = utils.unescape_description (description), + count = stack:get_count (), + custom = custom, + pallet_index = pallet_index, + } + else + inventory[slot] = + { + name = "", + description = "", + count = 0, + custom = false, + pallet_index = nil, + } + end + end + + utils.digilines_receptor_send (pos, + utils.digilines_default_rules, + channel, + { action = "inventory", + inventory = inventory }) + end + end + end + +end + + + +local function on_construct (pos) + conduit_connections:add_node (pos) +end + + + +local function on_destruct (pos) + deliver_all (pos) + conduit_connections:remove_node (pos) +end + + + +local function after_place_base (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local spec = + "formspec_version[3]".. + "size[7.0,3.8]".. + "field[0.5,1.0;6.0,0.8;channel;Channel;${channel}]".. + "button[2.0,2.3;3.0,0.8;setchannel;Set]" + + meta:set_string ("inventory", "{ main = { }, transfer = { }, filter = { } }") + meta:set_string ("formspec", spec) + meta:set_string ("transfer_data", minetest.serialize({ })) + meta:set_string ("automatic", "false") + meta:set_int ("conduit_id", math.random (1000000)) + + local inv = meta:get_inventory () + + inv:set_size ("main", 16) + inv:set_width ("main", 4) + + inv:set_size ("transfer", 32) + inv:set_width ("transfer", 8) + + inv:set_size ("filter", 8) + inv:set_width ("filter", 1) +end + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + after_place_base (pos, placer, itemstack, pointed_thing) + utils.pipeworks_after_place (pos) + + -- If return true no item is taken from itemstack + return false +end + + + +local function after_place_node_locked (pos, placer, itemstack, pointed_thing) + after_place_base (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", "Conduit (owned by "..placer:get_player_name ()..")") + end + + utils.pipeworks_after_place (pos) + + -- If return true no item is taken from itemstack + return false +end + + + +local function 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 on_receive_fields (pos, formname, fields, sender) + if not utils.can_interact_with_node (pos, sender) then + return + end + + if fields.setchannel then + local meta = minetest.get_meta (pos) + + if meta then + if tostring (fields.channel):len () > 0 then + if meta:get_string ("channel"):len () < 1 then + meta:set_string ("formspec", get_formspec (pos)) + end + + meta:set_string ("channel", fields.channel) + + conduit_connections:set_id (pos, tostring (fields.channel)) + + elseif meta:get_string ("channel"):len () > 0 then + if can_dig (pos, sender) ~= false then + local spec = + "formspec_version[3]".. + "size[7.0,3.8]".. + "field[0.5,1.0;6.0,0.8;channel;Channel;${channel}]".. + "button[2.0,2.3;3.0,0.8;setchannel;Set]" + + meta:set_string ("channel", fields.channel) + + meta:set_string ("formspec", spec) + + conduit_connections:set_id (pos, nil) + + elseif sender and sender:is_player () then + fields.channel = meta:get_string ("channel") + + local spec = + "formspec_version[3]".. + "size[8.0,4.0,false]".. + "label[2.5,1.0;Conduit not empty]".. + "button_exit[3.0,2.0;2.0,1.0;close;Close]" + + minetest.show_formspec (sender:get_player_name (), + "lwcomponents:conduit_not_empty", + spec) + + end + end + end + end + + if fields.settarget then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("target", fields.target) + end + end + + if fields.automatic then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("automatic", fields.automatic) + meta:set_string ("formspec", get_formspec (pos)) + + if fields.automatic == "true" then + meta:set_int ("run_active", 0) + end + + run_initialize (pos) + end + end + + for i = 1, 8, 1 do + if fields[string.format ("settarget%d", i)] then + local meta = minetest.get_meta (pos) + + if meta then + local name = string.format ("target%d", i) + + meta:set_string (name, fields[name]) + end + end + end +end + + + +local function 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 + utils.item_drop (stack, nil, pos) + on_destruct (pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function 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 + + + +local function 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 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 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 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 ~= "" and this_channel == channel then + if type (msg) == "string" then + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if m[1] == "target" then + meta:set_string ("target", (m[2] and tostring (m[2])) or "") + + elseif m[1] == "targets" then + send_targets_message (pos) + + elseif m[1] == "transfer" then + send_to_target (pos) + + elseif m[1] == "inventory" then + send_inventory_message (pos) + + end + + elseif type (msg) == "table" then + if msg.action and tostring (msg.action) == "transfer" then + send_to_target (pos, msg.target, msg.slot or msg.item) + end + + end + end + end + end, + } + } + end + + return nil +end + + + +local function mesecon_support () + if utils.mesecon_supported then + return + { + effector = + { + rules = utils.mesecon_flat_rules, + + action_on = function (pos, node) + send_to_target (pos) + end + } + } + end + + return nil +end + + + +local function pipeworks_support () + if utils.pipeworks_supported then + return + { + priority = 100, + input_inventory = "main", + connect_sides = { left = 1, right = 1, front = 1, back = 1, bottom = 1, top = 1 }, + + insert_object = function (pos, node, stack, direction) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + return inv:add_item ("main", stack) + end + + return stack + end, + + can_insert = function (pos, node, stack, direction) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + return inv:room_for_item ("main", stack) + end + + return false + end, + + can_remove = function (pos, node, stack, dir) + -- returns the maximum number of items of that stack that can be removed + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + local slots = inv:get_size ("main") + + for i = 1, slots, 1 do + local s = inv:get_stack ("main", i) + + if s and not s:is_empty () and utils.is_same_item (stack, s) then + return s:get_count () + end + end + end + + return 0 + end, + + remove_items = function (pos, node, stack, dir, count) + -- removes count items and returns them + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + local left = count + + if inv then + local slots = inv:get_size ("main") + + for i = 1, slots, 1 do + local s = inv:get_stack ("main", i) + + if s and not s:is_empty () and utils.is_same_item (s, stack) then + if s:get_count () > left then + s:set_count (s:get_count () - left) + inv:set_stack ("main", i, s) + left = 0 + else + left = left - s:get_count () + inv:set_stack ("main", i, nil) + end + end + + if left == 0 then + break + end + end + end + + local result = ItemStack (stack) + result:set_count (count - left) + + return result + end + } + end + + return nil +end + + + +local conduit_groups = { cracky = 3, wires_connect = 1 } +if utils.pipeworks_supported then + conduit_groups.tubedevice = 1 + conduit_groups.tubedevice_receiver = 1 +end + + + +minetest.register_node("lwcomponents:conduit", { + description = S("Conduit"), + short_description = S("Conduit"), + tiles = { "lwconduit.png" }, + drawtype = "nodebox", + node_box = { + type = "connected", + fixed = { -0.375, -0.375, -0.375, 0.375, 0.375, 0.375 }, -- body + connect_top = { -0.3125, 0.3125, -0.3125, 0.3125, 0.5, 0.3125 }, -- top + connect_bottom = { -0.3125, -0.5, -0.3125, 0.3125, -0.3125, 0.3125 }, -- down + connect_front = { -0.3125, -0.3125, -0.5, 0.3125, 0.3125, -0.3125 }, -- front + connect_back = { -0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125 }, -- back + connect_left = { -0.5, -0.3125, -0.3125, -0.3125, 0.3125, 0.3125 }, -- left + connect_right = { 0.3125, -0.3125, -0.3125, 0.5, 0.3125, 0.3125 }, -- right + }, + connect_sides = { "top", "bottom", "front", "back", "left", "right" }, + connects_to = { "lwcomponents:conduit", "lwcomponents:conduit_locked", + "lwcomponents:hopper", "lwcomponents:hopper_horz", + "hopper:hopper", "hopper:hopper_side", + "group:tube", "pipeworks:filter", "pipeworks:mese_filter", + "pipeworks:digiline_filter" }, + selection_box = { + type = "connected", + fixed = { -0.375, -0.375, -0.375, 0.375, 0.375, 0.375 }, -- body + connect_top = { -0.3125, 0.3125, -0.3125, 0.3125, 0.5, 0.3125 }, -- top + connect_bottom = { -0.3125, -0.5, -0.3125, 0.3125, -0.3125, 0.3125 }, -- down + connect_front = { -0.3125, -0.3125, -0.5, 0.3125, 0.3125, -0.3125 }, -- front + connect_back = { -0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125 }, -- back + connect_left = { -0.5, -0.3125, -0.3125, -0.3125, 0.3125, 0.3125 }, -- left + connect_right = { 0.3125, -0.3125, -0.3125, 0.5, 0.3125, 0.3125 }, -- right + }, + collision_box = { + type = "connected", + fixed = { -0.375, -0.375, -0.375, 0.375, 0.375, 0.375 }, -- body + connect_top = { -0.3125, 0.3125, -0.3125, 0.3125, 0.5, 0.3125 }, -- top + connect_bottom = { -0.3125, -0.5, -0.3125, 0.3125, -0.3125, 0.3125 }, -- down + connect_front = { -0.3125, -0.3125, -0.5, 0.3125, 0.3125, -0.3125 }, -- front + connect_back = { -0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125 }, -- back + connect_left = { -0.5, -0.3125, -0.3125, -0.3125, 0.3125, 0.3125 }, -- left + connect_right = { 0.3125, -0.3125, -0.3125, 0.5, 0.3125, 0.3125 }, -- right + }, + is_ground_content = false, + groups = table.copy (conduit_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + on_construct = on_construct, + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + can_dig = can_dig, + after_dig_node = utils.pipeworks_after_dig, + on_blast = on_blast, + on_rightclick = on_rightclick, + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_take = allow_metadata_inventory_take, + allow_metadata_inventory_move = allow_metadata_inventory_move + +}) + + + +minetest.register_node("lwcomponents:conduit_locked", { + description = S("Conduit (locked)"), + short_description = S("Conduit (locked)"), + tiles = { "lwconduit.png" }, + drawtype = "nodebox", + node_box = { + type = "connected", + fixed = { -0.375, -0.375, -0.375, 0.375, 0.375, 0.375 }, -- body + connect_top = { -0.3125, 0.3125, -0.3125, 0.3125, 0.5, 0.3125 }, -- top + connect_bottom = { -0.3125, -0.5, -0.3125, 0.3125, -0.3125, 0.3125 }, -- down + connect_front = { -0.3125, -0.3125, -0.5, 0.3125, 0.3125, -0.3125 }, -- front + connect_back = { -0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125 }, -- back + connect_left = { -0.5, -0.3125, -0.3125, -0.3125, 0.3125, 0.3125 }, -- left + connect_right = { 0.3125, -0.3125, -0.3125, 0.5, 0.3125, 0.3125 }, -- right + }, + connect_sides = { "top", "bottom", "front", "back", "left", "right" }, + connects_to = { "lwcomponents:conduit", "lwcomponents:conduit_locked", + "lwcomponents:hopper", "lwcomponents:hopper_horz", + "hopper:hopper", "hopper:hopper_side", + "group:tube", "pipeworks:filter", "pipeworks:mese_filter", + "pipeworks:digiline_filter" }, + selection_box = { + type = "connected", + fixed = { -0.375, -0.375, -0.375, 0.375, 0.375, 0.375 }, -- body + connect_top = { -0.3125, 0.3125, -0.3125, 0.3125, 0.5, 0.3125 }, -- top + connect_bottom = { -0.3125, -0.5, -0.3125, 0.3125, -0.3125, 0.3125 }, -- down + connect_front = { -0.3125, -0.3125, -0.5, 0.3125, 0.3125, -0.3125 }, -- front + connect_back = { -0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125 }, -- back + connect_left = { -0.5, -0.3125, -0.3125, -0.3125, 0.3125, 0.3125 }, -- left + connect_right = { 0.3125, -0.3125, -0.3125, 0.5, 0.3125, 0.3125 }, -- right + }, + collision_box = { + type = "connected", + fixed = { -0.375, -0.375, -0.375, 0.375, 0.375, 0.375 }, -- body + connect_top = { -0.3125, 0.3125, -0.3125, 0.3125, 0.5, 0.3125 }, -- top + connect_bottom = { -0.3125, -0.5, -0.3125, 0.3125, -0.3125, 0.3125 }, -- down + connect_front = { -0.3125, -0.3125, -0.5, 0.3125, 0.3125, -0.3125 }, -- front + connect_back = { -0.3125, -0.3125, 0.5, 0.3125, 0.3125, 0.3125 }, -- back + connect_left = { -0.5, -0.3125, -0.3125, -0.3125, 0.3125, 0.3125 }, -- left + connect_right = { 0.3125, -0.3125, -0.3125, 0.5, 0.3125, 0.3125 }, -- right + }, + is_ground_content = false, + groups = table.copy (conduit_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + on_construct = on_construct, + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node_locked, + can_dig = can_dig, + after_dig_node = utils.pipeworks_after_dig, + on_blast = on_blast, + on_rightclick = on_rightclick, + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_take = allow_metadata_inventory_take, + allow_metadata_inventory_move = allow_metadata_inventory_move +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:conduit", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:conduit", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:conduit", "main"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:conduit_locked", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:conduit_locked", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:conduit_locked", "main"}, -- insert items from hopper at side +}) + + + +-- legacy code v0.1.23 (24-2-22) +local function convert_conduits () + local list = conduit_connections:get_full_list () + + for _, data in ipairs (list) do + local node = utils.get_far_node (data.pos) + + if node and (node.name == "lwcomponents:conduit" or + node.name == "lwcomponents:conduit_locked") then + local meta = minetest.get_meta (data.pos) + + if meta then + if meta:get_int ("conduit_id") == 0 then + meta:set_int ("conduit_id", math.random (1000000)) + + if meta:get_string ("automatic") == "true" then + meta:set_int ("run_active", 1) + else + local inv = meta:get_inventory () + + if inv and not inv:is_empty ("transfer") then + meta:set_int ("run_active", 1) + end + end + end + + -- added v0.1.25 (2-3-22) + local tmeta = meta:to_table () + + if tmeta and tmeta.inventory then + if not tmeta.inventory.filter then + tmeta.inventory.filter = + { + [1] = "", [2] = "", [3] = "", [4] = "", + [5] = "", [6] = "", [7] = "", [8] = "" + } + + meta:from_table (tmeta) + + if meta:get_string ("channel") ~= "" then + meta:set_string ("formspec", get_formspec (data.pos)) + end + end + end + end + end + end +end + + + +local function restart_conduits () + convert_conduits () + + local list = conduit_connections:get_id_list () + + for _, data in ipairs (list) do + local meta = minetest.get_meta (data.pos) + + if meta and meta:get_int ("run_active") ~= 0 then + minetest.after (conduit_interval + (math.random (10) / 10), + run_conduit, data.pos, meta:get_int ("conduit_id")) + end + end + +end + + + +minetest.register_on_mods_loaded (function () + minetest.after (3.0, restart_conduits) +end) + + + +-- diff --git a/lwcomponents/connections.lua b/lwcomponents/connections.lua new file mode 100644 index 0000000..eaa3007 --- /dev/null +++ b/lwcomponents/connections.lua @@ -0,0 +1,334 @@ + + + +local connections = { } + + + +function connections:new (mod_storage, name) + local obj = { } + + setmetatable (obj, self) + self.__index = self + + obj.connector_list = { } + obj.name = tostring (name) + obj.storage = mod_storage + + if mod_storage then + local stored = mod_storage:get_string (obj.name) + + if stored == "" then + stored = "{ }" + end + + obj.connector_list = minetest.deserialize (stored) + + if not obj.connector_list then + obj.connector_list = { } + end + end + + return obj +end + + + +function connections:load () + if self.storage then + local stored = self.storage:get_string (self.name) + + if stored == "" then + stored = "{ }" + end + + self.connector_list = minetest.deserialize (stored) + end +end + + + +function connections:store () + if self.storage then + self.storage:set_string (self.name, minetest.serialize (self.connector_list)) + end +end + + + + +function connections:add_node (pos, id) + self.connector_list[minetest.pos_to_string (pos, 0)] = + { + id = (id and tostring (id)) or nil, + checked = false + } + + self:store () +end + + + +function connections:remove_node (pos) + self.connector_list[minetest.pos_to_string (pos, 0)] = nil + + self:store () +end + + + +function connections:set_id (pos, id) + local con = self.connector_list[minetest.pos_to_string (pos, 0)] + + if con then + con.id = (id and tostring (id)) or nil + + self:store () + + return true + end + + return false +end + + + +local function is_connected (self, pos, id, tally, test_coords) + if not id then + return nil + end + + local con = self.connector_list[minetest.pos_to_string (pos, 0)] + + if con and not con.checked then + con.checked = true + + if con.id == id then + con.checked = false + + return pos, tally + end + + for i = 1, #test_coords do + local result, agg = is_connected (self, + { + x = pos.x + test_coords[i].x, + y = pos.y + test_coords[i].y, + z = pos.z + test_coords[i].z + }, + id, + tally, + test_coords) + + if result then + con.checked = false + + return result, (tally + agg + 1) + end + end + + con.checked = false + end + + return nil, 0 +end + + + +function connections:is_connected (pos, id) + return is_connected (self, + pos, + tostring (id), + 0, + { + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 }, + { x = 0, y = 0, z = 1 }, + { x = 0, y = 0, z = -1 }, + { x = 0, y = 1, z = 0 }, + { x = 0, y = -1, z = 0 } + }) +end + + + +function connections:is_connected_horizontal (pos, id) + return is_connected (self, + pos, + tostring (id), + 0, + { + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 }, + { x = 0, y = 0, z = 1 }, + { x = 0, y = 0, z = -1 } + }) +end + + + +function connections:is_connected_vertical (pos, id) + return is_connected (self, + pos, + tostring (id), + 0, + { + { x = 0, y = 1, z = 0 }, + { x = 0, y = -1, z = 0 } + }) +end + + + +local function get_connected_ids (self, pos, test_coords, list) + local con = self.connector_list[minetest.pos_to_string (pos, 0)] + + if con and not con.checked then + con.checked = true + + if con.id then + list[#list + 1] = + { + pos = { x = pos.x, y = pos.y, z = pos.z }, + id = con.id + } + end + + for i = 1, #test_coords do + get_connected_ids (self, + { + x = pos.x + test_coords[i].x, + y = pos.y + test_coords[i].y, + z = pos.z + test_coords[i].z + }, + test_coords, + list) + end + + con.checked = false + end + + return list +end + + + +function connections:get_connected_ids (pos) + local list = get_connected_ids (self, + pos, + { + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 }, + { x = 0, y = 0, z = 1 }, + { x = 0, y = 0, z = -1 }, + { x = 0, y = 1, z = 0 }, + { x = 0, y = -1, z = 0 } + }, + { }) + + for i = #list, 1, -1 do + for j = 1, i - 1, 1 do + if list[i].pos.x == list[j].pos.x and + list[i].pos.y == list[j].pos.y and + list[i].pos.z == list[j].pos.z then + + list[i] = nil + break + end + end + end + + return list +end + + + +function connections:get_connected_ids_horizontal (pos) + return get_connected_ids (self, + pos, + { + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 }, + { x = 0, y = 0, z = 1 }, + { x = 0, y = 0, z = -1 } + }, + { }) +end + + + +function connections:get_connected_ids_vertical (pos) + return get_connected_ids (self, + pos, + { + { x = 0, y = 1, z = 0 }, + { x = 0, y = -1, z = 0 } + }, + { }) +end + + + +function connections:get_connected_ids_north_south (pos) + return get_connected_ids (self, + pos, + { + { x = 0, y = 0, z = 1 }, + { x = 0, y = 0, z = -1 } + }, + { }) +end + + + +function connections:get_connected_ids_east_west (pos) + return get_connected_ids (self, + pos, + { + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 } + }, + { }) +end + + + +function connections:get_id_list () + local list = { } + + for spos, data in pairs (self.connector_list) do + if data.id then + list[#list + 1] = + { + pos = minetest.string_to_pos (spos), + id = data.id + } + end + end + + return list +end + + + +function connections:get_full_list () + local list = { } + + for spos, data in pairs (self.connector_list) do + list[#list + 1] = + { + pos = minetest.string_to_pos (spos), + id = data.id + } + end + + return list +end + + + +return connections + + + +--