diff --git a/change.log b/change.log index 2b953e8..9961143 100644 --- a/change.log +++ b/change.log @@ -1,3 +1,11 @@ v 0.1.0 * initial release + + +v0.1.1 +* Fixed receiving digilines message on "" channel. +* Added siren. +* Made digilines optional for solid conductor blocks. +* Fixed lighting +* Added puncher diff --git a/collector.lua b/collector.lua index e44e21b..70bc962 100644 --- a/collector.lua +++ b/collector.lua @@ -400,7 +400,7 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel == channel then + if this_channel ~= "" and this_channel == channel then local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w @@ -432,7 +432,7 @@ minetest.register_node("lwcomponents:collector", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, floodable = false, drop = "lwcomponents:collector", @@ -458,7 +458,7 @@ minetest.register_node("lwcomponents:collector_locked", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, floodable = false, drop = "lwcomponents:collector_locked", @@ -484,7 +484,7 @@ minetest.register_node("lwcomponents:collector_on", { is_ground_content = false, groups = { cracky = 3, not_in_creative_inventory = 1 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, floodable = false, drop = "lwcomponents:collector", @@ -510,7 +510,7 @@ minetest.register_node("lwcomponents:collector_locked_on", { is_ground_content = false, groups = { cracky = 3, not_in_creative_inventory = 1 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, floodable = false, drop = "lwcomponents:collector_locked", diff --git a/crafting.lua b/crafting.lua index c516f78..8dd221d 100644 --- a/crafting.lua +++ b/crafting.lua @@ -58,6 +58,42 @@ minetest.register_craft( { }, }) + +minetest.register_craft( { + output = "lwcomponents:siren", + recipe = { + { "group:wood", "default:chest" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:siren_locked", + recipe = { + { "group:wood", "default:chest_locked" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:puncher", + recipe = { + { "default:chest", "default:sword_stone" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:puncher_locked", + recipe = { + { "default:chest_locked", "default:sword_stone" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + end -- utils.digilines_supported or utils.mesecon_supported @@ -115,7 +151,7 @@ end -- utils.digilines_supported and utils.mesecon_supported -if utils.unifieddyes_supported and utils.mesecon_supported and utils.digilines_supported then +if utils.unifieddyes_supported and utils.mesecon_supported then minetest.register_craft ({ output = "lwcomputers:solid_conductor_off 3", @@ -134,7 +170,7 @@ minetest.register_craft ({ }, }) -end -- utils.unifieddyes_supported and utils.mesecon_supported and utils.digilines_supported then +end -- utils.unifieddyes_supported and utils.mesecon_supported then diff --git a/detector.lua b/detector.lua index 7253c73..16b8910 100644 --- a/detector.lua +++ b/detector.lua @@ -576,7 +576,7 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel == channel then + if this_channel ~= "" and this_channel == channel then local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w @@ -666,7 +666,7 @@ minetest.register_node("lwcomponents:detector", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, @@ -695,7 +695,7 @@ minetest.register_node("lwcomponents:detector_locked", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, @@ -724,7 +724,7 @@ minetest.register_node("lwcomponents:detector_on", { is_ground_content = false, groups = { cracky = 3, not_in_creative_inventory = 1 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, @@ -753,7 +753,7 @@ minetest.register_node("lwcomponents:detector_locked_on", { is_ground_content = false, groups = { cracky = 3, not_in_creative_inventory = 1 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, diff --git a/digiswitch.lua b/digiswitch.lua index 8143ba5..193e260 100644 --- a/digiswitch.lua +++ b/digiswitch.lua @@ -71,7 +71,7 @@ local function digilines_support () if meta then local mychannel = meta:get_string ("channel") - if mychannel == channel then + if mychannel ~= "" and mychannel == channel then if type (msg) == "string" then local words = { } diff --git a/dispenser.lua b/dispenser.lua index bd15ac2..e794e7d 100644 --- a/dispenser.lua +++ b/dispenser.lua @@ -325,7 +325,7 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel == channel then + if this_channel ~= "" and this_channel == channel then local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w @@ -377,7 +377,7 @@ minetest.register_node("lwcomponents:dispenser", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, @@ -403,7 +403,7 @@ minetest.register_node("lwcomponents:dispenser_locked", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, diff --git a/dropper.lua b/dropper.lua index 21eb4e6..b190e2a 100644 --- a/dropper.lua +++ b/dropper.lua @@ -301,7 +301,7 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel == channel then + if this_channel ~= "" and this_channel == channel then local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w @@ -353,7 +353,7 @@ minetest.register_node("lwcomponents:dropper", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, @@ -379,7 +379,7 @@ minetest.register_node("lwcomponents:dropper_locked", { is_ground_content = false, groups = { cracky = 3 }, sounds = default.node_sound_stone_defaults (), - paramtype = "light", + paramtype = "none", param1 = 0, paramtype2 = "facedir", param2 = 1, diff --git a/init.lua b/init.lua index 5ad7d8c..50221a1 100644 --- a/init.lua +++ b/init.lua @@ -1,4 +1,4 @@ -local version = "0.1.0" +local version = "0.1.1" local mod_storage = minetest.get_mod_storage () @@ -20,6 +20,8 @@ loadfile (modpath.."/dropper.lua") (utils) loadfile (modpath.."/collector.lua") (utils) loadfile (modpath.."/dispenser.lua") (utils) loadfile (modpath.."/detector.lua") (utils) +loadfile (modpath.."/siren.lua") (utils) +loadfile (modpath.."/puncher.lua") (utils) loadfile (modpath.."/extras.lua") (utils) loadfile (modpath.."/digiswitch.lua") (utils) loadfile (modpath.."/movefloor.lua") (utils) diff --git a/license.txt b/license.txt index 6817b9b..635ce81 100644 --- a/license.txt +++ b/license.txt @@ -14,8 +14,57 @@ See the GNU Lesser General Public License for more details: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + +lwsiren-buzz.ogg +---------------- +derived from https://www.freesoundslibrary.com/alarm-sound-effect/ + +License: Attribution 4.0 International (CC BY 4.0). You are allowed to use +sound effects free of charge and royalty free in your multimedia projects +for commercial or non-commercial purposes. +https://creativecommons.org/licenses/by/4.0/ + + + +lwsiren-horn.ogg +---------------- +derived from https://www.freesoundslibrary.com/ahooga-horn/ + +License: Attribution 4.0 International (CC BY 4.0). You are allowed to use +sound effects free of charge and royalty free in your multimedia projects +for commercial or non-commercial purposes. +https://creativecommons.org/licenses/by/4.0/ + + + +lwsiren-siren.ogg +------------------ +https://www.freesoundslibrary.com/cop-siren/ + +License: Attribution 4.0 International (CC BY 4.0). You are allowed to use +sound effects free of charge and royalty free in your multimedia projects +for commercial or non-commercial purposes. +https://creativecommons.org/licenses/by/4.0/ + + + +lwsiren-raid.ogg +----------------- +derived from https://www.freesoundslibrary.com/space-ship-siren-sound/ + +License: Attribution 4.0 International (CC BY 4.0). You are allowed to use +sound effects free of charge and royalty free in your multimedia projects +for commercial or non-commercial purposes. +https://creativecommons.org/licenses/by/4.0/ + + + Media license ------------- +siren images derived from images from https://openclipart.org, which is +public domain. + +All other media, or media not covered by a licence, is licensed Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) You are free to: diff --git a/puncher.lua b/puncher.lua new file mode 100644 index 0000000..0a6c446 --- /dev/null +++ b/puncher.lua @@ -0,0 +1,572 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +local function direction_vector (pos) + local meta = minetest.get_meta (pos) + + if meta then + local mode = meta:get_int ("mode") + + if mode == 2 then + return { x = 0, y = 1, z = 0 } + elseif mode == 3 then + return { x = 0, y = -1, z = 0 } + else + local node = minetest.get_node (pos) + + if node then + if node.param2 == 0 then + return { x = 0, y = 0, z = -1 } + elseif node.param2 == 1 then + return { x = -1, y = 0, z = 0 } + elseif node.param2 == 2 then + return { x = 0, y = 0, z = 1 } + elseif node.param2 == 3 then + return { x = 1, y = 0, z = 0 } + end + end + end + end + + return { x = 0, y = 0, z = 0 } +end + + + +local function punch (pos) + local meta = minetest.get_meta (pos) + local node = minetest.get_node (pos) + + if meta and node and + (node.name == "lwcomponents:puncher_on" or + node.name == "lwcomponents:puncher_locked_on") then + + local reach = tonumber (meta:get_string ("reach")) or 1 + local dir = direction_vector (pos) + local punched = false + + for r = 1, reach do + local tpos = vector.add (pos, vector.multiply (dir, r)) + local object = minetest.get_objects_inside_radius (tpos, 0.68) + + for i = 1, #object do + if object[i]:is_player () then + + -- player + if meta:get_string ("players") == "true" then + + object[i]:punch (object[i], + 1.0, + { full_punch_interval = 1.0, + damage_groups = { fleshy = 4 } }, + vector.direction (pos, object[i]:get_pos ())) + + punched = 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 + + -- don't punch drops + + elseif object[i].get_pos and object[i]:get_pos () then + + -- entity + if meta:get_string ("entities") == "true" then + + object[i]:punch (object[i], + 1.0, + { full_punch_interval = 1.0, + damage_groups = { fleshy = 4 } }, + vector.direction (pos, object[i]:get_pos ())) + + punched = true + end + + end + + end + + if punched then + break + end + end + end +end + + + +local function get_form_spec (is_off, mode, entities, players) + return + "formspec_version[3]\n".. + "size[11.75,7.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;reach;Reach;${reach}]\n".. + "button[5.5,2.5;2.0,0.8;setreach;Set]\n".. + "checkbox[1.0,4.4;entities;Entities;"..entities.."]\n".. + "checkbox[1.0,5.4;players;Players;"..players.."]\n".. + "textlist[4.875,4.0;5.875,2.0;mode;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:puncher" or + node.name == "lwcomponents:puncher_locked" + + meta:set_string ("formspec", get_form_spec (is_off, + meta:get_int ("mode"), + meta:get_string ("entities"), + meta:get_string ("players"))) + end +end + + + +local function start_puncher (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:puncher" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:puncher_on" + + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + elseif node.name == "lwcomponents:puncher_locked" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:puncher_locked_on" + + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + end + end +end + + + +local function stop_puncher (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:puncher_on" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:puncher" + + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + elseif node.name == "lwcomponents:puncher_locked_on" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:puncher_locked" + + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + end + end +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:puncher" or + itemstack:get_name () == "lwcomponents:puncher_locked") + + meta:set_string ("formspec", get_form_spec (is_off, 1, "false", "false")) + + meta:set_string ("reach", "1") + meta:set_int ("mode", 1) + meta:set_string ("entities", "false") + meta:set_string ("players", "false") + + -- 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", "Puncher (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.setreach then + local meta = minetest.get_meta (pos) + + if meta then + local reach = math.min (math.max (tonumber (fields.reach) or 1, 1), 5) + + meta:set_string ("reach", tostring (reach)) + end + end + + if fields.start then + start_puncher (pos) + end + + if fields.stop then + stop_puncher (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.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 + 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) + 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 ~= "" and 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_puncher (pos) + + elseif m[1] == "stop" then + stop_puncher (pos) + + elseif m[1] == "reach" then + local reach = math.min (math.max (tonumber (m[2]) or 1, 1), 5) + + meta:set_string ("reach", tostring (reach)) + + 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] == "mode" then + if m[2] == "forward" then + meta:set_int ("mode", 1) + update_form_spec (pos) + + elseif m[2] == "up" then + meta:set_int ("mode", 2) + update_form_spec (pos) + + elseif m[2] == "down" then + meta:set_int ("mode", 3) + update_form_spec (pos) + + end + + elseif m[1] == "punch" then + punch (pos) + + end + end + end + end, + } + } + end + + return nil +end + + + +local function mesecon_support () + if utils.mesecon_supported then + return + { + effector = + { + rules = utils.mesecon_default_rules, + + action_on = function (pos, node) + -- do something to turn the effector on + punch (pos) + end + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:puncher", { + description = S("Puncher"), + tiles = { "lwpuncher_face.png", "lwpuncher_face.png", "lwpuncher.png", + "lwpuncher.png", "lwpuncher.png", "lwpuncher_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:puncher", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:puncher_locked", { + description = S("Puncher (locked)"), + tiles = { "lwpuncher_face.png", "lwpuncher_face.png", "lwpuncher.png", + "lwpuncher.png", "lwpuncher.png", "lwpuncher_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:puncher_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:puncher_on", { + description = S("Puncher"), + tiles = { "lwpuncher_face_on.png", "lwpuncher_face_on.png", "lwpuncher.png", + "lwpuncher.png", "lwpuncher.png", "lwpuncher_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:puncher", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:puncher_locked_on", { + description = S("Puncher (locked)"), + tiles = { "lwpuncher_face_on.png", "lwpuncher_face_on.png", "lwpuncher.png", + "lwpuncher.png", "lwpuncher.png", "lwpuncher_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:puncher_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_rightclick = on_rightclick +}) + + + +end -- utils.digilines_supported or utils.mesecon_supported + + + +-- diff --git a/readme.txt b/readme.txt index 8286543..de8640a 100644 --- a/readme.txt +++ b/readme.txt @@ -263,6 +263,101 @@ count +Siren +----- +* This block is only available if digilines and/or mesecons are loaded. + +Plays a sound repeatedly while active. Also acts as a digilines conductor. + +UI + +Channel - digilines channel of siren. +Distance - block distance the sound can be heard (range 0 to 100). +Volume - volume the sound is played. +Sound - select Buzzer, Horn, Raid or Siren. + +Mesecons + Sound plays while mesecons power is applied. + +Digilines messages + +"start" + Start the siren (turn on). + +"stop" + Stop the siren (turn off). + +"distance " + Set block distance the sound can be heard. should be a number + from 1 to 100, and is trimmed to this range. + +"volume " + Set the sound volume. should be a number from 1 to 100, and is + trimmed to this range. + +"sound buzzer" +"sound horn" +"sound raid" +"sound siren" + Set the sound of the siren. + +"siren on" + Activate the siren, if its on. + +"siren off" + deactivate the siren. + + + +Puncher +------- +* This block is only available if digilines and/or mesecons are loaded. + +Punches players or entities within a given reach. Also acts as a +digilines conductor. + +UI + +Channel - digilines channel of detector. +Reach - block distance from puncher to punch. +Entities - if checked punches entities. +Players - if checked punches players. + +mode: + Forward - punches to reach extent directly in front of the puncher (one block high). + Up - detects to reach extent directly above the puncher (one block wide). + Down - detects to reach extent directly below the puncher (one block wide). + +Mesecons + Punches the next item when power is turned on. + +Digilines messages + +"start" + Start the puncher. + +"stop" + Stop the puncher. + +"reach " + Set reach of the puncher. should be a number from 1 to 5, and is + trimmed to this range. + +"entities " + Set punching of entities on or off. + +"players " + Set punching of players on or off. + +"mode forward" +"mode up" +"mode down" + Set the puncher's mode. + +"punch" + Action a single punch if the puncher is turned on. + + DigiSwitch ---------- * This block is only available if both digilines and mesecons are loaded. @@ -309,8 +404,7 @@ the MoveFloor will not move. Solid Color Conductors ---------------------- -* These blocks are only defined if mesecons, digilines, and unifieddyes are -loaded. +* These blocks are only defined if mesecons and unifieddyes are loaded. Provides 2 blocks that can be colored the same as Solid Color Block (with the air brush) and is both a mesecons and digilines conductor. diff --git a/screenshot.png b/screenshot.png index 5563ab0..d9e649c 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/siren.lua b/siren.lua new file mode 100644 index 0000000..57966f3 --- /dev/null +++ b/siren.lua @@ -0,0 +1,692 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +local sound_interval = 5.0 + + + +local siren_sounds = +{ + "lwsiren-buzz", + "lwsiren-horn", + "lwsiren-raid", + "lwsiren-siren", +} + + + +local function start_sound (pos) + local meta = minetest.get_meta (pos) + + if meta then + local handle = meta:get_int ("sound_handle") + + if handle ~= 0 then + minetest.sound_stop (handle) + meta:set_int ("sound_handle", 0) + end + + local sound = siren_sounds[meta:get_int ("sound")] + + if sound then + handle = minetest.sound_play ( + sound, + { + pos = pos, + max_hear_distance = meta:get_int ("distance"), + gain = meta:get_int ("gain") / 100 + }) + + meta:set_int ("sound_handle", handle) + end + end +end + + + +local function stop_sound (pos) + local meta = minetest.get_meta (pos) + + if meta then + local handle = meta:get_int ("sound_handle") + + if handle ~= 0 then + minetest.sound_stop (handle) + meta:set_int ("sound_handle", 0) + end + end +end + + + +local function get_form_spec (is_off, distance, gain, sound) + return + "formspec_version[3]\n".. + "size[11.75,6.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".. + "label[1.0,2.5;Distance]\n".. + "scrollbaroptions[min=0;max=100;smallstep=10;largestep=10;thumbsize=10]\n".. + "scrollbar[1.0,2.9;6.0,0.5;horizontal;distance;"..tostring (distance).."]\n".. + "label[1.0,4.2;Volume]\n".. + "scrollbaroptions[min=0;max=100;smallstep=10;largestep=10;thumbsize=10]\n".. + "scrollbar[1.0,4.5;6.0,0.5;horizontal;gain;"..tostring (gain).."]\n".. + "textlist[7.75,2.25;3.0,2.75;sound;Buzzer,Horn,Raid,Siren;"..tostring (sound)..";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:siren" or + node.name == "lwcomponents:siren_locked" + + meta:set_string ("formspec", + get_form_spec (is_off, + meta:get_int ("distance"), + meta:get_int ("gain"), + meta:get_int ("sound"))) + end +end + + + +local function start_siren (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:siren" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren_on" + + stop_sound (pos) + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + elseif node.name == "lwcomponents:siren_locked" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren_locked_on" + + stop_sound (pos) + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + end + end +end + + + +local function stop_siren (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:siren_on" or + node.name == "lwcomponents:siren_alarm" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren" + + minetest.get_node_timer (pos):stop () + stop_sound (pos) + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + elseif node.name == "lwcomponents:siren_locked_on" or + node.name == "lwcomponents:siren_locked_alarm" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren_locked" + + minetest.get_node_timer (pos):stop () + stop_sound (pos) + minetest.swap_node (pos, node) + update_form_spec (pos) + end + + end + end +end + + + +local function start_alarm (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:siren_on" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren_alarm" + + minetest.get_node_timer (pos):start (sound_interval) + start_sound (pos) + minetest.swap_node (pos, node) + end + + elseif node.name == "lwcomponents:siren_locked_on" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren_locked_alarm" + + minetest.get_node_timer (pos):start (sound_interval) + start_sound (pos) + minetest.swap_node (pos, node) + end + + end + end +end + + + +local function stop_alarm (pos) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if node and meta then + if node.name == "lwcomponents:siren_alarm" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren_on" + + minetest.get_node_timer (pos):stop () + stop_sound (pos) + minetest.swap_node (pos, node) + end + + elseif node.name == "lwcomponents:siren_locked_alarm" then + local meta = minetest.get_meta (pos) + + if meta then + node.name = "lwcomponents:siren_locked_on" + + minetest.get_node_timer (pos):stop () + stop_sound (pos) + minetest.swap_node (pos, node) + end + + end + end +end + + + +local function on_destruct (pos) + minetest.get_node_timer (pos):stop () + stop_sound (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:siren" or + itemstack:get_name () == "lwcomponents:siren_locked") + + meta:set_string ("formspec", get_form_spec (is_off, 10, 50, 1)) + + meta:set_int ("sound", 1) + meta:set_int ("distance", 10) + meta:set_int ("gain", 50) + meta:set_int ("sound_handle", 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.start then + start_siren (pos) + end + + if fields.stop then + stop_siren (pos) + end + + if fields.sound then + local event = minetest.explode_textlist_event (fields.sound) + + if event.type == "CHG" then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_int ("sound", event.index) + end + end + end + + if fields.gain then + local event = minetest.explode_scrollbar_event (fields.gain) + + if event.type == "CHG" then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_int ("gain", event.value) + end + end + end + + if fields.distance then + local event = minetest.explode_scrollbar_event (fields.distance) + + if event.type == "CHG" then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_int ("distance", event.value) + 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) + start_sound (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 + + else + update_form_spec (pos) + 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 ~= "" and 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_siren (pos) + + elseif m[1] == "stop" then + stop_siren (pos) + + elseif m[1] == "siren" then + if m[2] == "on" then + start_alarm (pos) + + elseif m[2] == "off" then + stop_alarm (pos) + + end + + elseif m[1] == "distance" then + local distance = math.min (math.max (tonumber (m[2] or 1) or 1, 1), 100) + + meta:set_int ("distance", distance) + + elseif m[1] == "volume" then + local volume = math.min (math.max (tonumber (m[2] or 1) or 1, 1), 100) + + meta:set_int ("gain", volume) + + elseif m[1] == "sound" then + if m[2] == "buzzer" then + meta:set_int ("sound", 1) + update_form_spec (pos) + + elseif m[2] == "horn" then + meta:set_int ("sound", 2) + update_form_spec (pos) + + elseif m[2] == "raid" then + meta:set_int ("sound", 3) + update_form_spec (pos) + + elseif m[2] == "siren" then + meta:set_int ("sound", 4) + update_form_spec (pos) + + end + end + end + end + end, + } + } + end + + return nil +end + + + +local function mesecon_support () + if utils.mesecon_supported then + return + { + effector = + { + rules = utils.mesecon_default_rules, + + action_on = function (pos, node) + -- do something to turn the effector on + start_alarm (pos) + end, + + action_off = function (pos, node) + -- do something to turn the effector off + stop_alarm (pos) + end, + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:siren", { + description = S("Siren"), + tiles = { "lwsiren_base.png", "lwsiren_base.png", "lwsiren.png", + "lwsiren.png", "lwsiren.png", "lwsiren.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:siren", + _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:siren_locked", { + description = S("Siren (locked)"), + tiles = { "lwsiren_base.png", "lwsiren_base.png", "lwsiren.png", + "lwsiren.png", "lwsiren.png", "lwsiren.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:siren_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:siren_on", { + description = S("Siren"), + tiles = { "lwsiren_base.png", "lwsiren_base.png", "lwsiren_on.png", + "lwsiren_on.png", "lwsiren_on.png", "lwsiren_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:siren", + _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:siren_locked_on", { + description = S("Siren (locked)"), + tiles = { "lwsiren_base.png", "lwsiren_base.png", "lwsiren_on.png", + "lwsiren_on.png", "lwsiren_on.png", "lwsiren_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + floodable = false, + drop = "lwcomponents:siren_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:siren_alarm", { + description = S("Siren"), + tiles = { "lwsiren_base.png", "lwsiren_base.png", "lwsiren_alarm.png", + "lwsiren_alarm.png", "lwsiren_alarm.png", "lwsiren_alarm.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + light_source = 3, + floodable = false, + drop = "lwcomponents:siren", + _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:siren_locked_alarm", { + description = S("Siren (locked)"), + tiles = { "lwsiren_base.png", "lwsiren_base.png", "lwsiren_alarm.png", + "lwsiren_alarm.png", "lwsiren_alarm.png", "lwsiren_alarm.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + light_source = 3, + floodable = false, + drop = "lwcomponents:siren_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/solid_conductor.lua b/solid_conductor.lua index 4d82666..ebaf8b5 100644 --- a/solid_conductor.lua +++ b/solid_conductor.lua @@ -3,7 +3,7 @@ local S = utils.S -if utils.unifieddyes_supported and utils.mesecon_supported and utils.digilines_supported then +if utils.unifieddyes_supported and utils.mesecon_supported then @@ -17,7 +17,7 @@ mesecon.register_node (":lwcomponents:solid_conductor", palette = "unifieddyes_palette_extended.png", on_rotate = false, drop = "lwcomponents:solid_conductor_off", - digiline = { wire = { rules = mesecon.rules.default } }, + digiline = { wire = { rules = utils.digilines_default_rules } }, on_construct = unifieddyes.on_construct, on_dig = unifieddyes.on_dig, }, @@ -27,8 +27,8 @@ mesecon.register_node (":lwcomponents:solid_conductor", { conductor = { - rules = mesecon.rules.default, - state = mesecon.state.off, + rules = utils.mesecon_default_rules, + state = utils.mesecon_state_off, onstate = "lwcomponents:solid_conductor_on", } }, @@ -43,8 +43,8 @@ mesecon.register_node (":lwcomponents:solid_conductor", { conductor = { - rules = mesecon.rules.default, - state = mesecon.state.on, + rules = utils.mesecon_default_rules, + state = utils.mesecon_state_on, offstate = "lwcomponents:solid_conductor_off", } }, @@ -83,7 +83,7 @@ mesecon.register_node (":lwcomponents:solid_horizontal_conductor", palette = "unifieddyes_palette_extended.png", on_rotate = false, drop = "lwcomponents:solid_horizontal_conductor_off", - digiline = { wire = { rules = mesecon.rules.flat } }, + digiline = { wire = { rules = utils.digilines_flat_rules } }, on_construct = unifieddyes.on_construct, on_dig = unifieddyes.on_dig, }, @@ -93,8 +93,8 @@ mesecon.register_node (":lwcomponents:solid_horizontal_conductor", { conductor = { - rules = mesecon.rules.flat, - state = mesecon.state.off, + rules = utils.mesecon_flat_rules, + state = utils.mesecon_state_off, onstate = "lwcomponents:solid_horizontal_conductor_on", } }, @@ -109,8 +109,8 @@ mesecon.register_node (":lwcomponents:solid_horizontal_conductor", { conductor = { - rules = mesecon.rules.flat, - state = mesecon.state.on, + rules = utils.mesecon_flat_rules, + state = utils.mesecon_state_on, offstate = "lwcomponents:solid_horizontal_conductor_off", } }, @@ -139,4 +139,4 @@ unifieddyes.register_color_craft ({ -end -- utils.unifieddyes_supported and utils.mesecon_supported and utils.digilines_supported then +end -- utils.unifieddyes_supported and utils.mesecon_supported then diff --git a/utils.lua b/utils.lua index 9072f79..44a8df0 100644 --- a/utils.lua +++ b/utils.lua @@ -47,9 +47,18 @@ end -- check for digilines if minetest.global_exists ("digilines") then utils.digilines_supported = true + utils.digilines_default_rules = digiline.rules.default + utils.digilines_flat_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 }, + } utils.digilines_receptor_send = digilines.receptor_send else utils.digilines_supported = false + utils.digilines_default_rules = { } + utils.digilines_flat_rules = { } -- dummy utils.digilines_receptor_send = function (pos, rules, channel, msg)