From b20501ef6a333974b51bc7f3bef79be03dbadd48 Mon Sep 17 00:00:00 2001 From: yunomavori Date: Fri, 27 Jun 2025 17:50:53 +0200 Subject: [PATCH] Upload files to "lwcomponents" --- lwcomponents/description.txt | 1 + lwcomponents/destroyer.lua | 164 +++++++ lwcomponents/detector.lua | 906 +++++++++++++++++++++++++++++++++++ lwcomponents/digiswitch.lua | 378 +++++++++++++++ lwcomponents/dispenser.lua | 600 +++++++++++++++++++++++ 5 files changed, 2049 insertions(+) create mode 100644 lwcomponents/description.txt create mode 100644 lwcomponents/destroyer.lua create mode 100644 lwcomponents/detector.lua create mode 100644 lwcomponents/digiswitch.lua create mode 100644 lwcomponents/dispenser.lua diff --git a/lwcomponents/description.txt b/lwcomponents/description.txt new file mode 100644 index 0000000..075b9df --- /dev/null +++ b/lwcomponents/description.txt @@ -0,0 +1 @@ +Various components for mesecons and digilines. diff --git a/lwcomponents/destroyer.lua b/lwcomponents/destroyer.lua new file mode 100644 index 0000000..ee3af3c --- /dev/null +++ b/lwcomponents/destroyer.lua @@ -0,0 +1,164 @@ +local utils = ... +local S = utils.S + + + +local function trash (pos) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + local stack = inv:get_stack ("trash", 1) + + if stack and not stack:is_empty () then + utils.on_destroy (stack) + + inv:set_stack ("trash", 1, nil) + end + end +end + + + +local function trash_delayed (pos) + minetest.after (0.1, trash, pos) +end + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + meta:set_string ("inventory", "{ trash = { [1] = '' } }") + meta:set_string ("formspec", + "formspec_version[3]".. + "size[11.75,8.5,false]".. + "label[5.15,1.0;Destroy]".. + "list[context;trash;5.3,1.25;1,1;]".. + "list[current_player;main;1.0,2.75;8,4;]".. + "listring[]") + + inv:set_size ("trash", 1) + inv:set_width ("trash", 1) + end + end + + utils.pipeworks_after_place (pos) + + -- If return true no item is taken from itemstack + return false +end + + + +local function on_metadata_inventory_put (pos, listname, index, stack, player) + if listname == "trash" then + trash_delayed (pos) + end +end + + + +local function on_metadata_inventory_move (pos, from_list, from_index, + to_list, to_index, count, player) + if to_list == "trash" then + trash_delayed (pos) + end +end + + + +local function pipeworks_support () + if utils.pipeworks_supported then + return + { + priority = 100, + input_inventory = "trash", + 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 + trash_delayed (pos) + + return inv:add_item ("trash", 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 ("trash", 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 + return 0 + end, + + remove_items = function (pos, node, stack, dir, count) + -- removes count items and returns them + return stack + end + } + end + + return nil +end + + + +local destroyer_groups = { cracky = 3 } +if utils.pipeworks_supported then + destroyer_groups.tubedevice = 1 + destroyer_groups.tubedevice_receiver = 1 +end + + + +minetest.register_node("lwcomponents:destroyer", { + description = S("Destroyer"), + drawtype = "normal", + tiles = { "lwcomponents_destroyer_top.png", "lwcomponents_destroyer_top.png", + "lwcomponents_destroyer_side.png", "lwcomponents_destroyer_side.png", + "lwcomponents_destroyer_side.png", "lwcomponents_destroyer_side.png" }, + is_ground_content = false, + groups = table.copy (destroyer_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + + tube = pipeworks_support (), + + after_place_node = after_place_node, + after_dig_node = utils.pipeworks_after_dig, + on_metadata_inventory_put = on_metadata_inventory_put, + on_metadata_inventory_move = on_metadata_inventory_move, +}) + + + +utils.hopper_add_container({ + {"bottom", "lwcomponents:destroyer", "trash"}, -- insert items below from hopper above + {"side", "lwcomponents:destroyer", "trash"}, -- insert items from hopper at side +}) + + + +-- diff --git a/lwcomponents/detector.lua b/lwcomponents/detector.lua new file mode 100644 index 0000000..871ab03 --- /dev/null +++ b/lwcomponents/detector.lua @@ -0,0 +1,906 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +local detect_interval = 0.5 + + + +local function mesecons_on (pos) + local meta = minetest.get_meta (pos) + + if meta then + if meta:get_int ("power_on") == 0 then + utils.mesecon_receptor_on (pos, utils.mesecon_default_rules) + meta:set_int ("power_on", 1) + local node = utils.get_far_node (pos) + + if node then + if node.name == "lwcomponents:detector_locked_on" then + node.name = "lwcomponents:detector_locked_on_on" + elseif node.name == "lwcomponents:detector_on" then + node.name = "lwcomponents:detector_on_on" + end + + minetest.swap_node (pos, node) + end + end + end +end + + + +local function mesecons_off (pos) + local meta = minetest.get_meta (pos) + + if meta then + if meta:get_int ("power_on") ~= 0 then + utils.mesecon_receptor_off (pos, utils.mesecon_default_rules) + meta:set_int ("power_on", 0) + local node = utils.get_far_node (pos) + + if node then + if node.name == "lwcomponents:detector_locked_on_on" then + node.name = "lwcomponents:detector_locked_on" + elseif node.name == "lwcomponents:detector_on_on" then + node.name = "lwcomponents:detector_on" + end + + minetest.swap_node (pos, node) + end + end + end +end + + + +local function to_relative_coords (pos, testpos) + local base = { x = testpos.x - pos.x, + y = testpos.y - pos.y, + z = testpos.z - pos.z } + local node = minetest.get_node (pos) + + if node then + if node.param2 == 3 then -- +x + return { x = (base.z * -1), y = base.y, z = base.x } + elseif node.param2 == 0 then -- -z + return { x = (base.x * -1), y = base.y, z = (base.z * -1) } + elseif node.param2 == 1 then -- -x + return { x = base.z, y = base.y, z = (base.x * -1) } + elseif node.param2 == 2 then -- +z + return { x = base.x, y = base.y, z = base.z } + end + end + + return { x = 0, y = 0, z = 0 } +end + + + +local function send_detect_message (pos, detected_list) + 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 = "detect", + detected = detected_list }) + end + end + end +end + + + +local function filter_item (pos, mode, testpos) + local base = { x = math.floor (testpos.x - pos.x + 0.5), + y = math.floor (testpos.y - pos.y + 0.5), + z = math.floor (testpos.z - pos.z + 0.5) } + + if base.x == 0 and base.y == 0 and base.z == 0 then + return false + end + + if mode == 1 then + -- all + return true + + elseif mode == 2 then + -- forward + local node = minetest.get_node (pos) + + if node then + if node.param2 == 0 then + -- -z + return (base.x == 0 and base.y == 0 and base.z < 0) + elseif node.param2 == 1 then + -- -x + return (base.x < 0 and base.y == 0 and base.z == 0) + elseif node.param2 == 2 then + -- +z + return (base.x == 0 and base.y == 0 and base.z > 0) + elseif node.param2 == 3 then + -- +x + return (base.x > 0 and base.y == 0 and base.z == 0) + end + end + + elseif mode == 3 then + -- up + return (base.x == 0 and base.z == 0 and base.y > 0) + + elseif mode == 4 then + -- down + return (base.x == 0 and base.z == 0 and base.y < 0) + + end + + return false +end + + + +local function get_entity_height (objref) + if objref.get_luaentity then + local entity = objref:get_luaentity () + + if entity and entity.name then + local def = minetest.registered_entities[entity.name] + + if def and type (def.collisionbox) == "table" and + type (def.collisionbox[5]) == "number" then + + return def.collisionbox[5] + end + end + end + + local props = objref:get_properties () + if props and props.collisionbox and type (props.collisionbox) == "table" and + type (props.collisionbox[5]) == "number" then + + return props.collisionbox[5] + end + + return 0 +end + + + +local function detect (pos) + local meta = minetest.get_meta (pos) + local detected = false + + if meta then + local radius = meta:get_int ("radius") + local mode = meta:get_int ("mode") + local object = minetest.get_objects_inside_radius (pos, radius + 0.5) + local detected_list = { } + + for i = 1, #object, 1 do + if object[i]:is_player () then + + -- player + if meta:get_string ("players") == "true" and + filter_item (pos, mode, object[i]:get_pos ()) then + + detected_list[#detected_list + 1] = + { + type = "player", + name = object[i]:get_player_name (), + label = object[i]:get_player_name (), + pos = to_relative_coords (pos, object[i]:get_pos ()), + count = 1, + hp = object[i]:get_hp (), + height = get_entity_height (object[i]) + } + + detected = true + end + + elseif object[i].get_luaentity and object[i]:get_luaentity () and + object[i]:get_luaentity ().name and + object[i]:get_luaentity ().name == "__builtin:item" then + + -- drop + if meta:get_string ("drops") == "true" and + filter_item (pos, mode, object[i]:get_pos ()) then + + local stack = ItemStack (object[i]:get_luaentity ().itemstring or "") + + if stack and not stack:is_empty () then + + detected_list[#detected_list + 1] = + { + type = "drop", + name = stack:get_name (), + label = stack:get_name (), + pos = to_relative_coords (pos, object[i]:get_pos ()), + count = stack:get_count (), + hp = 0, + height = 0 + } + + detected = true + end + + end + + elseif object[i].get_pos and object[i]:get_pos () then + + -- entity + if meta:get_string ("entities") == "true" and + filter_item (pos, mode, object[i]:get_pos ()) then + + local name = object[i]:get_nametag_attributes () + local label = "" + + if type (name) == "table" then + label = tostring (name.text or "") + end + + name = (object[i].get_luaentity and + object[i]:get_luaentity () and + object[i]:get_luaentity ().name) or "" + + detected_list[#detected_list + 1] = + { + type = "entity", + name = name, + label = label, + pos = to_relative_coords (pos, object[i]:get_pos ()), + count = 1, + hp = object[i]:get_hp (), + height = get_entity_height (object[i]) + } + + detected = true + + end + + end + end + + + if meta:get_string ("nodes") == "true" then + for y = (pos.y - radius), (pos.y + radius) do + for x = (pos.x - radius), (pos.x + radius) do + for z = (pos.z - radius), (pos.z + radius) do + local testpos = { x = x, y = y, z = z } + local node = minetest.get_node (testpos) + + if node and node.name ~= "air" and node.name ~= "ignore" and + filter_item (pos, mode, testpos) then + + detected_list[#detected_list + 1] = + { + type = "node", + name = node.name, + label = node.name, + pos = testpos, + count = 1, + hp = 0, + height = 0 + } + + detected = true + end + end + end + end + end + + if detected then + mesecons_on (pos) + + send_detect_message (pos, detected_list) + else + mesecons_off (pos) + end + end +end + + + +local function get_form_spec (is_off, radius, entities, players, drops, nodes, mode) + return + "formspec_version[3]\n".. + "size[11.75,9.0;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".. + "field[1.0,2.5;4.0,0.8;radius;Radius;"..tostring (radius).."]\n".. + "button[5.5,2.5;2.0,0.8;setradius;Set]\n".. + "checkbox[1.0,4.4;entities;Entities;"..entities.."]\n".. + "checkbox[1.0,5.4;players;Players;"..players.."]\n".. + "checkbox[1.0,6.4;drops;Drops;"..drops.."]\n".. + "checkbox[1.0,7.4;nodes;Nodes;"..nodes.."]\n".. + "textlist[4.875,4.0;5.875,4.0;mode;All,Forward,Up,Down;"..tostring (mode)..";false]" +end + + + +local function update_form_spec (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + local is_off = node.name == "lwcomponents:detector" or + node.name == "lwcomponents:detector_locked" + + meta:set_string ("formspec", + get_form_spec (is_off, + meta:get_int ("radius"), + meta:get_string ("entities"), + meta:get_string ("players"), + meta:get_string ("drops"), + meta:get_string ("nodes"), + meta:get_int ("mode"))) + end +end + + + +local function start_detector (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:detector" then + node.name = "lwcomponents:detector_on" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (detect_interval) + update_form_spec (pos) + + elseif node.name == "lwcomponents:detector_locked" then + node.name = "lwcomponents:detector_locked_on" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (detect_interval) + update_form_spec (pos) + + end + end +end + + + +local function stop_detector (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:detector_on" or + node.name == "lwcomponents:detector_on_on" then + node.name = "lwcomponents:detector" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):stop () + mesecons_off (pos) + update_form_spec (pos) + + elseif node.name == "lwcomponents:detector_locked_on" or + node.name == "lwcomponents:detector_locked_on_on" then + node.name = "lwcomponents:detector_locked" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):stop () + mesecons_off (pos) + update_form_spec (pos) + + end + end +end + + + +local function on_destruct (pos) + minetest.get_node_timer (pos):stop () + + mesecons_off (pos) +end + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local is_off = itemstack and (itemstack:get_name () == "lwcomponents:detector" or + itemstack:get_name () == "lwcomponents:detector_locked") + + meta:set_string ("formspec", get_form_spec (is_off, 1, 0, 0, 0, 0, 1)) + + meta:set_string ("entities", "false") + meta:set_string ("players", "false") + meta:set_string ("drops", "false") + meta:set_string ("nodes", "false") + meta:set_int ("mode", 1) + meta:set_int ("radius", 1) + meta:set_int ("power_on", 0) + + -- 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_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", "Detector (owned by "..placer:get_player_name ()..")") + end + + -- 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 + end + + if fields.setradius then + local meta = minetest.get_meta (pos) + + if meta then + local radius = math.min (math.max (tonumber (fields.radius) or 1, 1), 5) + + meta:set_int ("radius", radius) + update_form_spec (pos) + end + end + + if fields.start then + start_detector (pos) + end + + if fields.stop then + stop_detector (pos) + end + + if fields.entities ~= nil then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("entities", fields.entities) + update_form_spec (pos) + end + end + + if fields.players ~= nil then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("players", fields.players) + update_form_spec (pos) + end + end + + if fields.drops ~= nil then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("drops", fields.drops) + update_form_spec (pos) + end + end + + if fields.nodes ~= nil then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("nodes", fields.nodes) + update_form_spec (pos) + end + end + + if fields.mode then + local event = minetest.explode_textlist_event (fields.mode) + + if event.type == "CHG" then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_int ("mode", event.index) + update_form_spec (pos) + end + end + end +end + + + +local function can_dig (pos, player) + if not utils.can_interact_with_node (pos, player) then + return false + end + + return true +end + + + +local function on_blast (pos, intensity) + local meta = minetest.get_meta (pos) + + if meta then + if intensity >= 1.0 then + on_destruct (pos) + minetest.remove_node (pos) + + else -- intensity < 1.0 + 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_timer (pos, elapsed) + detect (pos) + + return true +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 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_detector (pos) + + elseif m[1] == "stop" then + stop_detector (pos) + + elseif m[1] == "radius" then + local radius = math.min (math.max (tonumber (m[2] or 1) or 1, 1), 5) + + meta:set_int ("radius", radius) + update_form_spec (pos) + + elseif m[1] == "entities" then + meta:set_string ("entities", ((m[2] == "true") and "true") or "false") + update_form_spec (pos) + + elseif m[1] == "players" then + meta:set_string ("players", ((m[2] == "true") and "true") or "false") + update_form_spec (pos) + + elseif m[1] == "drops" then + meta:set_string ("drops", ((m[2] == "true") and "true") or "false") + update_form_spec (pos) + + elseif m[1] == "nodes" then + meta:set_string ("nodes", ((m[2] == "true") and "true") or "false") + update_form_spec (pos) + + + elseif m[1] == "mode" then + if m[2] == "all" then + meta:set_int ("mode", 1) + update_form_spec (pos) + + elseif m[2] == "forward" then + meta:set_int ("mode", 2) + update_form_spec (pos) + + elseif m[2] == "up" then + meta:set_int ("mode", 3) + update_form_spec (pos) + + elseif m[2] == "down" then + meta:set_int ("mode", 4) + update_form_spec (pos) + + end + end + end + end + end, + } + } + end + + return nil +end + + + +local function mesecon_support_on () + if utils.mesecon_supported then + return + { + receptor = + { + state = utils.mesecon_state_on, + rules = utils.mesecon_default_rules + } + } + end + + return nil +end + + + +local function mesecon_support_off () + if utils.mesecon_supported then + return + { + receptor = + { + state = utils.mesecon_state_off, + rules = utils.mesecon_default_rules + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:detector", { + description = S("Detector"), + tiles = { "lwdetector_face.png", "lwdetector_face.png", "lwdetector.png", + "lwdetector.png", "lwdetector.png", "lwdetector_face.png"}, + is_ground_content = false, + groups = { cracky = 3, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support_off (), + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:detector_locked", { + description = S("Detector (locked)"), + tiles = { "lwdetector_face.png", "lwdetector_face.png", "lwdetector.png", + "lwdetector.png", "lwdetector.png", "lwdetector_face.png"}, + is_ground_content = false, + groups = { cracky = 3, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support_off (), + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:detector_on", { + description = S("Detector"), + tiles = { "lwdetector_face_on.png", "lwdetector_face_on.png", "lwdetector.png", + "lwdetector.png", "lwdetector.png", "lwdetector_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support_off (), + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:detector_locked_on", { + description = S("Detector (locked)"), + tiles = { "lwdetector_face_on.png", "lwdetector_face_on.png", "lwdetector.png", + "lwdetector.png", "lwdetector.png", "lwdetector_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support_off (), + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:detector_on_on", { + description = S("Detector"), + tiles = { "lwdetector_face_on.png", "lwdetector_face_on.png", "lwdetector.png", + "lwdetector.png", "lwdetector.png", "lwdetector_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support_on (), + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:detector_locked_on_on", { + description = S("Detector (locked)"), + tiles = { "lwdetector_face_on.png", "lwdetector_face_on.png", "lwdetector.png", + "lwdetector.png", "lwdetector.png", "lwdetector_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support_on (), + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +end -- utils.digilines_supported or utils.mesecon_supported + + + +-- diff --git a/lwcomponents/digiswitch.lua b/lwcomponents/digiswitch.lua new file mode 100644 index 0000000..596704a --- /dev/null +++ b/lwcomponents/digiswitch.lua @@ -0,0 +1,378 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported and utils.mesecon_supported then + + + +local function get_mesecon_rule_for_side (side) + if side == "white" then + return { { x = 0, y = 1, z = 0 } } + elseif side == "black" then + return { { x = 0, y = -1, z = 0 } } + elseif side == "red" then + return { { x = -1, y = 0, z = 0 } } + elseif side == "green" then + return { { x = 1, y = 0, z = 0 } } + elseif side == "blue" then + return { { x = 0, y = 0, z = -1 } } + elseif side == "yellow" then + return { { x = 0, y = 0, z = 1 } } + elseif side == "switch" then + return nil + else + return + { + { 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 +end + + + +local side_bits = +{ + ["white"] = 0, + ["black"] = 1, + ["red"] = 2, + ["green"] = 3, + ["blue"] = 4, + ["yellow"] = 5 +} + + + +local function get_side_bit (side) + if side then + if side == "switch" then + return 64 + end + + local bit = side_bits[side] + + if bit then + return math.pow (2, bit) + end + end + + return 63 +end + + + +local function is_side_on (bits, side) + local bit = get_side_bit (side) + + for i = 0, 6, 1 do + if (bit % 2) == 1 and (bits % 2) ~= 1 then + return false + end + + bit = math.floor (bit / 2) + bits = math.floor (bits / 2) + end + + return true +end + + + +local function set_side_bit (bits, side, on) + local bit = get_side_bit (side) + local result = 0 + + for i = 0, 6, 1 do + if (bit % 2) == 1 then + if on then + result = result + math.pow (2, i) + end + elseif (bits % 2) == 1 then + result = result + math.pow (2, i) + end + + bit = math.floor (bit / 2) + bits = math.floor (bits / 2) + end + + return result +end + + + +local function get_powered_rules (node) + local rules = { } + + if is_side_on (node.param1, "switch") then + rules = table.copy (utils.mesecon_default_rules) + + if is_side_on (node.param1, "white") then + rules[#rules + 1] = get_mesecon_rule_for_side ("white")[1] + end + + if is_side_on (node.param1, "black") then + rules[#rules + 1] = get_mesecon_rule_for_side ("black")[1] + end + else + local sides = + { + "white", + "black", + "red", + "green", + "blue", + "yellow", + } + + for _, side in ipairs (sides) do + if is_side_on (node.param1, side) then + rules[#rules + 1] = get_mesecon_rule_for_side (side)[1] + end + end + end + + return rules +end + + + +local function get_node_name (node) + if node.param1 ~= 0 then + return "lwcomponents:digiswitch_on" + end + + return "lwcomponents:digiswitch" +end + + + +local function switch_on (pos, side) + utils.mesecon_receptor_on (pos, get_mesecon_rule_for_side (side)) + + local node = utils.get_far_node (pos) + if node then + node.param1 = set_side_bit (node.param1, side, true) + node.name = get_node_name (node) + minetest.swap_node (pos, node) + end +end + + + +local function switch_off (pos, side) + utils.mesecon_receptor_off (pos, get_mesecon_rule_for_side (side)) + + local node = utils.get_far_node (pos) + if node then + node.param1 = set_side_bit (node.param1, side, false) + node.name = get_node_name (node) + minetest.swap_node (pos, node) + end +end + + + +local function digilines_support () + return + { + wire = + { + rules = + { + { x = 0, y = 0, z = -1 }, + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 }, + { x = 0, y = 0, z = 1 }, + { x = 1, y = 1, z = 0 }, + { x = 1, y = -1, z = 0 }, + { x = -1, y = 1, z = 0 }, + { x = -1, y = -1, z = 0 }, + { x = 0, y = 1, z = 1 }, + { x = 0, y = -1, z = 1 }, + { x = 0, y = 1, z = -1 }, + { x = 0, y = -1, z = -1 }, + { x = 0, y = 1, z = 0 }, + { x = 0, y = -1, z = 0 } + } + }, + + effector = + { + action = function (pos, node, channel, msg) + local meta = minetest.get_meta(pos) + + if meta then + local mychannel = meta:get_string ("channel") + + if mychannel ~= "" and mychannel == channel then + if type (msg) == "string" then + local words = { } + + for word in string.gmatch (msg, "%S+") do + words[#words + 1] = word + end + + if words[1] == "on" then + switch_on (pos, words[2]) + elseif words[1] == "off" then + switch_off (pos, words[2]) + end + end + end + end + end, + } + } +end + + + +local function mesecon_support () + return + { + receptor = + { + state = mesecon.state.off, + rules = + { + { x = 0, y = 0, z = -1 }, + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 }, + { x = 0, y = 0, z = 1 }, + { x = 1, y = 1, z = 0 }, + { x = 1, y = -1, z = 0 }, + { x = -1, y = 1, z = 0 }, + { x = -1, y = -1, z = 0 }, + { x = 0, y = 1, z = 1 }, + { x = 0, y = -1, z = 1 }, + { x = 0, y = 1, z = -1 }, + { x = 0, y = -1, z = -1 }, + { x = 0, y = 1, z = 0 }, + { x = 0, y = -1, z = 0 } + } + }, + } +end + + + +local function mesecon_support_on () + return + { + receptor = + { + state = mesecon.state.on, + rules = get_powered_rules + }, + } +end + + + +local function on_construct (pos) + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("channel", "") + + local formspec = + "formspec_version[3]\n".. + "size[6.0,4.0]\n".. + "field[1.0,0.8;4.0,1.0;channel;Channel;${channel}]\n".. + "button_exit[2.0,2.5;2.0,1.0;set;Set]\n" + + meta:set_string ("formspec", formspec) + end +end + + + +local function on_destruct (pos) + local node = utils.get_far_node (pos) + + if node then + local rules = get_powered_rules (node) + + if #rules > 0 then + utils.mesecon_receptor_off (pos, rules) + end + end +end + + + +local function on_receive_fields (pos, formname, fields, sender) + if fields.channel then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("channel", fields.channel or "") + end + end +end + + + +minetest.register_node ("lwcomponents:digiswitch", { + description = S("Digilines Switch"), + tiles = { "lwdigiswitch_white.png", "lwdigiswitch_black.png", + "lwdigiswitch_green.png", "lwdigiswitch_red.png", + "lwdigiswitch_yellow.png", "lwdigiswitch_blue.png" }, + sunlight_propagates = false, + drawtype = "normal", + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, + } + }, + paramtype = "none", + param1 = 0, + groups = { cracky = 2, oddly_breakable_by_hand = 2, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + mesecons = mesecon_support (), + digiline = digilines_support (), + _digistuff_channelcopier_fieldname = "channel", + + on_construct = on_construct, + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, +}) + + + +minetest.register_node ("lwcomponents:digiswitch_on", { + description = S("Digilines Switch"), + tiles = { "lwdigiswitch_white.png", "lwdigiswitch_black.png", + "lwdigiswitch_green.png", "lwdigiswitch_red.png", + "lwdigiswitch_yellow.png", "lwdigiswitch_blue.png" }, + sunlight_propagates = false, + drawtype = "normal", + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, + } + }, + paramtype = "none", + param1 = 0, + groups = { cracky = 2, oddly_breakable_by_hand = 2, not_in_creative_inventory = 1, wires_connect = 1 }, + --sounds = default.node_sound_stone_defaults (), + mesecons = mesecon_support_on (), + digiline = digilines_support (), + _digistuff_channelcopier_fieldname = "channel", + + on_construct = on_construct, + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, +}) + + + +end -- utils.digilines_supported and utils.mesecon_supported diff --git a/lwcomponents/dispenser.lua b/lwcomponents/dispenser.lua new file mode 100644 index 0000000..d2d006e --- /dev/null +++ b/lwcomponents/dispenser.lua @@ -0,0 +1,600 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +local dispenser_force = 25 + + + +local function dispense_dir (node) + if node.param2 == 0 then + return { x = 0, y = 0, z = -1 } + elseif node.param2 == 1 then + return { x = -1, y = 0, z = 0 } + elseif node.param2 == 2 then + return { x = 0, y = 0, z = 1 } + elseif node.param2 == 3 then + return { x = 1, y = 0, z = 0 } + else + return { x = 0, y = 0, z = 0 } + end +end + + + +local function dispense_pos (pos, node) + if node.param2 == 0 then + return { x = pos.x, y = pos.y, z = pos.z - 1 } + elseif node.param2 == 1 then + return { x = pos.x - 1, y = pos.y, z = pos.z } + elseif node.param2 == 2 then + return { x = pos.x, y = pos.y, z = pos.z + 1 } + elseif node.param2 == 3 then + return { x = pos.x + 1, y = pos.y, z = pos.z } + else + return { x = pos.x, y = pos.y, z = pos.z } + end +end + + + +local function dispense_velocity (node) + local tilt = (math.random (1 , 2001) - 1001) / 1000 + local sway = (math.random (1 , 4001) - 2001) / 1000 + + if node.param2 == 0 then + return { x = sway, y = tilt, z = -dispenser_force } + elseif node.param2 == 1 then + return { x = -dispenser_force, y = tilt, z = sway } + elseif node.param2 == 2 then + return { x = sway, y = tilt, z = dispenser_force } + elseif node.param2 == 3 then + return { x = dispenser_force, y = tilt, z = sway } + else + return { x = 0, y = 0, z = 0 } + end +end + + + +local function send_dispense_message (pos, slot, name) + 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 = "dispense", + name = name, + slot = slot }) + end + end + end +end + + + +-- slot: +-- nil - next item, no dispense if empty +-- number - 1 item from slot, no dispense if empty +-- string - name of item to dispense, no dispense if none +local function dispense_item (pos, node, 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 + slot = i + break + 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 not stack:is_empty () and stack:get_count () > 0 then + local name = stack:get_name () + local item = ItemStack (stack) + + if item then + item:set_count (1) + local spawn_pos = dispense_pos (pos, node) + local owner = meta:get_string ("owner") + local obj = nil + local cancel + + if utils.settings.spawn_mobs then + obj, cancel = utils.spawn_registered (name, + spawn_pos, + item, + owner, + pos, + dispense_dir (node), + dispenser_force) + + if obj == nil and cancel then + return false + end + end + + if not obj then + obj = minetest.add_item (spawn_pos, item) + + if obj then + obj:set_velocity (dispense_velocity (node)) + end + end + + if obj then + stack:set_count (stack:get_count () - 1) + inv:set_stack ("main", slot, stack) + + send_dispense_message (pos, slot, name) + + return true, slot, name + end + end + end + end + end + end + + return false +end + + + +local function after_place_base (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local spec = + "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".. + "list[context;main;3.5,2.5;4,4;]\n".. + "list[current_player;main;1.0,8.0;8,4;]\n".. + "listring[]" + + meta:set_string ("inventory", "{ main = { } }") + meta:set_string ("formspec", spec) + + local inv = meta:get_inventory () + + inv:set_size ("main", 16) + inv:set_width ("main", 4) +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", "Dispenser (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 + 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 + 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 + end + + 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) + 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 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] == "dispense" then + if m[2] and tonumber (m[2]) then + m[2] = tonumber (m[2]) + end + + dispense_item (pos, node, m[2]) + 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) + dispense_item (pos, node) + 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, 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 dispenser_groups = { cracky = 3, wires_connect = 1 } +if utils.pipeworks_supported then + dispenser_groups.tubedevice = 1 + dispenser_groups.tubedevice_receiver = 1 +end + + + +minetest.register_node("lwcomponents:dispenser", { + description = S("Dispenser"), + tiles = { "lwdispenser.png", "lwdispenser.png", "lwdispenser.png", + "lwdispenser.png", "lwdispenser.png", "lwdispenser_face.png"}, + is_ground_content = false, + groups = table.copy (dispenser_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + 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 +}) + + + +minetest.register_node("lwcomponents:dispenser_locked", { + description = S("Dispenser (locked)"), + tiles = { "lwdispenser.png", "lwdispenser.png", "lwdispenser.png", + "lwdispenser.png", "lwdispenser.png", "lwdispenser_face.png"}, + is_ground_content = false, + groups = table.copy (dispenser_groups), + --sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + 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 +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:dispenser", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:dispenser", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:dispenser", "main"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:dispenser_locked", "main"}, -- take items from above into hopper below + {"bottom", "lwcomponents:dispenser_locked", "main"}, -- insert items below from hopper above + {"side", "lwcomponents:dispenser_locked", "main"}, -- insert items from hopper at side +}) + + + +end -- utils.digilines_supported or utils.mesecon_supported + + + +--