local S = minetest.get_translator("lzr_laser") local mirror_out = { [0] = {1,0,0}, [1] = {0,0,-1}, [2] = {-1,0,0}, [3] = {0,0,1}, [4] = {1,0,0}, [5] = {0,1,0}, [6] = {-1,0,0}, [7] = {0,-1,0}, [8] = {1,0,0}, [9] = {0,-1,0}, [10] = {-1,0,0}, [11] = {0,1,0}, [12] = {0,-1,0}, [13] = {0,0,-1}, [14] = {0,1,0}, [15] = {0,0,1}, [16] = {0,1,0}, [17] = {0,0,-1}, [18] = {0,-1,0}, [19] = {0,0,1}, [20] = {-1,0,0}, [21] = {0,0,-1}, [22] = {1,0,0}, [23] = {0,0,1}, } lzr_laser.get_front_dir = function(param2) if not param2 then return end local dir_input = minetest.facedir_to_dir(param2) if not dir_input then return end local v = vector.new(dir_input[1], dir_input[2], dir_input[3]) dir_input = vector.multiply(v, -1) return dir_input end lzr_laser.check_front = function(node_pos, laser_dir) local node = minetest.get_node(node_pos) local node_dir_in = lzr_laser.get_front_dir(node.param2) if not node_dir_in then return false end local reverse_laser_dir = vector.multiply(laser_dir, -1) if vector.equals(reverse_laser_dir, node_dir_in) then return true end return false end lzr_laser.check_detector = function(detector_pos, laser_dir) local detector = minetest.get_node(detector_pos) local detector_group = minetest.get_item_group(detector.name, "detector") if detector_group == 0 then return false end return lzr_laser.check_front(detector_pos, laser_dir) end lzr_laser.get_mirror_dirs = function(param2) local dir_input = minetest.facedir_to_dir(param2) if not dir_input then return end dir_input = vector.multiply(dir_input, -1) local dir_output = vector.new(unpack(mirror_out[param2])) return dir_input, dir_output end -- Mirror a laser that touches a mirror at mirror_pos with a laser coming towards -- the mirror with the laser_dir direction (direction vector). -- Returns , -- 1) If the laser can be mirrored, then is the direction of -- the mirrored laser and denotes on of the 2 possible -- sides the laser came from: true if it came in the front side, false if -- it came from the angled side. is useful for the -- transmissive mirror. -- 2) If the laser cannot be mirrored is false and -- is nil lzr_laser.get_mirrored_laser_dir = function(mirror_pos, laser_dir) local mirror = minetest.get_node(mirror_pos) local mirror_group = minetest.get_item_group(mirror.name, "mirror") local mirror_transmissive_group = minetest.get_item_group(mirror.name, "transmissive_mirror") if mirror_group == 0 and mirror_transmissive_group == 0 then return false end local reverse_laser_dir = vector.multiply(laser_dir, -1) local mirror_dir_in, mirror_dir_out = lzr_laser.get_mirror_dirs(mirror.param2) if not mirror_dir_in then return false end if vector.equals(reverse_laser_dir, mirror_dir_in) then return mirror_dir_out, true elseif vector.equals(reverse_laser_dir, mirror_dir_out) then return mirror_dir_in, false end return false end function lzr_laser.unlock_chests(min, max) local closed_chests = minetest.find_nodes_in_area(min, max, {"group:chest_closed"}) local detectors = minetest.find_nodes_in_area(min, max, {"group:detector"}) for c=1, #closed_chests do local cpos = closed_chests[c] local node = minetest.get_node(cpos) local def = minetest.registered_nodes[node.name] if def._lzr_unlock then def._lzr_unlock(cpos, node) for d=1, #detectors do local dpos = detectors[d] lzr_laser.particle_line(dpos, cpos, "lzr_laser_particle_trigger.png") end end end end -- Update the whole playfield after placing or digging a laser node local full_update = function() local state = lzr_gamestate.get_state() if state ~= lzr_gamestate.LEVEL and state ~= lzr_gamestate.EDITOR then return end lzr_laser.full_laser_update(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END) if state == lzr_gamestate.EDITOR then return end local done = lzr_laser.check_level_won() if done then lzr_levels.level_complete() else local detectors = lzr_laser.check_all_detectors() if detectors then lzr_laser.unlock_chests(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END) end end end -- Same as above, but special case after a detector was placed. -- This is hack to tell the detector check that there one detector in the player -- inventory has to be ignored because after_dig_node is called BEFORE -- the inventory change after placing the node. local full_update_detector_placed = function() local state = lzr_gamestate.get_state() if state ~= lzr_gamestate.LEVEL and state ~= lzr_gamestate.EDITOR then return end lzr_laser.full_laser_update(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END) if state == lzr_gamestate.EDITOR then return end local done = lzr_laser.check_level_won() if done then lzr_levels.level_complete() else local detectors = lzr_laser.check_all_detectors(true) if detectors then lzr_laser.unlock_chests(lzr_globals.PLAYFIELD_START, lzr_globals.PLAYFIELD_END) end end end local after_rotate = function() full_update() end local element_can_dig = function() return lzr_gamestate.get_state() ~= lzr_gamestate.LEVEL_COMPLETE end local element_on_place = function(itemstack, placer, pointed_thing) if lzr_gamestate.get_state() == lzr_gamestate.LEVEL_COMPLETE then -- Prevent node placement when in 'level complete' state return itemstack else -- node's on_rightclick action takes precedence if pointed_thing.type == "node" and placer then local node = minetest.get_node(pointed_thing.under) local def = minetest.registered_nodes[node.name] local sneak = placer:get_player_control().sneak if def and def.on_rightclick and not sneak then def.on_rightclick(pointed_thing.under, node, placer, itemstack, pointed_thing) return itemstack end end -- Default node placement behavior return minetest.item_place_node(itemstack, placer, pointed_thing) end end local register_element = function(subname, def, options) local def_core = table.copy(def) if not options then options = {} end if options.is_detector then def_core.after_place_node = full_update_detector_placed else def_core.after_place_node = full_update end if options.not_walkable_if_off then def_core.walkable = false end if options.allow_take then def_core._lzr_takable = "lzr_laser:"..subname.."_takable" end def_core.description = S("@1 (fixed)", def.description) def_core.after_dig_node = full_update def_core._after_rotate = after_rotate if options.activate ~= false then def_core._lzr_active = "lzr_laser:"..subname.."_on" end def_core.tiles = def.tiles_off def_core.mesh = def.mesh_off if not def_core.groups then def_core.groups = {} end def_core.groups.breakable = 1 local groupname if options.group then groupname = options.group else groupname = subname end def_core.groups[groupname] = 1 def_core.can_dig = element_can_dig def_core.on_place = element_on_place if options.inactive ~= nil then def_core._lzr_inactive = options.inactive if not options.keep_state_on_take then def_core.drop = options.inactive end end minetest.register_node("lzr_laser:"..subname, def_core) local def_core_on if options.activate ~= false then def_core_on = table.copy(def_core) if options.not_walkable_if_off then def_core_on.walkable = true elseif options.not_walkable_if_on then def_core_on.walkable = false end def_core_on.description = S("@1 (fixed, active)", def.description) def_core_on._lzr_active = nil def_core_on._lzr_inactive = "lzr_laser:"..subname def_core_on.tiles = def.tiles_on def_core_on.mesh = def.mesh_on if not options.keep_state_on_take then def_core_on.drop = "lzr_laser:"..subname end if options.allow_take then def_core_on._lzr_takable = "lzr_laser:"..subname.."_takable_on" end def_core_on.groups[groupname] = 2 def_core_on.light_source = def.light_source_on minetest.register_node("lzr_laser:"..subname.."_on", def_core_on) end if options.allow_take then local def_takable = table.copy(def_core) def_takable.tiles = def.tiles_takable_off def_takable.groups.takable = 1 def_takable._lzr_untakable = "lzr_laser:"..subname def_takable.description = def.description if options.inactive ~= nil then def_takable._lzr_inactive = options.inactive.."_takable" if not options.keep_state_on_take then def_takable.drop = options.inactive.."_takable" end end minetest.register_node("lzr_laser:"..subname.."_takable", def_takable) if options.activate ~= false then def_takable._lzr_active = "lzr_laser:"..subname.."_takable_on" local def_takable_on = table.copy(def_core_on) def_takable_on._lzr_untakable = "lzr_laser:"..subname.."_on" def_takable_on.tiles = def.tiles_takable_on def_takable_on.light_source = def.light_source_on def_takable_on.groups.takable = 1 def_takable_on.description = S("@1 (active)", def.description) if not options.keep_state_on_take then def_takable_on.drop = "lzr_laser:"..subname.."_takable" end def_takable_on._lzr_inactive = "lzr_laser:"..subname.."_takable" minetest.register_node("lzr_laser:"..subname.."_takable_on", def_takable_on) end end end register_element("crate", { description = S("Crate"), tiles_off = { "lzr_laser_crate_fixed.png", }, tiles_takable_off = { "lzr_laser_crate.png", }, sounds = lzr_sounds.node_sound_wood_defaults(), }, { allow_take = true, activate = false }) register_element("mirror", { description = S("Mirror"), paramtype = "light", paramtype2 = "facedir", tiles_off = { {name="lzr_laser_mirror_block.png^lzr_laser_fixed.png", backface_culling=true}, {name="lzr_laser_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_mirror_block.png^lzr_laser_fixed.png", backface_culling=true}, }, tiles_on = { {name="lzr_laser_laser.png", backface_culling=true}, {name="lzr_laser_mirror_block.png^lzr_laser_fixed.png", backface_culling=true}, {name="lzr_laser_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_mirror_block.png^lzr_laser_fixed.png", backface_culling=true}, }, tiles_takable_off = { {name="lzr_laser_mirror_block.png", backface_culling=true}, {name="lzr_laser_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_mirror_block.png", backface_culling=true}, }, tiles_takable_on = { {name="lzr_laser_laser.png", backface_culling=true}, {name="lzr_laser_mirror_block.png", backface_culling=true}, {name="lzr_laser_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_mirror_block.png", backface_culling=true}, }, use_texture_alpha = "blend", drawtype = "mesh", mesh_off = "lzr_laser_mirror.obj", mesh_on = "lzr_laser_mirror_on.obj", light_source_on = lzr_globals.LASER_GLOW, groups = { rotatable = 1, laser_block = 1 }, sounds = lzr_sounds.node_sound_glass_defaults({ _rotate = {name = "lzr_laser_mirror_rotate", gain = 1.0}, }) }, { allow_take = true }) local tm_def = { description = S("Transmissive Mirror"), paramtype = "light", paramtype2 = "facedir", use_texture_alpha = "blend", drawtype = "mesh", groups = { rotatable = 1, laser_block = 1 }, sounds = lzr_sounds.node_sound_glass_defaults({ _rotate = {name = "lzr_laser_mirror_rotate", gain = 1.0}, }), _lzr_transmissive_mirror_00 = "lzr_laser:transmissive_mirror_00", _lzr_transmissive_mirror_01 = "lzr_laser:transmissive_mirror_01", _lzr_transmissive_mirror_10 = "lzr_laser:transmissive_mirror_10", _lzr_transmissive_mirror_11 = "lzr_laser:transmissive_mirror_11", } local tm_def_off = table.copy(tm_def) tm_def_off._lzr_transmissive_mirror_state = "00" tm_def_off.tiles_off = { {name="lzr_laser_transmissive_mirror_block.png^lzr_laser_fixed.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror_out.png", backface_culling=true}, } tm_def_off.tiles_takable_off = { {name="lzr_laser_transmissive_mirror_block.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror_out.png", backface_culling=true}, } tm_def_off.mesh_off = "lzr_laser_mirror.obj" -- Use custom fields to identify transmissive mirror states -- Transmissive Mirror local tm_def_on_01 = table.copy(tm_def) tm_def_on_01.light_source = lzr_globals.LASER_GLOW tm_def_on_01._lzr_transmissive_mirror_state = "01" tm_def_on_01.description = S("Transmissive Mirror (active, 01)") tm_def_on_01.tiles_off = { {name="lzr_laser_laser.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_block.png^lzr_laser_fixed.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror_out.png", backface_culling=true}, } tm_def_on_01.tiles_takable_off = { {name="lzr_laser_laser.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_block.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror.png", backface_culling=true}, {name="lzr_laser_transmissive_mirror_mirror_out.png", backface_culling=true}, } tm_def_on_01.mesh_off = "lzr_laser_mirror_on_thru1.obj" local tm_def_on_10 = table.copy(tm_def_on_01) tm_def_on_10._lzr_transmissive_mirror_state = "10" tm_def_on_10.description = S("Transmissive Mirror (active, 10)") tm_def_on_10.mesh_off = "lzr_laser_mirror_on_thru2.obj" local tm_def_on_11 = table.copy(tm_def_on_01) tm_def_on_11._lzr_transmissive_mirror_state = "11" tm_def_on_11.description = S("Transmissive Mirror (active, 11)") tm_def_on_11.mesh_off = "lzr_laser_mirror_on_thru3.obj" local tm_options_off = { allow_take = true, activate = false, group = "transmissive_mirror" } local tm_options_on = { allow_take = true, activate = false, group = "transmissive_mirror", inactive = "lzr_laser:transmissive_mirror_00" } register_element("transmissive_mirror_00", tm_def_off, tm_options_off) register_element("transmissive_mirror_01", tm_def_on_01, tm_options_on) register_element("transmissive_mirror_10", tm_def_on_10, tm_options_on) register_element("transmissive_mirror_11", tm_def_on_11, tm_options_on) register_element("crystal", { description = S("Crystal"), paramtype = "light", paramtype2 = "facedir", tiles_takable_off = { "lzr_laser_crystal.png", }, tiles_takable_on = { "lzr_laser_crystal_on.png", }, tiles_off = { "lzr_laser_crystal.png^lzr_laser_fixed.png", }, tiles_on = { "lzr_laser_crystal_on.png^lzr_laser_fixed.png", }, light_source_on = lzr_globals.LASER_GLOW, groups = { laser_block = 1 }, sounds = lzr_sounds.node_sound_glass_defaults({ _rotate = {name = "lzr_laser_mirror_rotate", gain = 1.0}, }) }, { allow_take = true }) register_element("emitter", { description = S("Emitter"), paramtype2 = "facedir", tiles_takable_off = { "lzr_laser_emitter.png", "lzr_laser_emitter.png", "lzr_laser_emitter.png", "lzr_laser_emitter.png", "lzr_laser_emitter.png", "lzr_laser_emitter_front.png", }, tiles_takable_on = { "lzr_laser_emitter_on.png", "lzr_laser_emitter_on.png", "lzr_laser_emitter_on.png", "lzr_laser_emitter_on.png", "lzr_laser_emitter_on.png", "lzr_laser_emitter_on_front.png", }, tiles_off = { "lzr_laser_emitter.png^lzr_laser_fixed.png", "lzr_laser_emitter.png^lzr_laser_fixed.png", "lzr_laser_emitter.png^lzr_laser_fixed.png", "lzr_laser_emitter.png^lzr_laser_fixed.png", "lzr_laser_emitter.png^lzr_laser_fixed.png", "lzr_laser_emitter_front.png^lzr_laser_fixed.png", }, tiles_on = { "lzr_laser_emitter_on.png^lzr_laser_fixed.png", "lzr_laser_emitter_on.png^lzr_laser_fixed.png", "lzr_laser_emitter_on.png^lzr_laser_fixed.png", "lzr_laser_emitter_on.png^lzr_laser_fixed.png", "lzr_laser_emitter_on.png^lzr_laser_fixed.png", "lzr_laser_emitter_on_front.png^lzr_laser_fixed.png", }, light_source_on = 7, _lzr_on_toggle = function(pos, node) if lzr_gamestate.get_state() == lzr_gamestate.LEVEL_COMPLETE then return end local nname local on = false if node.name == "lzr_laser:emitter" then nname = "lzr_laser:emitter_on" on = true elseif node.name == "lzr_laser:emitter_on" then nname = "lzr_laser:emitter" elseif node.name == "lzr_laser:emitter_takable" then nname = "lzr_laser:emitter_takable_on" on = true elseif node.name == "lzr_laser:emitter_takable_on" then nname = "lzr_laser:emitter_takable" end if on then minetest.sound_play({name="lzr_laser_emitter_activate", gain=1.0}, {pos=pos}, true) else minetest.sound_play({name="lzr_laser_emitter_activate", gain=0.8}, {pos=pos, pitch=0.7}, true) end minetest.swap_node(pos, {name=nname, param2=node.param2}) full_update() end, groups = { rotatable = 2, laser_block = 1 }, sounds = lzr_sounds.node_sound_wood_defaults(), }, { allow_take = true, keep_state_on_take = true }) register_element("detector", { description = S("Detector"), paramtype2 = "facedir", tiles_off = { "lzr_laser_detector.png^lzr_laser_fixed.png", "lzr_laser_detector.png^lzr_laser_fixed.png", "lzr_laser_detector.png^lzr_laser_fixed.png", "lzr_laser_detector.png^lzr_laser_fixed.png", "lzr_laser_detector.png^lzr_laser_fixed.png", "lzr_laser_detector_front.png^lzr_laser_fixed.png", }, tiles_on = { "lzr_laser_detector_on.png^lzr_laser_fixed.png", "lzr_laser_detector_on.png^lzr_laser_fixed.png", "lzr_laser_detector_on.png^lzr_laser_fixed.png", "lzr_laser_detector_on.png^lzr_laser_fixed.png", "lzr_laser_detector_on.png^lzr_laser_fixed.png", "lzr_laser_detector_on_front.png^lzr_laser_fixed.png", }, tiles_takable_off = { "lzr_laser_detector.png", "lzr_laser_detector.png", "lzr_laser_detector.png", "lzr_laser_detector.png", "lzr_laser_detector.png", "lzr_laser_detector_front.png", }, tiles_takable_on = { "lzr_laser_detector_on.png", "lzr_laser_detector_on.png", "lzr_laser_detector_on.png", "lzr_laser_detector_on.png", "lzr_laser_detector_on.png", "lzr_laser_detector_on_front.png", }, light_source_on = 5, groups = { rotatable = 2, laser_block = 1 }, sounds = lzr_sounds.node_sound_wood_defaults(), }, { allow_take = true, is_detector = true }) -- Is non-walkable if off, -- and walkable if on. register_element("skull_cursed", { description = S("Cursed Skull (experimental)"), paramtype2 = "facedir", -- TODO: Draw proper images tiles_off = { "lzr_laser_cskull_top.png^lzr_laser_fixed.png", "lzr_laser_cskull_bottom.png^lzr_laser_fixed.png", "lzr_laser_cskull_side.png^lzr_laser_fixed.png", "lzr_laser_cskull_side.png^lzr_laser_fixed.png", "lzr_laser_cskull_back.png^lzr_laser_fixed.png", "lzr_laser_cskull_front.png^lzr_laser_fixed.png", }, tiles_on = { "lzr_laser_cskull_on_top.png^lzr_laser_fixed.png", "lzr_laser_cskull_on_bottom.png^lzr_laser_fixed.png", "lzr_laser_cskull_on_side.png^lzr_laser_fixed.png", "lzr_laser_cskull_on_side.png^lzr_laser_fixed.png", "lzr_laser_cskull_on_back.png^lzr_laser_fixed.png", "lzr_laser_cskull_on_front.png^lzr_laser_fixed.png", }, tiles_takable_off = { "lzr_laser_cskull_top.png", "lzr_laser_cskull_bottom.png", "lzr_laser_cskull_side.png", "lzr_laser_cskull_side.png", "lzr_laser_cskull_back.png", "lzr_laser_cskull_front.png", }, tiles_takable_on = { "lzr_laser_cskull_on_top.png", "lzr_laser_cskull_on_bottom.png", "lzr_laser_cskull_on_side.png", "lzr_laser_cskull_on_side.png", "lzr_laser_cskull_on_back.png", "lzr_laser_cskull_on_front.png", }, light_source_on = 5, groups = { rotatable = 2, laser_block = 1 }, on_rotate = screwdriver.rotate_simple, sounds = lzr_sounds.node_sound_stone_defaults(), }, { allow_take = true, not_walkable_if_off = true }) -- Is walkable if off, -- and non-walkable if on. register_element("skull_shy", { description = S("Shy Skull (experimental)"), paramtype2 = "facedir", -- TODO: Draw proper images tiles_off = { "lzr_laser_sskull_top.png^lzr_laser_fixed.png", "lzr_laser_sskull_bottom.png^lzr_laser_fixed.png", "lzr_laser_sskull_side.png^lzr_laser_fixed.png", "lzr_laser_sskull_side.png^lzr_laser_fixed.png", "lzr_laser_sskull_back.png^lzr_laser_fixed.png", "lzr_laser_sskull_front.png^lzr_laser_fixed.png", }, tiles_on = { "lzr_laser_sskull_on_top.png^lzr_laser_fixed.png", "lzr_laser_sskull_on_bottom.png^lzr_laser_fixed.png", "lzr_laser_sskull_on_side.png^lzr_laser_fixed.png", "lzr_laser_sskull_on_side.png^lzr_laser_fixed.png", "lzr_laser_sskull_on_back.png^lzr_laser_fixed.png", "lzr_laser_sskull_on_front.png^lzr_laser_fixed.png", }, tiles_takable_off = { "lzr_laser_sskull_top.png", "lzr_laser_sskull_bottom.png", "lzr_laser_sskull_side.png", "lzr_laser_sskull_side.png", "lzr_laser_sskull_back.png", "lzr_laser_sskull_front.png", }, tiles_takable_on = { "lzr_laser_sskull_on_top.png", "lzr_laser_sskull_on_bottom.png", "lzr_laser_sskull_on_side.png", "lzr_laser_sskull_on_side.png", "lzr_laser_sskull_on_back.png", "lzr_laser_sskull_on_front.png", }, light_source_on = 5, groups = { rotatable = 2, laser_block = 1 }, on_rotate = screwdriver.rotate_simple, sounds = lzr_sounds.node_sound_stone_defaults(), }, { allow_take = true, not_walkable_if_on = true }) minetest.register_node("lzr_laser:barricade", { description = S("Barricade"), drawtype = "mesh", mesh = "lzr_laser_burning.obj", paramtype = "light", inventory_image = "xdecor_baricade.png", wield_image = "xdecor_baricade.png", tiles = {"blank.png","xdecor_baricade.png"}, use_texture_alpha = "clip", groups = { breakable = 1, flammable = 1, laser_destroys = 2 }, sounds = lzr_sounds.node_sound_wood_defaults({ dug = {name="lzr_laser_barricade_break", gain=1.0}, }), on_place = element_on_place, can_dig = element_can_dig, _lzr_active = "lzr_laser:barricade_on", }) minetest.register_node("lzr_laser:barricade_on", { description = S("Burning Barricade"), drawtype = "mesh", paramtype = "light", light_source = 12, mesh = "lzr_laser_burning.obj", inventory_image = "lzr_laser_baricade_burning.png", wield_image = "lzr_laser_baricade_burning.png", use_texture_alpha = "clip", tiles = { { name="fire_basic_flame_animated.png", animation={ type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 1.0, },}, { name="lzr_laser_baricade_burning_animated.png", animation={ type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = 1.0, },}, }, groups = { breakable = 1, not_in_creative_inventory = 1 }, sounds = lzr_sounds.node_sound_wood_defaults({ dug = {name="lzr_laser_barricade_break", gain=1.0}, }), on_construct = function(pos) minetest.sound_play({name="lzr_laser_quickburn", gain=1.0}, {pos=pos}, true) local timer = minetest.get_node_timer(pos) timer:start(1) end, on_timer = function(pos) -- spawn a few particles for the removed node minetest.add_particlespawner({ amount = 16, time = 0.001, minpos = vector.subtract(pos, vector.new(0.5, 0.5, 0.5)), maxpos = vector.add(pos, vector.new(0.5, 0.5, 0.5)), minvel = vector.new(-0.5, -0.2, -0.5), maxvel = vector.new(0.5, 0.2, 0.5), minacc = vector.new(0, -lzr_globals.GRAVITY, 0), maxacc = vector.new(0, -lzr_globals.GRAVITY, 0), minsize = 1.5, maxsize = 1.5, node = {name="lzr_laser:barricade"}, }) -- Play randomly-pitched break sound local pitch = 1.0+math.random(-100, 100)*0.001 -- 0.9..1.1 minetest.sound_play({name="lzr_laser_barricade_break", gain=1.0}, {gain=1.0, pitch=pitch, pos=pos}, true) -- remove node minetest.remove_node(pos) full_update() -- propagate to neighbors local posses = { vector.new(-1,0,0), vector.new(1,0,0), vector.new(0,-1,0), vector.new(0,1,0), vector.new(0,0,-1), vector.new(0,0,1), } for p=1, #posses do local ppos = vector.add(pos, posses[p]) local node = minetest.get_node(ppos) local def = minetest.registered_nodes[node.name] if def and def._lzr_active then if minetest.get_item_group(node.name, "flammable") > 0 then minetest.set_node(ppos, {name=def._lzr_active}) end end end end, drop = "", on_place = element_on_place, can_dig = element_can_dig, _lzr_inactive = "lzr_laser:barricade", })