From 3692738a749682c5cdb41972f0c960f633f35494 Mon Sep 17 00:00:00 2001 From: loosewheel <76670709+loosewheel@users.noreply.github.com> Date: Sat, 6 Nov 2021 21:46:50 +1000 Subject: [PATCH] Add files via upload --- change.log | 3 + collector.lua | 536 ++++++++++++++++++++++++++++++ crafting.lua | 141 ++++++++ depends.txt | 6 + description.txt | 1 + detector.lua | 782 ++++++++++++++++++++++++++++++++++++++++++++ digiswitch.lua | 188 +++++++++++ dispenser.lua | 429 ++++++++++++++++++++++++ dropper.lua | 405 +++++++++++++++++++++++ extras.lua | 88 +++++ init.lua | 31 ++ license.txt | 38 +++ mod.conf | 6 + movefloor.lua | 199 +++++++++++ readme.txt | 330 +++++++++++++++++++ solid_conductor.lua | 142 ++++++++ utils.lua | 131 ++++++++ 17 files changed, 3456 insertions(+) create mode 100644 change.log create mode 100644 collector.lua create mode 100644 crafting.lua create mode 100644 depends.txt create mode 100644 description.txt create mode 100644 detector.lua create mode 100644 digiswitch.lua create mode 100644 dispenser.lua create mode 100644 dropper.lua create mode 100644 extras.lua create mode 100644 init.lua create mode 100644 license.txt create mode 100644 mod.conf create mode 100644 movefloor.lua create mode 100644 readme.txt create mode 100644 solid_conductor.lua create mode 100644 utils.lua diff --git a/change.log b/change.log new file mode 100644 index 0000000..2b953e8 --- /dev/null +++ b/change.log @@ -0,0 +1,3 @@ + +v 0.1.0 +* initial release diff --git a/collector.lua b/collector.lua new file mode 100644 index 0000000..e44e21b --- /dev/null +++ b/collector.lua @@ -0,0 +1,536 @@ +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, + digiline.rules.default, + 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_node (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) + + -- 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", "Collector (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 + + 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 + preserve_metadata (pos, node, meta, { stack }) + utils.item_drop (stack, nil, pos) + on_destruct (pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function 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 = digiline.rules.default, + }, + + 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 == channel 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 + + + +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 = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + can_dig = can_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 = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector_locked", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node_locked, + can_dig = can_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 = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + can_dig = can_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 = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + floodable = false, + drop = "lwcomponents:collector_locked", + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node_locked, + can_dig = can_dig, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick +}) + + + +end -- utils.digilines_supported + + + +-- diff --git a/crafting.lua b/crafting.lua new file mode 100644 index 0000000..c516f78 --- /dev/null +++ b/crafting.lua @@ -0,0 +1,141 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + +minetest.register_craft( { + output = "lwcomponents:dropper", + recipe = { + { "default:stone", "default:chest" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:dropper_locked", + recipe = { + { "default:stone", "default:chest_locked" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:dispenser", + recipe = { + { "default:chest", "default:stone" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:dispenser_locked", + recipe = { + { "default:chest_locked", "default:stone" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:detector", + recipe = { + { "default:copper_ingot", "default:steel_ingot" }, + { "default:stone", "default:chest" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:detector_locked", + recipe = { + { "default:copper_ingot", "default:steel_ingot" }, + { "default:stone", "default:chest_locked" }, + }, +}) + +end -- utils.digilines_supported or utils.mesecon_supported + + + +if utils.digilines_supported then + +minetest.register_craft( { + output = "lwcomponents:collector", + recipe = { + { "default:copper_ingot", "default:steel_ingot" }, + { "default:chest", "default:stone" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:collector_locked", + recipe = { + { "default:copper_ingot", "default:steel_ingot" }, + { "default:chest_locked", "default:stone" }, + }, +}) + +end -- utils.digilines_supported + + + +if utils.mesecon_supported and mesecon.mvps_push then + +minetest.register_craft ({ + output = "lwcomponents:movefloor", + recipe = { + { "default:stick", "default:stick", "default:stick" }, + { "default:stick", "default:steel_ingot", "default:stick" }, + { "default:stick", "default:stick", "default:stick" }, + } +}) + +end -- utils.mesecon_supported and mesecon.mvps_push + + + +if utils.digilines_supported and utils.mesecon_supported then + +minetest.register_craft ({ + output = "lwcomponents:digiswitch 2", + recipe = { + { "default:stone", "default:stone" }, + { "default:copper_ingot", "default:mese_crystal_fragment" }, + { "default:stick", "default:stick" }, + } +}) + +end -- utils.digilines_supported and utils.mesecon_supported + + + +if utils.unifieddyes_supported and utils.mesecon_supported and utils.digilines_supported then + +minetest.register_craft ({ + output = "lwcomputers:solid_conductor_off 3", + recipe = { + { "default:mese_crystal_fragment", "group:wood", ""}, + { "group:wood", "group:wood", "dye:white" }, + }, +}) + + +minetest.register_craft ({ + output = "lwcomputers:solid_horizontal_conductor_off 3", + recipe = { + { "group:wood", "group:wood", ""}, + { "default:mese_crystal_fragment", "group:wood", "dye:white" }, + }, +}) + +end -- utils.unifieddyes_supported and utils.mesecon_supported and utils.digilines_supported then + + + +-- diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..023d040 --- /dev/null +++ b/depends.txt @@ -0,0 +1,6 @@ +default +lwdrops? +mesecons? +digilines? +unifieddyes? +intllib? diff --git a/description.txt b/description.txt new file mode 100644 index 0000000..075b9df --- /dev/null +++ b/description.txt @@ -0,0 +1 @@ +Various components for mesecons and digilines. diff --git a/detector.lua b/detector.lua new file mode 100644 index 0000000..7253c73 --- /dev/null +++ b/detector.lua @@ -0,0 +1,782 @@ +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) + 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) + 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, item_type, name, label, item_pos, 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, + digiline.rules.default, + channel, + { action = "detect", + type = item_type, + name = name, + label = label, + pos = to_relative_coords (pos, item_pos), + count = count }) + 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 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) + + for i = 1, #object do + if object[i]:is_player () then + + -- player + if meta:get_string ("players") == "true" and + filter_item (pos, mode, object[i]:get_pos ()) then + + send_detect_message (pos, + "player", + object[i]:get_player_name (), + object[i]:get_player_name (), + object[i]:get_pos (), + 1) + + 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 + send_detect_message (pos, + "drop", + stack:get_name (), + stack:get_name (), + object[i]:get_pos (), + stack:get_count ()) + + 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 "" + + send_detect_message (pos, + "entity", + name, + label, + object[i]:get_pos (), + 1) + + 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 + + send_detect_message (pos, + "node", + node.name, + node.name, + testpos, + 1) + + detected = true + end + end + end + end + end + + if detected then + mesecons_on (pos) + 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) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:detector" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:detector_on" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (detect_interval) + update_form_spec (pos) + end + + elseif node.name == "lwcomponents:detector_locked" then + local meta = minetest.get_meta (pos) + + if meta 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 +end + + + +local function stop_detector (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:detector_on" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:detector" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):stop () + mesecons_off (pos) + update_form_spec (pos) + end + + elseif node.name == "lwcomponents:detector_locked_on" then + local meta = minetest.get_meta (pos) + + if meta 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 +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 ("inventory", "{ main = { }, filter = { } }") + 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 + preserve_metadata (pos, node, meta, { stack }) + utils.item_drop (stack, nil, pos) + on_destruct (pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function 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 = digiline.rules.default, + }, + + 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 == channel 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 () + 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 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + 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 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + 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 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + 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 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:detector_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + 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/digiswitch.lua b/digiswitch.lua new file mode 100644 index 0000000..8143ba5 --- /dev/null +++ b/digiswitch.lua @@ -0,0 +1,188 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported and utils.mesecon_supported then + + + +local function get_mesecon_rule_for_side (side) + local base = nil + + if side == "white" then + return { { x = 0, y = 1, z = 0 } } + elseif side == "black" then + return { { x = 0, y = -1, z = 0 } } -- down doesn't work + 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 }, -- down doesn't work + } + 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 == 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 + utils.mesecon_receptor_on (pos, get_mesecon_rule_for_side (words[2])) + elseif words[1] == "off" then + utils.mesecon_receptor_off (pos, get_mesecon_rule_for_side (words[2])) + end + end + end + end + end, + } + } +end + + + +local function mesecon_support () + return + { + receptor = + { + state = mesecon.state.off, + rules = + { + { 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 }, -- down doesn't work + } + }, + } +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;]\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) + utils.mesecon_receptor_off (pos, get_mesecon_rule_for_side ()) +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 "") + + local formspec = + "formspec_version[3]\n".. + "size[6.0,4.0]\n".. + "field[1.0,0.8;4.0,1.0;channel;Channel;"..minetest.formspec_escape (meta:get_string ("channel")).."]\n".. + "button_exit[2.0,2.5;2.0,1.0;set;Set]\n" + + meta:set_string ("formspec", formspec) + 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}, + } + }, + groups = { cracky = 2, oddly_breakable_by_hand = 2 }, + 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, +}) + + + +end -- utils.digilines_supported and utils.mesecon_supported diff --git a/dispenser.lua b/dispenser.lua new file mode 100644 index 0000000..bd15ac2 --- /dev/null +++ b/dispenser.lua @@ -0,0 +1,429 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +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 force = 25 --math.random (30 , 35) + 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 = -force } + elseif node.param2 == 1 then + return { x = -force, y = tilt, z = sway } + elseif node.param2 == 2 then + return { x = sway, y = tilt, z = force } + elseif node.param2 == 3 then + return { x = 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, + digiline.rules.default, + 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 obj = minetest.add_item (dispense_pos (pos, node), item) + + if obj then + obj:set_velocity (dispense_velocity (node)) + + 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_node (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) + + -- 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", "Dispenser (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 +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 + preserve_metadata (pos, node, meta, { stack }) + 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 = digiline.rules.default, + }, + + 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 == channel 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 + + + +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 = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + can_dig = can_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 = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + after_place_node = after_place_node_locked, + can_dig = can_dig, + on_blast = on_blast, + on_rightclick = on_rightclick +}) + + + +end -- utils.digilines_supported or utils.mesecon_supported + + + +-- diff --git a/dropper.lua b/dropper.lua new file mode 100644 index 0000000..21eb4e6 --- /dev/null +++ b/dropper.lua @@ -0,0 +1,405 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +local function drop_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 send_drop_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, + digiline.rules.default, + channel, + { action = "drop", + name = name, + slot = slot }) + end + end + end +end + + + +-- slot: +-- nil - next item, no drop if empty +-- number - 1 item from slot, no drop if empty +-- string - name of item to drop, no drop if none +local function drop_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) + + stack:set_count (stack:get_count () - 1) + inv:set_stack ("main", slot, stack) + + utils.item_drop (item, nil, drop_pos (pos, node)) + + send_drop_message (pos, slot, name) + + return true, slot, name + end + end + end + end + end + + return false +end + + + +local function after_place_node (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) + + -- 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", "Dropper (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 +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 + preserve_metadata (pos, node, meta, { stack }) + 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 = digiline.rules.default, + }, + + 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 == channel then + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if m[1] == "drop" then + if m[2] and tonumber (m[2]) then + m[2] = tonumber (m[2]) + end + + drop_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) + drop_item (pos, node) + end + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:dropper", { + description = S("Dropper"), + tiles = { "lwdropper.png", "lwdropper.png", "lwdropper.png", + "lwdropper.png", "lwdropper.png", "lwdropper_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + can_dig = can_dig, + on_blast = on_blast, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:dropper_locked", { + description = S("Dropper (locked)"), + tiles = { "lwdropper.png", "lwdropper.png", "lwdropper.png", + "lwdropper.png", "lwdropper.png", "lwdropper_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + after_place_node = after_place_node_locked, + can_dig = can_dig, + on_blast = on_blast, + on_rightclick = on_rightclick +}) + + + +end -- utils.digilines_supported or utils.mesecon_supported + + + +-- diff --git a/extras.lua b/extras.lua new file mode 100644 index 0000000..08dacc6 --- /dev/null +++ b/extras.lua @@ -0,0 +1,88 @@ +local utils = ... +local S = utils.S + + + +local touchscreen = minetest.registered_nodes["digistuff:touchscreen"] +if touchscreen then + local touchblock = table.copy (touchscreen) + + touchblock.description = S("LWComponents Touchscreen") + + touchblock.node_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } + } + } + + touchblock.selection_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } + } + } + + touchblock.collision_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } + } + } + + minetest.register_node ("lwcomponents:touchscreen", touchblock) + + minetest.register_craft({ + output = "lwcomponents:touchscreen", + recipe = { + {"mesecons_luacontroller:luacontroller0000","default:glass","default:glass"}, + {"default:glass","digilines:lcd","default:glass"}, + {"default:glass","default:glass","default:stone"} + } + }) +end + + + +local panel = minetest.registered_nodes["digistuff:panel"] +if panel then + local panelblock = table.copy (panel) + + panelblock.description = S("LWComponents Control Panel") + + panelblock.node_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } + } + } + + panelblock.selection_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } + } + } + + panelblock.collision_box = { + type = "fixed", + fixed = { + { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } + } + } + + minetest.register_node ("lwcomponents:panel", panelblock) + + minetest.register_craft({ + output = "lwcomponents:panel", + recipe = { + {"","digistuff:button",""}, + {"digistuff:button","digilines:lcd","digistuff:button"}, + {"","digistuff:button","default:stone"} + } + }) +end + + + +-- diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..5ad7d8c --- /dev/null +++ b/init.lua @@ -0,0 +1,31 @@ +local version = "0.1.0" +local mod_storage = minetest.get_mod_storage () + + + +lwcomponents = { } + + + +function lwcomponents.version () + return version +end + + +local utils = { } +local modpath = minetest.get_modpath ("lwcomponents") + +loadfile (modpath.."/utils.lua") (utils, mod_storage) +loadfile (modpath.."/dropper.lua") (utils) +loadfile (modpath.."/collector.lua") (utils) +loadfile (modpath.."/dispenser.lua") (utils) +loadfile (modpath.."/detector.lua") (utils) +loadfile (modpath.."/extras.lua") (utils) +loadfile (modpath.."/digiswitch.lua") (utils) +loadfile (modpath.."/movefloor.lua") (utils) +loadfile (modpath.."/solid_conductor.lua") (utils) +loadfile (modpath.."/crafting.lua") (utils) + + + +-- diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..6817b9b --- /dev/null +++ b/license.txt @@ -0,0 +1,38 @@ +Source code license +------------------- + +GNU Lesser General Public License, version 2.1 +Copyright (C) 2021 loosewheel + +This program is free software; you can redistribute it and/or modify it under the terms +of the GNU Lesser General Public License as published by the Free Software Foundation; +either version 2.1 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details: +https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + + +Media license +------------- +Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) + +You are free to: +Share — copy and redistribute the material in any medium or format. +Adapt — remix, transform, and build upon the material for any purpose, even commercially. +The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and +indicate if changes were made. You may do so in any reasonable manner, but not in any way +that suggests the licensor endorses you or your use. + +ShareAlike — If you remix, transform, or build upon the material, you must distribute +your contributions under the same license as the original. + +No additional restrictions — You may not apply legal terms or technological measures that +legally restrict others from doing anything the license permits. + +See http://creativecommons.org/licenses/by-sa/3.0/ diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..7cdea89 --- /dev/null +++ b/mod.conf @@ -0,0 +1,6 @@ +author = loosewheel +description = Various components for mesecons and digilines. +title = LWComponents +name = lwcomponents +depends = default +optional_depends = lwdrops, mesecons, digilines, unifieddyes, intllib diff --git a/movefloor.lua b/movefloor.lua new file mode 100644 index 0000000..2b426db --- /dev/null +++ b/movefloor.lua @@ -0,0 +1,199 @@ +local utils = ... +local S = utils.S + + + +if utils.mesecon_supported and mesecon.mvps_push then + + + +local mesecon_rules = +{ + { 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 = 1, y = -1, z = 0 }, + { x = -1, y = -1, z = 0 }, + { x = 0, y = -1, z = 1 }, + { x = 0, y = -1, z = -1 }, +} + + + +-- use mesecons movestone settings +local timer_interval = 1 / mesecon.setting ("movestone_speed", 3) +local max_push = 3 +local max_pull = 3 + + + +-- helper functions: +local function get_movefloor_direction (rulename) + if rulename.y > 0 then + return { x = 0, y = 1, z = 0 } + elseif rulename.y < 0 then + return { x = 0, y = -1, z = 0 } + end +end + + +local function add_movefloor_list (pos, list) + for i = 1, #list do + if list[i].x == pos.x and + list[i].y == pos.y and + list[i].z == pos.z then + + return false + end + end + + list[#list + 1] = { x = pos.x, y = pos.y, z = pos.z } + + return true +end + + + +local function find_adjoining_movefloor (pos, list) + local tpos = + { + { x = pos.x + 1, y = pos.y, z = pos.z }, + { x = pos.x - 1, y = pos.y, z = pos.z }, + { x = pos.x, y = pos.y, z = pos.z + 1 }, + { x = pos.x, y = pos.y, z = pos.z - 1 } + } + + for i = 1, #tpos do + local node = minetest.get_node (tpos[i]) + if node and node.name == "lwcomponents:movefloor" then + if add_movefloor_list (tpos[i], list) then + find_adjoining_movefloor (tpos[i], list) + end + end + end +end + + + +-- copied from mesecons movestone +local function movefloor_move (pos, node, rulename, is_sticky) + local direction = get_movefloor_direction (rulename) + local play_sound = false + + local list = + { + { x = pos.x, y = pos.y, z = pos.z } + } + + find_adjoining_movefloor (pos, list) + + for i = 1, #list do + local frontpos = vector.add (list[i], direction) + local meta = minetest.get_meta (list[i]) + local owner = meta:get_string ("owner") + local continue = true + + -- ### Step 1: Push nodes in front ### + local success, stack, oldstack = mesecon.mvps_push (frontpos, direction, max_push, owner) + if not success then + if stack == "protected" then + meta:set_string ("infotext", "Can't move: protected area on the way") + else + minetest.get_node_timer (list[i]):start (timer_interval) + continue = false + end + end + + if continue then + mesecon.mvps_move_objects (frontpos, direction, oldstack) + + -- ### Step 2: Move the movestone ### + minetest.set_node (frontpos, node) + local meta2 = minetest.get_meta (frontpos) + meta2:set_string ("owner", owner) + minetest.remove_node (list[i]) + mesecon.on_dignode (list[i], node) + mesecon.on_placenode (frontpos, node) + minetest.get_node_timer (frontpos):start (timer_interval) + play_sound = true + + -- ### Step 3: If sticky, pull stack behind ### + if is_sticky and direction.y < 0 then + local backpos = vector.subtract (list[i], direction) + success, stack, oldstack = mesecon.mvps_pull_all (backpos, direction, max_pull, owner) + if success then + mesecon.mvps_move_objects (backpos, vector.multiply (direction, -1), oldstack, -1) + end + end + + -- ### Step 4: Let things fall ### + minetest.check_for_falling (vector.add (list[i], { x = 0, y = 1, z = 0 })) + end + end + + if play_sound then + minetest.sound_play("movestone", { pos = list[i], max_hear_distance = 20, gain = 0.5 }, true) + end + +end + + + +local function on_timer (pos, elapsed) + local sourcepos = mesecon.is_powered (pos) + + if not sourcepos then + return + end + + local rulename = vector.subtract (sourcepos[1], pos) + + mesecon.activate (pos, minetest.get_node (pos), rulename, 0) +end + + + +local function mesecon_support () + return + { + effector = + { + rules = table.copy (mesecon_rules), + + action_on = function (pos, node, rulename) + -- do something to turn the effector on + + if rulename and not minetest.get_node_timer (pos):is_started () then + movefloor_move (pos, node, rulename, true) + end + end + } + } +end + + + +minetest.register_node("lwcomponents:movefloor", { + description = S("Moving Floor"), + tiles = { "lwmovefloortop.png", "lwmovefloortop.png", + "lwmovefloorside.png", "lwmovefloorside.png", + "lwmovefloorside.png", "lwmovefloorside.png" }, + sunlight_propagates = false, + drawtype = "normal", + node_box = { + type = "fixed", + fixed = { + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, + } + }, + groups = { cracky = 2 }, + sounds = default.node_sound_wood_defaults (), + mesecons = mesecon_support (), + + on_timer = on_timer, +}) + + + +end diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..d4f5d74 --- /dev/null +++ b/readme.txt @@ -0,0 +1,330 @@ +LWComponents + by loosewheel + + +Licence +======= +Code licence: +LGPL 2.1 + +Media licence +CC BY-SA 3.0 + + +Version +======= +0.1.0 + + +Minetest Version +================ +This mod was developed on version 5.4.0 + + +Dependencies +============ +default + + +Optional Dependencies +===================== +lwdrops +mesecons +digilines +unifieddyes +intllib + + +Installation +============ +Copy the 'lwcomponents' folder to your mods folder. + + +Bug Report +========== + + + +Description +=========== +Various components for mesecons and digilines. + + + +Dropper +------- +* This block is only available if digilines and/or mesecons are loaded. + +Contains an inventory and drops an item on command. Also acts as a +digilines conductor. + +UI + +Channel - digilines channel of dropper. +Top 16 slot inventory - storage of items to drop. +Bottom 32 slot inventory - player's inventory. + +Mesecons + Drops the next item when power is turned on. + +Digilines messages + +"drop" + Drops the next item. No drop if dropper is empty. + +"drop " + Drops 1 item from the given slot (1 to 16). No drop if slot is empty. + eg. "drop 7" + +"drop " + Drops 1 item of the given name. No drop if dropper does not contain the + item. + eg. "drop default:stone" + +When an item is dropped a digilines message is sent with the dropper's +channel. The message is a table with the following keys: +{ + action = "drop", + name = "", -- name of dropped item + slot = -- slot number the item was taken from (1 to 16). +} + + + +Dispenser +--------- +* This block is only available if digilines and/or mesecons are loaded. + +Contains an inventory and dispenses (with velocity) an item on command. +Also acts as a digilines conductor. + +UI + +Channel - digilines channel of dispenser. +Top 16 slot inventory - storage of items to dispense. +Bottom 32 slot inventory - player's inventory. + +Mesecons + Dispenses the next item when power is turned on. + +Digilines messages + +"dispense" + Dispenses the next item. No dispense if dispenser is empty. + +"dispense " + Dispenses 1 item from the given slot (1 to 16). No dispense if slot is + empty. + eg. "dispense 7" + +"dispense " + Dispenses 1 item of the given name. No dispense if dispenser does not + contain the item. + eg. "dispense default:stone" + +When an item is dropped a digilines message is sent with the dropper's +channel. The message is a table with the following keys: +{ + action = "dispense", + name = "", -- name of dropped item + slot = -- slot number the item was taken from (1 to 16). +} + + + +Collector +--------- +* This block is only available if digilines is loaded. + +Picks up dropped items in adjacent block, with optional filtering. Also +acts as a digilines conductor. + +UI + +Channel - digilines channel of collector. +Left 16 slot inventory - storage of picked up items. +Right 8 slot inventory - Filter list. Place what items should be picked + up in this list. Leave empty to pick up all. +Bottom 32 slot inventory - player's inventory. + +Digilines messages + +"start" + Start the collector. + +"stop" + Stop the collector. + +When items are picked up a digilines message is sent with the collector's +channel. The message is a table with the following keys: +{ + action = "collect", + name = "", -- name of picked up items. + count = -- number of the item picked up. +} + + + +Detector +-------- +* This block is only available if digilines and/or mesecons are loaded. + +Detects items or entities within a given radius. Also acts as a +digilines conductor. + +UI + +Channel - digilines channel of detector. +Radius - block distance from detector to detect. +Entities - if checked detects entities. +Players - if checked detects players. +Drops - if checked detects drops. +Nodes - if checked detects nodes. + +mode: + All - detects to radius in all directions, including diagonal. + Forward - detects to radius directly in front of the detector (one block high). + Up - detects to radius directly above the detector (one block wide). + Down - detects to radius directly below the detector (one block wide). + +Mesecons + Mesecons power is turned on when something is detected, and turned off + when nothing is detected. + +Digilines messages + +"start" + Start the detector. + +"stop" + Stop the detector. + +"radius " + Set radius of the detector. should be a number from 1 to 5, and is + trimmed to this range. + +"entities " + Set detection of entities on or off. + +"players " + Set detection of players on or off. + +"drops " + Set detection of drops on or off. + +"nodes " + Set detection of nodes on or off. + +"mode all" +"mode forward" +"mode up" +"mode down" + Set the detector's mode. + +When items or entities are detected a digilines message is sent with the +detector's channel. A message is sent for each found item/entity. The +message is a table with the following keys: +{ + action = "detect", + type = "", -- will be "entity", "player", "drop" or "node" + name = "", + label = "