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 = "