local S = minetest.get_translator("lzr_laser")

local colortiles = {
	lzr_laser.TILE_LASER_R,
	lzr_laser.TILE_LASER_G,
	lzr_laser.TILE_LASER_Y,
	lzr_laser.TILE_LASER_B,
	lzr_laser.TILE_LASER_M,
	lzr_laser.TILE_LASER_C,
	lzr_laser.TILE_LASER_W,
}

-- Opacity (0-255) of skull when it is "gone",
-- i.e. is non-walkable
local OPACITY_SKULL_GONE = 128

-- If using patterns on lasers, patterns will be drawn on detectors
-- and emitters as well
local use_patterns = minetest.settings:get_bool("lzr_patterned_lasers", false)

minetest.register_node("lzr_laser:crate", {
	description = S("Heavy Crate"),
	tiles = {
		"lzr_laser_crate_fixed.png",
	},
	sounds = lzr_sounds.node_sound_wood_defaults(),
	groups = { crate = 1, breakable = 1 },
})
-- Crate that is destroyed at the slightest touch
minetest.register_node("lzr_laser:crate_old", {
	description = S("Old Crate"),
	tiles = {
		"lzr_laser_crate_old.png",
	},
	sounds = lzr_sounds.node_sound_wood_squeak_defaults({
		dug = { name = "lzr_laser_crate_old_break", gain = 0.3 },
	}),
	groups = { crate = 1, breakable = 1, punchdig = 1, explosion_destroys = 1 },
	drop = "",
})
minetest.register_node("lzr_laser:crate_mossy", {
	description = S("Mossy Heavy Crate"),
	tiles = {
		"lzr_laser_crate_fixed_mossy.png",
	},
	sounds = lzr_sounds.node_sound_wood_defaults(),
	groups = { crate = 1, breakable = 1 },
})
minetest.register_node("lzr_laser:crate_takable", {
	description = S("Light Crate"),
	_tt_help = S("Light enough to be picked up"),
	tiles = {
		"lzr_laser_crate.png",
	},
	sounds = lzr_sounds.node_sound_wood_defaults(),
	groups = { crate = 1, breakable = 1, takable = 1 },
})

lzr_laser.register_element("lzr_laser:mirror", {
	description = S("Mirror"),
	_tt_help = S("Deflects a laser"),
	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_backside.png^lzr_laser_fixed.png", backface_culling=true},
		{name="(lzr_laser_mirror_backside.png^[transformFX)^lzr_laser_fixed.png", backface_culling=true},
	},
	__tiles_on = {
		lzr_laser.LASER_TILE,
		{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_backside.png^lzr_laser_fixed.png", backface_culling=true},
		{name="(lzr_laser_mirror_backside.png^[transformFX)^lzr_laser_fixed.png", backface_culling=true},
	},
	__tiles_rotatable_off = {
		{name="lzr_laser_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
		{name="lzr_laser_mirror_mirror.png", backface_culling=true},
		{name="lzr_laser_mirror_backside.png^lzr_laser_fixed_soft.png", backface_culling=true},
		{name="(lzr_laser_mirror_backside.png^[transformFX)^lzr_laser_fixed_soft.png", backface_culling=true},
	},
	__tiles_rotatable_on = {
		lzr_laser.LASER_TILE,
		{name="lzr_laser_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
		{name="lzr_laser_mirror_mirror.png", backface_culling=true},
		{name="lzr_laser_mirror_backside.png^lzr_laser_fixed_soft.png", backface_culling=true},
		{name="(lzr_laser_mirror_backside.png^[transformFX)^lzr_laser_fixed_soft.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_backside.png", backface_culling=true},
		{name="lzr_laser_mirror_backside.png^[transformFX", backface_culling=true},
	},
	__tiles_takable_on = {
		lzr_laser.LASER_TILE,
		{name="lzr_laser_mirror_block.png", backface_culling=true},
		{name="lzr_laser_mirror_mirror.png", backface_culling=true},
		{name="lzr_laser_mirror_backside.png", backface_culling=true},
		{name="lzr_laser_mirror_backside.png^[transformFX", backface_culling=true},
	},
	__use_texture_alpha_off = "clip",
	__use_texture_alpha_on = lzr_laser.ALPHA_LASER,
	drawtype = "mesh",
	__mesh_off = "lzr_laser_mirror.obj",
	__mesh_on = "lzr_laser_mirror_on.obj",

	__light_source_on = lzr_globals.LASER_GLOW,

	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_glass_defaults()
}, { group = "mirror", allow_take = true, allow_rotate = true })



-- Beam splitter (internally called "transmissive_mirror")
-- Splits a laser beam into two lasers.
local tm_def = {
	description = S("Beam Splitter"),
	_tt_help = S("Both deflects a laser and lets it through"),
	paramtype = "light",
	paramtype2 = "facedir",
	use_texture_alpha = lzr_laser.ALPHA_LASER,
	drawtype = "mesh",
	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_glass_defaults(),
}

-- Beam splitter, inactive (state 00)
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^(lzr_laser_fixed.png^[mask:lzr_laser_transmissive_mirror_mirror_out_screw_mask.png)", backface_culling=true},
}
tm_def_off.__tiles_rotatable_off = {
	{name="lzr_laser_transmissive_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
	{name="lzr_laser_transmissive_mirror_mirror.png", backface_culling=true},
	{name="lzr_laser_transmissive_mirror_mirror_out.png^(lzr_laser_fixed_soft.png^[mask:lzr_laser_transmissive_mirror_mirror_out_screw_mask.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_tmirror.obj"
local tm_options_off = { allow_take = true, allow_rotate = true, activate = false, group = "transmissive_mirror", register_color = false }
lzr_laser.register_element("lzr_laser:transmissive_mirror_00", tm_def_off, tm_options_off)


-- Beam splitter, active

-- Register active beam splitters
--[[
This registers a LOT of nodes, for each laser color AND
laser color combination. The beam splitter needs to deal with
multiple cases in which the laser impacts the mirror from
one of two possible sides of the angled mirror.

There are 4 basic possibilities:

* No laser: State 00 (inactive state, already handled above)
* Laser from side A: State X0
* Laser from side B: State 0X
* Laser from both sides: State XX

Where X = a colorcode of laser

The incoming direction of the laser is important because it determines
on which side the laser will "go through" the mirror.

In the following pictures, we will label each side:

* A: Front of the mirror
* B: Right of the mirror (relatively spoken)
* C: Opposite side of A
* D: Opposite side of B

  C
D   B
  A

The lasers may come from side A or side B.
If they come from side C or D, they are blocked.

A laser from side A will be deflected to B
but also be "let through" to C. This is State X0.


     C

     ^
     |
   +---+
   | |/ 
D  | /---> B
   |/|  
   + |  
     |
     ^
     
     A

A laser from side B will be deflected to A
but also be "let through" to D. This is State 0X.

       C

     +---+
     |  / 
D <--|-/---< B
     |/|  
     + |  
       |
       v
       
       A

If lasers come from both sides, this is
essentially a combination of State 0X and X0.
This is State XX. It looks like this:

       C

       ^
       |
       |
     +---+
     | |/ 
D <--|-/----<> B
     |/|  
     + |  
       |
       ^
       v

       A

Laser from A is deflected to B, AND
the laser from B is deflected to A. This causes
both lasers to overlap while travelling between A and B,
causing the colors to be mixed. E.g. red + green = yellow.
This is just the normal laser behavior, overlapping
happens with the regular laser as well.

But also, laser from A goes through to C, AND
laser from B goes through to D. The "let through"
portion of the node must assume the color of the
incoming laser and NOT the mixed color between A and B.

This means in State XX, it is possible for the node
having to deal with not one, but three laser colors:
One for the portion A-B, one for center to D, and
one for center to C. This unfortunately means that
a LOT of nodes must be registered, one for each
possible combination.

For example, with a red laser from A, and a green laser
from B, the laser colors of the node must be:

* A to B: yellow (mix from red and green)
* center to C: red
* center to D: green

However, even if there can be up to 3 laser colors in the same
node, the node only needs to "know" 2 colors, not 3: one for
each incoming laser. The 3rd laser laser color results from
color mixing and can be derived from the incoming lasers.


If the laser only comes from one side,
the lasers inside the node will always be of the same color,
so fewer registrations are needed here.
This is because both the deflected laser and the laser that
goes through are the same laser.


<<< WHAT DOES THIS ALL MEAN??? >>>

In short:

* If no lasers are incoming (state 00), nothing special to do.
* If one laser is incoming (state X0 or 0X), register nodes for every colorcode.
* If two lasers are incoming (state XX), register nodes for each possible combination of *two* colorcodes

]]

-- Definition templates for active beam splitters

-- State 0X (1 laser from one side)
local tm_def_on_0X = table.copy(tm_def)
tm_def_on_0X.light_source = lzr_globals.LASER_GLOW
if not tm_def_on_0X.groups then
	tm_def_on_0X.groups = {}
end
tm_def_on_0X.groups.not_in_creative_inventory = 1

-- Note: We only use the 'off' tiles for all beam splitter
-- nodes because we handle the on/off state
-- ourselves, rather than let register_element do it for us.
tm_def_on_0X.__tiles_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	{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^(lzr_laser_fixed.png^[mask:lzr_laser_transmissive_mirror_mirror_out_screw_mask.png)", backface_culling=true},
}
tm_def_on_0X.__tiles_rotatable_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	{name="lzr_laser_transmissive_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
	{name="lzr_laser_transmissive_mirror_mirror.png", backface_culling=true},
	{name="lzr_laser_transmissive_mirror_mirror_out.png^(lzr_laser_fixed_soft.png^[mask:lzr_laser_transmissive_mirror_mirror_out_screw_mask.png)", backface_culling=true},
}
tm_def_on_0X.__tiles_takable_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	{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_0X.__mesh_off = "lzr_laser_tmirror_on_thru1.obj"

-- State X0 (1 laser from other side)
local tm_def_on_X0 = table.copy(tm_def_on_0X)
tm_def_on_X0.__mesh_off = "lzr_laser_tmirror_on_thru2.obj"

-- State XX (laser from both sides)
local tm_def_on_XX = table.copy(tm_def_on_0X)
tm_def_on_XX.__tiles_off = {
	lzr_laser.LASER_TILE, -- << incoming lasers
	lzr_laser.LASER_TILE, -- << outgoing laser behind mirror
	lzr_laser.LASER_TILE, -- << outgoing laser behind mirror (other side)
	{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^(lzr_laser_fixed.png^[mask:lzr_laser_transmissive_mirror_mirror_out_screw_mask.png)", backface_culling=true},
}
tm_def_on_XX.__tiles_rotatable_off = {
	lzr_laser.LASER_TILE, -- << incoming lasers
	lzr_laser.LASER_TILE, -- << outgoing laser behind mirror
	lzr_laser.LASER_TILE, -- << outgoing laser behind mirror (other side)
	{name="lzr_laser_transmissive_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
	{name="lzr_laser_transmissive_mirror_mirror.png", backface_culling=true},
	{name="lzr_laser_transmissive_mirror_mirror_out.png^(lzr_laser_fixed_soft.png^[mask:lzr_laser_transmissive_mirror_mirror_out_screw_mask.png)", backface_culling=true},
}
tm_def_on_XX.__tiles_takable_off = {
	lzr_laser.LASER_TILE, -- << incoming lasers
	lzr_laser.LASER_TILE, -- << outgoing laser behind mirror
	lzr_laser.LASER_TILE, -- << outgoing laser behind mirror (other side)
	{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_XX.__mesh_off = "lzr_laser_tmirror_on_thru3.obj"

local tm_options_on = table.copy(tm_options_off)
tm_options_on.inactive = "lzr_laser:transmissive_mirror_00"

-- Iterate through all possible combinations of 2 colorcodes
-- and register the neccessary active beam splitters accordingly
for laser1=0, #colortiles do
for laser2=0, #colortiles do
	local laserid = tostring(laser1) .. tostring(laser2)
	local colstring = lzr_laser.dirstring_to_colstring(tostring(laserid))
	-- State 0X (X = 1 to max. colorcode)
	if laser1 == 0 and laser2 > 0 then
		local def = table.copy(tm_def_on_0X)
		def._lzr_transmissive_mirror_state = laserid
		--~ Beam splitter block description with active lasers inside. @1 = a color code uniquely identifying the laser colors
		def.description = S("Beam Splitter (active, @1)", colstring)
		-- Apply the laser color
		def.__tiles_off[1] = colortiles[laser2]
		def.__tiles_rotatable_off[1] = colortiles[laser2]
		def.__tiles_takable_off[1] = colortiles[laser2]

		lzr_laser.register_element("lzr_laser:transmissive_mirror_"..laserid, def, tm_options_on)
	-- State X0
	elseif laser1 > 0 and laser2 == 0 then
		local def = table.copy(tm_def_on_X0)
		def._lzr_transmissive_mirror_state = laserid
		def.description = S("Beam Splitter (active, @1)", colstring)
		-- Apply the laser color
		def.__tiles_off[1] = colortiles[laser1]
		def.__tiles_rotatable_off[1] = colortiles[laser1]
		def.__tiles_takable_off[1] = colortiles[laser1]

		lzr_laser.register_element("lzr_laser:transmissive_mirror_"..laserid, def, tm_options_on)
	-- State XX
	elseif laser1 > 0 and laser2 > 0 then
		local def = table.copy(tm_def_on_XX)
		def._lzr_transmissive_mirror_state = laserid
		def.description = S("Beam Splitter (active, @1)", colstring)

		-- Apply the laser color (need 3 textures, explained above)
		local laser_mixed = bit.bor(laser1, laser2)
		def.__tiles_off[1] = colortiles[laser_mixed]
		def.__tiles_rotatable_off[1] = colortiles[laser_mixed]
		def.__tiles_takable_off[1] = colortiles[laser_mixed]
		def.__tiles_off[2] = colortiles[laser1]
		def.__tiles_rotatable_off[2] = colortiles[laser1]
		def.__tiles_takable_off[2] = colortiles[laser1]
		def.__tiles_off[3] = colortiles[laser2]
		def.__tiles_rotatable_off[3] = colortiles[laser2]
		def.__tiles_takable_off[3] = colortiles[laser2]

		lzr_laser.register_element("lzr_laser:transmissive_mirror_"..laserid, def, tm_options_on)
	end
end
end


-- Double-sided mirror
local dm_def = {
	description = S("Double Mirror"),
	_tt_help = S("Deflects lasers on both sides"),
	paramtype = "light",
	paramtype2 = "facedir",
	drawtype = "mesh",
	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_glass_defaults(),
	__mesh_on = "lzr_laser_dmirror_on.obj",
	-- Same as 'on' mesh, we just hide the laser faces with blank textures
	__mesh_off = "lzr_laser_dmirror_on.obj",
}

-- Double mirror, inactive (also known as "State 00")
local dm_def_off = table.copy(dm_def)
dm_def_off._lzr_double_mirror_state = "00"
dm_def_off.__use_texture_alpha_off = "clip"
dm_def_off.__tiles_off = {
	"blank.png",
	"blank.png",
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_off.__tiles_rotatable_off = {
	"blank.png",
	"blank.png",
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_off.__tiles_takable_off = {
	"blank.png",
	"blank.png",
	{name="lzr_laser_double_mirror_block.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
local dm_options_off = { allow_take = true, allow_rotate = true, activate = false, group = "double_mirror", register_color = false }
lzr_laser.register_element("lzr_laser:double_mirror_00", dm_def_off, dm_options_off)


-- Double Mirror, active

-- Register active double mirrors
--[[
This registers a LOT of nodes, for each laser color AND
laser color combination. The double mirror needs to deal with
multiple cases in which a laser impacts the mirror from
one of two possible sides of the mirror.

There are 4 basic possibilities:

* No laser: State 00 (inactive state, already handled above)
* Laser hits front side of the mirror: State X0
* Laser hits back side of the mirror: State 0X
* Lasers hit both sides of the mirror: State XX

Where X = a colorcode of laser

It is a similar system as for beam splitters, but the
lasers themselves are much simpler. We only need up
to two laser colors here, one for each mirror side. ]]

-- Definition templates for active double mirrors

-- State 0X (1 laser on one side)
local dm_def_on_0X = table.copy(dm_def)
dm_def_on_0X.light_source = lzr_globals.LASER_GLOW
if not dm_def_on_0X.groups then
	dm_def_on_0X.groups = {}
end
dm_def_on_0X.groups.not_in_creative_inventory = 1
dm_def_on_0X.__use_texture_alpha_off = lzr_laser.ALPHA_LASER

-- Note: We only use the 'off' tiles for all double
-- mirror nodes because we handle the on/off state
-- ourselves, rather than let register_element do it for us.
dm_def_on_0X.__tiles_off = {
	"blank.png",
	lzr_laser.LASER_TILE, -- << will be replaced
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_on_0X.__tiles_rotatable_off = {
	"blank.png",
	lzr_laser.LASER_TILE, -- << will be replaced
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_on_0X.__tiles_takable_off = {
	"blank.png",
	lzr_laser.LASER_TILE, -- << will be replaced
	{name="lzr_laser_double_mirror_block.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}

-- State X0 (1 laser on other side)
local dm_def_on_X0 = table.copy(dm_def_on_0X)
dm_def_on_X0.__tiles_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	"blank.png",
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_on_X0.__tiles_rotatable_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	"blank.png",
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_on_X0.__tiles_takable_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	"blank.png",
	{name="lzr_laser_double_mirror_block.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}

-- State XX (lasers on both sides)
local dm_def_on_XX = table.copy(dm_def_on_0X)
dm_def_on_XX.__tiles_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	lzr_laser.LASER_TILE, -- << will be replaced
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_on_XX.__tiles_rotatable_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	lzr_laser.LASER_TILE, -- << will be replaced
	{name="lzr_laser_double_mirror_block.png^lzr_laser_fixed_soft.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}
dm_def_on_XX.__tiles_takable_off = {
	lzr_laser.LASER_TILE, -- << will be replaced
	lzr_laser.LASER_TILE, -- << will be replaced
	{name="lzr_laser_double_mirror_block.png", backface_culling=true},
	{name="lzr_laser_double_mirror_mirror.png", backface_culling=false},
}

dm_def_on_XX.__mesh_off = "lzr_laser_dmirror_on.obj"

local dm_options_on = table.copy(dm_options_off)
dm_options_on.inactive = "lzr_laser:double_mirror_00"

-- Iterate through all possible combinations of 2 colorcodes
-- and register the neccessary active double mirrors accordingly
for laser1=0, #colortiles do
for laser2=0, #colortiles do
	local laserid = tostring(laser1) .. tostring(laser2)
	local colstring = lzr_laser.dirstring_to_colstring(tostring(laserid))

	-- State 0X (X = 1 to max. colorcode)
	if laser1 == 0 and laser2 > 0 then
		local def = table.copy(dm_def_on_0X)
		def._lzr_double_mirror_state = laserid
		--~ Double mirror block description with active lasers inside. @1 = a color code uniquely identifying the laser colors
		def.description = S("Double Mirror (active, @1)", colstring)
		-- Apply the laser color
		def.__tiles_off[2] = colortiles[laser2]
		def.__tiles_rotatable_off[2] = colortiles[laser2]
		def.__tiles_takable_off[2] = colortiles[laser2]

		lzr_laser.register_element("lzr_laser:double_mirror_"..laserid, def, dm_options_on)
	-- State X0
	elseif laser1 > 0 and laser2 == 0 then
		local def = table.copy(dm_def_on_X0)
		def._lzr_double_mirror_state = laserid
		def.description = S("Double Mirror (active, @1)", colstring)
		-- Apply the laser color
		def.__tiles_off[1] = colortiles[laser1]
		def.__tiles_rotatable_off[1] = colortiles[laser1]
		def.__tiles_takable_off[1] = colortiles[laser1]

		lzr_laser.register_element("lzr_laser:double_mirror_"..laserid, def, dm_options_on)
	-- State XX
	elseif laser1 > 0 and laser2 > 0 then
		local def = table.copy(dm_def_on_XX)
		def._lzr_double_mirror_state = laserid
		def.description = S("Double Mirror (active, @1)", colstring)

		-- Apply the laser color (need 2 textures)
		def.__tiles_off[1] = colortiles[laser1]
		def.__tiles_rotatable_off[1] = colortiles[laser1]
		def.__tiles_takable_off[1] = colortiles[laser1]
		def.__tiles_off[2] = colortiles[laser2]
		def.__tiles_rotatable_off[2] = colortiles[laser2]
		def.__tiles_takable_off[2] = colortiles[laser2]

		lzr_laser.register_element("lzr_laser:double_mirror_"..laserid, def, dm_options_on)
	end
end
end



lzr_laser.register_element("lzr_laser:crystal", {
	description = S("Crystal"),
	_tt_help = S("Deflects lasers in all directions"),
	paramtype = "light",
	drawtype = "mesh",
	__mesh_off = "lzr_laser_crystal.obj",
	__mesh_on = "lzr_laser_crystal_on.obj",
	__tiles_takable_off = {
		-- Note: The crystal PNG files should have opacity > 50%
		-- so they're still visible in opaque laser mode.
		"lzr_laser_crystal.png",
	},
	__tiles_takable_on = {
		"lzr_laser_crystal_on.png",
		lzr_laser.LASER_TILE,
	},
	__tiles_off = {
		"lzr_laser_crystal.png^lzr_laser_crystal_fixed.png",
	},
	__tiles_on = {
		"lzr_laser_crystal_on.png^lzr_laser_crystal_fixed.png",
		lzr_laser.LASER_TILE,
	},
	collision_box = {
		type = "fixed",
		fixed = {
			{ -4/16, 4/16, -4/16, 4/16, 0.5, 4/16 },
			{ -0.5, -4/16, -4/16, 0.5, 4/16, 4/16 },
			{ -4/16, -4/16, -0.5, 4/16, 4/16, 0.5 },
			{ -4/16, -0.5, -4/16, 4/16, -4/16, 4/16 },
		},
	},
	selection_box = {
		type = "fixed",
		fixed = {
			{ -4/16, 4/16, -4/16, 4/16, 0.5, 4/16 },
			{ -0.5, -4/16, -4/16, 0.5, 4/16, 4/16 },
			{ -4/16, -4/16, -0.5, 4/16, 4/16, 0.5 },
			{ -4/16, -0.5, -4/16, 4/16, -4/16, 4/16 },
		},
	},
	__light_source_on = lzr_globals.LASER_GLOW,
	-- Crystals become opaque in opaque lasers mode as well
	__use_texture_alpha_on = lzr_laser.ALPHA_LASER,
	__use_texture_alpha_off = lzr_laser.ALPHA_LASER,
	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_glass_defaults(),
}, { allow_take = true, allow_rotate = false, group = "crystal" })

lzr_laser.register_element("lzr_laser:pillar_crystal", {
	description = S("Pillar Crystal"),
	_tt_help = S("Deflects lasers in three directions"),
	paramtype = "light",
	drawtype = "mesh",
	paramtype2 = "facedir",
	__mesh_off = "lzr_laser_pillar_crystal.obj",
	__mesh_on = "lzr_laser_pillar_crystal_on.obj",
	__tiles_takable_off = {
		{ name = "lzr_laser_pillar_crystal_frame.png", backface_culling = true },
		-- Note: The crystal PNG files should have opacity > 50%
		-- so they're still visible in opaque laser mode.
		"lzr_laser_crystal.png",
	},
	__tiles_takable_on = {
		{ name = "lzr_laser_pillar_crystal_frame.png", backface_culling = true },
		"lzr_laser_crystal_on.png",
		lzr_laser.LASER_TILE,
	},
	__tiles_off = {
		{ name = "lzr_laser_pillar_crystal_frame.png^lzr_laser_fixed.png", backface_culling = true },
		"lzr_laser_crystal.png",
	},
	__tiles_on = {
		{ name = "lzr_laser_pillar_crystal_frame.png^lzr_laser_fixed.png", backface_culling = true },
		"lzr_laser_crystal_on.png",
		lzr_laser.LASER_TILE,
	},
	__tiles_rotatable_off = {
		{ name = "lzr_laser_pillar_crystal_frame.png^lzr_laser_fixed_soft.png", backface_culling = true },
		"lzr_laser_crystal.png",
	},
	__tiles_rotatable_on = {
		{ name = "lzr_laser_pillar_crystal_frame.png^lzr_laser_fixed_soft.png", backface_culling = true },
		"lzr_laser_crystal_on.png",
		lzr_laser.LASER_TILE,
	},
	__light_source_on = lzr_globals.LASER_GLOW,
	-- Crystals become opaque in opaque lasers mode as well
	__use_texture_alpha_on = lzr_laser.ALPHA_LASER,
	__use_texture_alpha_off = lzr_laser.ALPHA_LASER,
	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_glass_defaults(),
}, { allow_take = true, allow_rotate = true, group = "pillar_crystal" })

lzr_laser.register_element("lzr_laser:triangle_crystal", {
	description = S("Triangle Crystal"),
	_tt_help = S("Deflects a laser in two directions"),
	paramtype = "light",
	drawtype = "mesh",
	paramtype2 = "facedir",
	__mesh_off = "lzr_laser_triangle_crystal.obj",
	__mesh_on = "lzr_laser_triangle_crystal_on.obj",
	__tiles_takable_off = {
		{ name = "lzr_laser_triangle_crystal_top.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_neutral.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_inside.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_backside.png", backface_culling = true },
		-- Note: The crystal PNG files should have opacity > 50%
		-- so they're still visible in opaque laser mode.
		-- Backface culling activated to prevent z-fighting
		{ name = "lzr_laser_crystal.png", backface_culling = true },
	},
	__tiles_takable_on = {
		{ name = "lzr_laser_triangle_crystal_top.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_neutral.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_inside.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_backside.png", backface_culling = true },
		{ name = "lzr_laser_crystal_on.png", backface_culling = true },
		lzr_laser.LASER_TILE,
	},
	__tiles_off = {
		{ name = "lzr_laser_triangle_crystal_top.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_neutral.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_inside.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_backside.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_crystal.png", backface_culling = true },
	},
	__tiles_on = {
		{ name = "lzr_laser_triangle_crystal_top.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_neutral.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_inside.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_backside.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_crystal_on.png", backface_culling = true },
		lzr_laser.LASER_TILE,
	},
	__tiles_rotatable_off = {
		{ name = "lzr_laser_triangle_crystal_top.png^lzr_laser_fixed_soft.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_neutral.png^lzr_laser_fixed_soft.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_inside.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_backside.png^lzr_laser_fixed_soft.png", backface_culling = true },
		{ name = "lzr_laser_crystal.png", backface_culling = true },
	},
	__tiles_rotatable_on = {
		{ name = "lzr_laser_triangle_crystal_top.png^lzr_laser_fixed_soft.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_neutral.png^lzr_laser_fixed_soft.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_inside.png", backface_culling = true },
		{ name = "lzr_laser_triangle_crystal_backside.png^lzr_laser_fixed_soft.png", backface_culling = true },
		{ name = "lzr_laser_crystal_on.png", backface_culling = true },
		lzr_laser.LASER_TILE,
	},
	__light_source_on = lzr_globals.LASER_GLOW,
	-- Crystals become opaque in opaque lasers mode as well
	__use_texture_alpha_on = lzr_laser.ALPHA_LASER,
	__use_texture_alpha_off = lzr_laser.ALPHA_LASER,
	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_glass_defaults(),
}, { allow_take = true, allow_rotate = true, group = "triangle_crystal" })

local MIXER_ANIM_LEN = 0.8

local get_mixer_node_color = function(hexcode)
	local r, g, b = lzr_util.hexcode_to_rgb(hexcode)
	local h, s, v = lzr_util.rgb_to_hsv(r, g, b)
	v = math.min(1, v * 1.5)
	v = math.max(0.2, v)
	r, g, b = lzr_util.hsv_to_rgb(h, s, v)
	hexcode = lzr_util.rgb_to_hexcode(r, g, b)
	return hexcode
end

local build_mixer_color_mask = function(basename, mask_prefix, with_white)
	local colors = {}
	for c=1, lzr_globals.MAX_COLORCODE do
		if c ~= lzr_globals.COLOR_WHITE or with_white then
			local hexcolor = lzr_laser.LASER_COLORS[c]
			table.insert(colors, {
				hexcolor = get_mixer_node_color(hexcolor),
				name = lzr_globals.COLOR_NAMES[c],
			})
		end
	end
	local maskstr = "("
	for c=1, #colors do
		maskstr = maskstr .. "("..basename.."^[multiply:#"..colors[c].hexcolor.."^[mask:"..mask_prefix..colors[c].name..".png)"
		if c < #colors then
			maskstr = maskstr .. "^"
		end
	end
	maskstr = maskstr .. ")"
	return maskstr
end

build_mixer_color_mask("lzr_laser_mixer_out.png", "lzr_laser_mixer_out_mask_")

lzr_laser.register_element("lzr_laser:mixer", {
	--~ A block that mixes laser colors
	description = S("Mixer"),
	_tt_help = S("Two lasers go in, a laser with their colors combined goes out"),
	paramtype = "light",
	paramtype2 = "facedir",
	__tiles_takable_off = {
		"lzr_laser_mixer_side.png^"..build_mixer_color_mask("lzr_laser_mixer_side.png", "lzr_laser_mixer_side_mask_", true),
		"lzr_laser_mixer_side_r.png^"..build_mixer_color_mask("lzr_laser_mixer_side.png", "lzr_laser_mixer_side_mask_", true),
		"lzr_laser_mixer_in.png^("..build_mixer_color_mask("lzr_laser_mixer_in.png", "lzr_laser_mixer_in_mask_", true).."^[hsl:0:-50:0)",
		"(lzr_laser_mixer_in.png^("..build_mixer_color_mask("lzr_laser_mixer_in.png", "lzr_laser_mixer_in_mask_", true).."^[hsl:0:-50:0))^[transformR180",
		"lzr_laser_mixer_back.png",
		"lzr_laser_mixer_out.png^("..build_mixer_color_mask("lzr_laser_mixer_out.png", "lzr_laser_mixer_out_mask_", false).."^[hsl:0:-50:0)",
	},
	__tiles_takable_on = {
		{ name = "lzr_laser_mixer_on_side_anim.png^"..build_mixer_color_mask("lzr_laser_mixer_on_side_anim.png", "lzr_laser_mixer_on_side_anim_mask_", true), animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = MIXER_ANIM_LEN } },
		{ name = "lzr_laser_mixer_on_side_r_anim.png^"..build_mixer_color_mask("lzr_laser_mixer_on_side_anim.png", "lzr_laser_mixer_on_side_anim_mask_", true), animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = MIXER_ANIM_LEN } },
		"lzr_laser_mixer_on_in.png^"..build_mixer_color_mask("lzr_laser_mixer_on_in.png", "lzr_laser_mixer_in_mask_", true),
		"(lzr_laser_mixer_on_in.png^"..build_mixer_color_mask("lzr_laser_mixer_on_in.png", "lzr_laser_mixer_in_mask_", true)..")^[transformR180",
		"lzr_laser_mixer_on_back.png",
		"lzr_laser_mixer_on_out.png^"..build_mixer_color_mask("lzr_laser_mixer_on_out.png", "lzr_laser_mixer_out_mask_", false),
	},
	__tiles_rotatable_off = {
		"lzr_laser_mixer_side.png^"..build_mixer_color_mask("lzr_laser_mixer_side.png", "lzr_laser_mixer_side_mask_", true).."^lzr_laser_fixed_soft.png",
		"lzr_laser_mixer_side_r.png^"..build_mixer_color_mask("lzr_laser_mixer_side.png", "lzr_laser_mixer_side_mask_", true).."^lzr_laser_fixed_soft.png",
		"lzr_laser_mixer_in.png^("..build_mixer_color_mask("lzr_laser_mixer_in.png", "lzr_laser_mixer_in_mask_", true).."^[hsl:0:-50:0)^lzr_laser_fixed_soft.png",
		"(lzr_laser_mixer_in.png^("..build_mixer_color_mask("lzr_laser_mixer_in.png", "lzr_laser_mixer_in_mask_", true).."^[hsl:0:-50:0))^[transformR180^lzr_laser_fixed_soft.png",
		"lzr_laser_mixer_back.png^lzr_laser_fixed_soft.png",
		"lzr_laser_mixer_out.png^("..build_mixer_color_mask("lzr_laser_mixer_out.png", "lzr_laser_mixer_out_mask_", false).."^[hsl:0:-50:0)^lzr_laser_fixed_soft.png",
	},
	__tiles_rotatable_on = {
		{ name = "lzr_laser_mixer_on_side_anim.png^lzr_laser_mixer_fixed_soft.png^"..build_mixer_color_mask("lzr_laser_mixer_on_side_anim.png", "lzr_laser_mixer_on_side_anim_mask_", true), animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = MIXER_ANIM_LEN } },
		{ name = "lzr_laser_mixer_on_side_r_anim.png^lzr_laser_mixer_fixed_soft.png^"..build_mixer_color_mask("lzr_laser_mixer_on_side_anim.png", "lzr_laser_mixer_on_side_anim_mask_", true), animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = MIXER_ANIM_LEN } },
		"lzr_laser_mixer_on_in.png^"..build_mixer_color_mask("lzr_laser_mixer_on_in.png", "lzr_laser_mixer_in_mask_", true).."^lzr_laser_fixed_soft.png",
		"(lzr_laser_mixer_on_in.png^"..build_mixer_color_mask("lzr_laser_mixer_on_in.png", "lzr_laser_mixer_in_mask_", true)..")^[transformR180^lzr_laser_fixed_soft.png",
		"lzr_laser_mixer_on_back.png^lzr_laser_fixed_soft.png",
		"lzr_laser_mixer_on_out.png^"..build_mixer_color_mask("lzr_laser_mixer_on_out.png", "lzr_laser_mixer_out_mask_", false).."^lzr_laser_fixed_soft.png",
	},
	__tiles_off = {
		"lzr_laser_mixer_side.png^"..build_mixer_color_mask("lzr_laser_mixer_side.png", "lzr_laser_mixer_side_mask_", true).."^lzr_laser_fixed.png",
		"lzr_laser_mixer_side_r.png^"..build_mixer_color_mask("lzr_laser_mixer_side.png", "lzr_laser_mixer_side_mask_", true).."^lzr_laser_fixed.png",
		"lzr_laser_mixer_in.png^("..build_mixer_color_mask("lzr_laser_mixer_in.png", "lzr_laser_mixer_in_mask_", true).."^[hsl:0:-50:0)^lzr_laser_fixed.png",
		"(lzr_laser_mixer_in.png^("..build_mixer_color_mask("lzr_laser_mixer_in.png", "lzr_laser_mixer_in_mask_", true).."^[hsl:0:-50:0))^[transformR180^lzr_laser_fixed.png",
		"lzr_laser_mixer_back.png^lzr_laser_fixed.png",
		"lzr_laser_mixer_out.png^("..build_mixer_color_mask("lzr_laser_mixer_out.png", "lzr_laser_mixer_out_mask_", false).."^[hsl:0:-50:0)^lzr_laser_fixed.png",
	},
	__tiles_on = {
		{ name = "lzr_laser_mixer_on_side_anim.png^lzr_laser_mixer_fixed.png^"..build_mixer_color_mask("lzr_laser_mixer_on_side_anim.png", "lzr_laser_mixer_on_side_anim_mask_", true), animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = MIXER_ANIM_LEN } },
		{ name = "lzr_laser_mixer_on_side_r_anim.png^lzr_laser_mixer_fixed.png^"..build_mixer_color_mask("lzr_laser_mixer_on_side_anim.png", "lzr_laser_mixer_on_side_anim_mask_", true), animation = { type = "vertical_frames", aspect_w = 16, aspect_h = 16, length = MIXER_ANIM_LEN } },
		"lzr_laser_mixer_on_in.png^"..build_mixer_color_mask("lzr_laser_mixer_on_in.png", "lzr_laser_mixer_in_mask_", true).."^lzr_laser_fixed.png",
		"(lzr_laser_mixer_on_in.png^"..build_mixer_color_mask("lzr_laser_mixer_on_in.png", "lzr_laser_mixer_in_mask_", true)..")^[transformR180^lzr_laser_fixed.png",
		"lzr_laser_mixer_on_back.png^lzr_laser_fixed.png",
		"lzr_laser_mixer_on_out.png^"..build_mixer_color_mask("lzr_laser_mixer_on_out.png", "lzr_laser_mixer_out_mask_", false).."^lzr_laser_fixed.png",
	},
	__light_source_on = lzr_globals.LASER_GLOW,
	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_wood_defaults(),
}, { allow_take = true, allow_rotate = true, group = "mixer" })

-- List of emitters
local emitters = {
	-- list order: colorname, suffix for item ID, suffix for texture, description, extended tootip, colorcode, colorblind pattern
	{ "red", "_red", "", S("Red Emitter"), S("Emits a red laser"), S("Would emit a red laser if it were turned on"), lzr_globals.COLOR_RED, "lzr_laser_pattern_emitter_solid.png" },
	{ "green", "_green", "_green", S("Green Emitter"), S("Emits a green laser"), S("Would emit a green laser if it were turned on"), lzr_globals.COLOR_GREEN, "lzr_laser_pattern_emitter_lines.png" },
	{ "blue", "_blue", "_blue", S("Blue Emitter"), S("Emits a blue laser"), S("Would emit a blue laser if it were turned on"),  lzr_globals.COLOR_BLUE, "lzr_laser_pattern_emitter_dots.png" },
	{ "yellow", "_yellow", "_yellow", S("Yellow Emitter"), S("Emits a yellow laser"), S("Would emit a yellow laser if it were turned on"),  lzr_globals.COLOR_YELLOW, "lzr_laser_pattern_emitter_checkers.png" },
	{ "magenta", "_magenta", "_magenta", S("Magenta Emitter"), S("Emits a magenta laser"), S("Would emit a magenta laser if it were turned on"),  lzr_globals.COLOR_MAGENTA, "lzr_laser_pattern_emitter_alternating.png" },
	{ "cyan", "_cyan", "_cyan", S("Cyan Emitter"), S("Emits a cyan laser"), S("Would emit a cyan laser if it were turned on"),  lzr_globals.COLOR_CYAN, "lzr_laser_pattern_emitter_long_checkers.png" },
	{ "white", "_white", "_white", S("White Emitter"), S("Emits a white laser"), S("Would emit a white laser if it were turned on"), lzr_globals.COLOR_WHITE, "lzr_laser_pattern_emitter_holes.png" },
}

local emitter_color_order = {}
for c=1, lzr_globals.MAX_COLORCODE do
	for e=1, #emitters do
		local e_colorcode = emitters[e][7]
		if e_colorcode == c then
			table.insert(emitter_color_order, e)
		end
	end
end

local get_emitter_node_color = function(hexcode)
	local r, g, b = lzr_util.hexcode_to_rgb(hexcode)
	local h, s, v = lzr_util.rgb_to_hsv(r, g, b)
	v = math.min(1, v * 1.4)
	v = math.max(0.2, v)
	s = math.max(0, s * 0.75)
	r, g, b = lzr_util.hsv_to_rgb(h, s, v)
	hexcode = lzr_util.rgb_to_hexcode(r, g, b)
	return hexcode
end

for e=1, #emitters do
	local suffix = emitters[e][2]
	local suffix_tex = emitters[e][3]
	local desc = emitters[e][4]
	local tt_help_on = emitters[e][5]
	local tt_help_off = emitters[e][6]
	local colorcode = emitters[e][7]

	local nextcolor, prevcolor, name_nextcolor, name_prevcolor
	nextcolor = colorcode + 1
	if nextcolor > lzr_globals.MAX_COLORCODE then
		nextcolor = 1
	end
	name_nextcolor = "lzr_laser:emitter"..emitters[emitter_color_order[nextcolor]][2]

	prevcolor = colorcode - 1
	if prevcolor < 1 then
		prevcolor = lzr_globals.MAX_COLORCODE
	end
	name_prevcolor = "lzr_laser:emitter"..emitters[emitter_color_order[prevcolor]][2]

	local hexcode = lzr_laser.LASER_COLORS[colorcode]
	local emitter_color = get_emitter_node_color(hexcode)

	-- optional colorblind pattern
	local pattern_on, pattern_off = "", ""
	if use_patterns then
		pattern_on = "^("..emitters[e][8].."^[opacity:100)"
		pattern_off = "^("..emitters[e][8].."^[opacity:120)"
	end

	lzr_laser.register_element("lzr_laser:emitter"..suffix, {
		description = desc,
		__tt_help_off = tt_help_off,
		__tt_help_on = tt_help_on,
		paramtype2 = "facedir",
		__tiles_takable_off = {
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_emitter_front_hole.png",
		},
		__tiles_takable_on = {
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^((lzr_laser_emitter_on_front_hole.png^[multiply:#"..hexcode..")^[mask:lzr_laser_emitter_on_front_hole_mask.png)",
		},
		__tiles_rotatable_off = {
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_emitter_front_hole.png^lzr_laser_fixed_soft.png",
		},
		__tiles_rotatable_on = {
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed_soft.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^((lzr_laser_emitter_on_front_hole.png^[multiply:#"..hexcode..")^[mask:lzr_laser_emitter_on_front_hole_mask.png)^lzr_laser_fixed_soft.png",
		},
		__tiles_off = {
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_off.png"..pattern_off..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_emitter_front_hole.png^lzr_laser_fixed.png",
		},
		__tiles_on = {
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^lzr_laser_fixed.png",
			"((lzr_laser_emitter_color_template_on.png"..pattern_on..")^[multiply:#"..emitter_color..")^lzr_laser_emitter_frame.png^((lzr_laser_emitter_on_front_hole.png^[multiply:#"..hexcode..")^[mask:lzr_laser_emitter_on_front_hole_mask.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 is_on = minetest.get_item_group(node.name, "emitter") == 2
			local def = minetest.registered_nodes[node.name]
			local newname
			if is_on then
				minetest.sound_play({name="lzr_laser_emitter_activate", gain=0.8}, {pos=pos, pitch=0.7}, true)
				newname = def._lzr_inactive
			else
				minetest.sound_play({name="lzr_laser_emitter_activate", gain=1.0}, {pos=pos}, true)
				newname = def._lzr_active
			end
			minetest.swap_node(pos, {name=newname, param2=node.param2})
			lzr_laser.full_laser_update_if_needed()
		end,
		_lzr_on_toggle_item = function(holding_player, item)
			if lzr_gamestate.get_state() == lzr_gamestate.LEVEL_COMPLETE then
				return
			end
			local is_on = minetest.get_item_group(item:get_name(), "emitter") == 2
			local def = minetest.registered_items[item:get_name()]
			local pos = holding_player:get_pos()
			local newname
			if is_on then
				minetest.sound_play({name="lzr_laser_emitter_activate", gain=0.8}, {pos=pos, pitch=0.7}, true)
				newname = def._lzr_inactive
			else
				minetest.sound_play({name="lzr_laser_emitter_activate", gain=1.0}, {pos=pos}, true)
				newname = def._lzr_active
			end
			item:set_name(newname)
			return item
		end,
		__lzr_next_color = name_nextcolor,
		__lzr_prev_color = name_prevcolor,
		groups = { laser_block = 1, emitter_color = colorcode, receiver = 1 },
		sounds = lzr_sounds.node_sound_wood_defaults(),
	}, { allow_take = true, allow_rotate = true, keep_state_on_take = true, active_in_creative = true, register_color = false, group = "emitter", explicit_inactive_description = true })
end

minetest.register_alias("lzr_laser:emitter", "lzr_laser:emitter_red")
minetest.register_alias("lzr_laser:emitter_takable", "lzr_laser:emitter_red_takable")
minetest.register_alias("lzr_laser:emitter_on", "lzr_laser:emitter_red_on")
minetest.register_alias("lzr_laser:emitter_takable_on", "lzr_laser:emitter_red_takable_on")

-- List of detectors
local detectors = {
	-- list order: color name, suffix for item ID, suffix for texture, description, colorcode, colorblind pattern

	-- Colorless detector that accepts any color
	{ "", "", "", S("Detector"), S("Activates when a laser goes into the hole"), nil, nil },

	-- Colored detectors that accept only one color
	{ "red", "_red", "_red", S("Red Detector"), S("Activates when a red laser goes into the hole"), lzr_globals.COLOR_RED, "lzr_laser_pattern_detector_solid.png" },
	{ "green", "_green", "_green", S("Green Detector"), S("Activates when a green laser goes into the hole"), lzr_globals.COLOR_GREEN, "lzr_laser_pattern_detector_lines.png" },
	{ "blue", "_blue", "_blue", S("Blue Detector"), S("Activates when a blue laser goes into the hole"), lzr_globals.COLOR_BLUE, "lzr_laser_pattern_detector_dots.png"  },
	{ "yellow", "_yellow", "_yellow", S("Yellow Detector"), S("Activates when a yellow laser goes into the hole"), lzr_globals.COLOR_YELLOW, "lzr_laser_pattern_detector_checkers.png"  },
	{ "magenta", "_magenta", "_magenta", S("Magenta Detector"), S("Activates when a magenta laser goes into the hole"), lzr_globals.COLOR_MAGENTA, "lzr_laser_pattern_detector_alternating.png"  },
	{ "cyan", "_cyan", "_cyan", S("Cyan Detector"), S("Activates when a cyan laser goes into the hole"), lzr_globals.COLOR_CYAN, "lzr_laser_pattern_detector_long_checkers.png"  },
	{ "white", "_white", "_white", S("White Detector"), S("Activates when a white laser goes into the hole"), lzr_globals.COLOR_WHITE, "lzr_laser_pattern_detector_holes.png"  },
}
local detector_color_order = {}
for c=1, lzr_globals.MAX_COLORCODE do
	for d=1, #detectors do
		local d_colorcode = detectors[d][6]
		if d_colorcode == c then
			table.insert(detector_color_order, d)
		end
	end
end

local detector_color_order = {}
for c=1, lzr_globals.MAX_COLORCODE do
	for d=1, #detectors do
		local d_colorcode = detectors[d][6]
		if d_colorcode == c then
			table.insert(detector_color_order, d)
		end
	end
end

-- Takes a color hexcode and returns one that is suitable
-- for detectors
local get_detector_node_color = function(hexcode)
	-- This function only does one thing,
	-- and it cap the HSV value to be 20% at minimum.
	-- This is so that black (or very dark) detectors
	-- still have some contrast.
	local r, g, b = lzr_util.hexcode_to_rgb(hexcode)
	local h, s, v = lzr_util.rgb_to_hsv(r, g, b)
	v = math.max(0.2, v)
	r, g, b = lzr_util.hsv_to_rgb(h, s, v)
	hexcode = lzr_util.rgb_to_hexcode(r, g, b)
	return hexcode
end
for d=1, #detectors do
	local suffix = detectors[d][2]
	local suffix_tex = detectors[d][3]
	local desc = detectors[d][4]
	local tt_help = detectors[d][5]
	local colorcode = detectors[d][6]

	local nextcolor, prevcolor, name_nextcolor, name_prevcolor

	-- Define __lzr_nextcolor and __lzr_prevcolor for the color changer tool
	if colorcode ~= nil then
		nextcolor = colorcode + 1
		if nextcolor > lzr_globals.MAX_COLORCODE then
			nextcolor = 0
		end
		if nextcolor == 0 then
			-- We add the uncolored detector as well for convenience
			name_nextcolor = "lzr_laser:detector"
		else
			name_nextcolor = "lzr_laser:detector"..detectors[detector_color_order[nextcolor]][2]
		end

		prevcolor = colorcode - 1
		if prevcolor < 1 then
			name_prevcolor = "lzr_laser:detector"
		else
			name_prevcolor = "lzr_laser:detector"..detectors[detector_color_order[prevcolor]][2]
		end
	else
		name_nextcolor = "lzr_laser:detector"..detectors[detector_color_order[1]][2]
		name_prevcolor = "lzr_laser:detector"..detectors[detector_color_order[lzr_globals.MAX_COLORCODE]][2]
	end

	local hexcode
	if colorcode then
		hexcode = lzr_laser.LASER_COLORS[colorcode]
		hexcode = get_detector_node_color(hexcode)
	end

	local __tiles_off, __tiles_on, __tiles_takable_off, __tiles_takable_on
	if colorcode == nil then
		-- Uncolored detector has simple, fixed textures
		__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_rotatable_off = {
			"lzr_laser_detector.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_front.png^lzr_laser_fixed_soft.png",
		}
		__tiles_rotatable_on = {
			"lzr_laser_detector_on.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on.png^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on_front.png^lzr_laser_fixed_soft.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",
		}
	else
		local pattern = ""
		if use_patterns then
			-- Pattern overlay if using patterned lasers (colorblind support)
			pattern = "^"..detectors[d][7]
		end

		-- Colored detectors have dynamic texture, using the laser color
		__tiles_off = {
			"(lzr_laser_detector_colored.png"..pattern..")^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed.png",
			"(lzr_laser_detector_colored.png"..pattern..")^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed.png",
			"(lzr_laser_detector_colored.png"..pattern..")^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed.png",
			"(lzr_laser_detector_colored.png"..pattern..")^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed.png",
			"(lzr_laser_detector_colored.png"..pattern..")^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed.png",
			"(lzr_laser_detector_colored_front.png"..pattern..")^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed.png",
		}
		__tiles_on = {
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed.png",
			"lzr_laser_detector_on_colored_front.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^(lzr_laser_detector_on_colored_hole.png^[multiply:#"..hexcode.."^[mask:lzr_laser_detector_on_colored_hole.png)^lzr_laser_fixed.png",
		}
		__tiles_rotatable_off = {
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_colored_front.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)^lzr_laser_fixed_soft.png",
		}
		__tiles_rotatable_on = {
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^lzr_laser_fixed_soft.png",
			"lzr_laser_detector_on_colored_front.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^(lzr_laser_detector_on_colored_hole.png^[multiply:#"..hexcode.."^[mask:lzr_laser_detector_on_colored_hole.png)^lzr_laser_fixed_soft.png",
		}
		__tiles_takable_off = {
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)",
			"lzr_laser_detector_colored.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)",
			"lzr_laser_detector_colored_front.png"..pattern.."^((lzr_laser_detector_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_colored_mask.png)",
		}
		__tiles_takable_on = {
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)",
			"lzr_laser_detector_on_colored.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)",
			"lzr_laser_detector_on_colored_front.png"..pattern.."^((lzr_laser_detector_on_colored.png^[multiply:#"..hexcode..")^[mask:lzr_laser_detector_on_colored_mask.png)^(lzr_laser_detector_on_colored_hole.png^[multiply:#"..hexcode.."^[mask:lzr_laser_detector_on_colored_hole.png)",
		}
	end

	lzr_laser.register_element("lzr_laser:detector"..suffix, {
		description = desc,
		__tt_help_off = tt_help,
		paramtype2 = "facedir",
		__tiles_off = __tiles_off,
		__tiles_on = __tiles_on,
		__tiles_rotatable_off = __tiles_rotatable_off,
		__tiles_rotatable_on = __tiles_rotatable_on,
		__tiles_takable_off = __tiles_takable_off,
		__tiles_takable_on = __tiles_takable_on,
		__light_source_on = 5,
		groups = { laser_block = 1, detector_color = colorcode, sender = 1 },
		sounds = lzr_sounds.node_sound_wood_defaults(),
		__after_dig_node = function(pos, oldnode, oldmetadata, digger)
			-- Play 'deactivate' sound if digging an active detector,
			-- because the player can only get it in inactive state.
			if minetest.get_item_group(oldnode.name, "detector") == 2 then
				minetest.sound_play({name="lzr_laser_detector_deactivate", gain=0.7}, {pos=pos}, true)
			end
		end,
		__lzr_next_color = name_nextcolor,
		__lzr_prev_color = name_prevcolor,
	}, { allow_take = true, allow_rotate = true, is_detector = true, register_color = false, group = "detector" })
end

-- Hollow barrel
lzr_laser.register_element("lzr_laser:hollow_barrel", {
	description = S("Hollow Barrel"),
	paramtype = "light",
	paramtype2 = "facedir",
	drawtype = "mesh",
	__mesh_off = "lzr_laser_hollow_barrel.obj",
	__mesh_on = "lzr_laser_hollow_barrel_on.obj",
	__tiles_off = {
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed.png",
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed.png",
		"lzr_laser_hollow_barrel_sides.png^lzr_laser_fixed.png",
		"lzr_laser_hollow_barrel_insides.png",
	},
	__tiles_on = {
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed.png",
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed.png",
		"lzr_laser_hollow_barrel_sides.png^lzr_laser_fixed.png",
		"lzr_laser_hollow_barrel_insides.png",
		lzr_laser.LASER_TILE,
	},
	__tiles_rotatable_off = {
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed_soft.png",
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed_soft.png",
		"lzr_laser_hollow_barrel_sides.png^lzr_laser_fixed_soft.png",
		"lzr_laser_hollow_barrel_insides.png",
	},
	__tiles_rotatable_on = {
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed_soft.png",
		"lzr_laser_hollow_barrel_top.png^lzr_laser_fixed_soft.png",
		"lzr_laser_hollow_barrel_sides.png^lzr_laser_fixed_soft.png",
		"lzr_laser_hollow_barrel_insides.png",
		lzr_laser.LASER_TILE,
	},
	__tiles_takable_off = {
		"lzr_laser_hollow_barrel_top.png",
		"lzr_laser_hollow_barrel_top.png",
		"lzr_laser_hollow_barrel_sides.png",
		"lzr_laser_hollow_barrel_insides.png",
	},
	__tiles_takable_on = {
		"lzr_laser_hollow_barrel_top.png",
		"lzr_laser_hollow_barrel_top.png",
		"lzr_laser_hollow_barrel_sides.png",
		"lzr_laser_hollow_barrel_insides.png",
		lzr_laser.LASER_TILE,
	},
	__use_texture_alpha_off = "clip",
	__use_texture_alpha_on = lzr_laser.ALPHA_LASER,
	__light_source_on = lzr_globals.LASER_GLOW,
	groups = { laser_block = 1 },
	sounds = lzr_sounds.node_sound_wood_defaults(),
}, { allow_take = true, allow_rotate = true, group = "hollow_barrel" })


-- Is non-walkable if off,
-- and walkable if on.
lzr_laser.register_element("lzr_laser:skull_cursed", {
	description = S("Cursed Skull"),
	__tt_help_off = S("Is untouchable unless a laser goes through it"),
	__tt_help_on = S("Becomes untouchable when no laser goes through it"),
	drawtype = "mesh",
	-- Mesh based on skull nodebox by rudzik8
	__mesh_off = "lzr_laser_skull.obj",
	__mesh_on = "lzr_laser_skull.obj",
	collision_box = {type="regular"},
	selection_box = {type="regular"},
	paramtype = "light",
	paramtype2 = "4dir",
	__tiles_off = {
		{ name = "(lzr_laser_cskull_top.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_cskull_bottom.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_cskull_side.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_cskull_side.png^lzr_laser_fixed.png^[transformFX)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_cskull_back.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_cskull_front.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
	},
	__tiles_on = {
		{ name = "lzr_laser_cskull_top.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_cskull_bottom.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_cskull_side.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_cskull_side.png^lzr_laser_fixed.png^[transformFX", backface_culling = true },
		{ name = "lzr_laser_cskull_back.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_cskull_front.png^lzr_laser_fixed.png^lzr_laser_cskull_on.png", backface_culling = true },
	},
	__tiles_takable_off = {
		{ name = "lzr_laser_cskull_top.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_cskull_bottom.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_cskull_side.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_cskull_side.png^[transformFX)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_cskull_back.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_cskull_front.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
	},
	__tiles_takable_on = {
		{ name = "lzr_laser_cskull_top.png", backface_culling = true },
		{ name = "lzr_laser_cskull_bottom.png", backface_culling = true },
		{ name = "lzr_laser_cskull_side.png", backface_culling = true },
		{ name = "lzr_laser_cskull_side.png^[transformFX", backface_culling = true },
		{ name = "lzr_laser_cskull_back.png", backface_culling = true },
		{ name = "lzr_laser_cskull_front.png^lzr_laser_cskull_on.png", backface_culling = true },
	},
	__use_texture_alpha_off = "blend",
	__use_texture_alpha_on = "clip",
	__light_source_on = 5,
	__sounds_off = {
		footstep = "",
		dug = { name = "lzr_laser_skull_ghostly_dug", gain = 0.5 },
		place = { name = "lzr_laser_skull_place", gain = 0.6 },
		_rotate = { name = "lzr_laser_skull_rotate", gain = 0.3 },
	},
	__sounds_on = {
		footstep = { name = "lzr_laser_skull_footstep", gain = 0.3 },
		dug = { name = "lzr_laser_skull_dug", gain = 0.6 },
		place = { name = "lzr_laser_skull_place", gain = 0.6 },
		_rotate = { name = "lzr_laser_skull_rotate", gain = 0.3 },
	},
	groups = { skull = 1, laser_block = 1 },
}, { allow_take = true, not_walkable_if_off = true, not_pointable_if_off = true, register_color = false, group = "skull_cursed" })

-- Is walkable if off,
-- and non-walkable if on.
lzr_laser.register_element("lzr_laser:skull_shy", {
	description = S("Shy Skull"),
	__tt_help_off = S("Becomes untouchable when a laser goes through it"),
	__tt_help_on = S("Is untouchable while a laser goes through it"),
	drawtype = "mesh",
	-- Mesh based on skull nodebox by rudzik8
	__mesh_off = "lzr_laser_skull.obj",
	__mesh_on = "lzr_laser_skull.obj",
	collision_box = {type="regular"},
	selection_box = {type="regular"},
	paramtype = "light",
	paramtype2 = "4dir",
	__tiles_off = {
		{ name = "lzr_laser_sskull_top.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_sskull_bottom.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_sskull_side.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_sskull_side.png^lzr_laser_fixed.png^[transformFX", backface_culling = true },
		{ name = "lzr_laser_sskull_back.png^lzr_laser_fixed.png", backface_culling = true },
		{ name = "lzr_laser_sskull_front.png^lzr_laser_fixed.png", backface_culling = true },
	},
	__tiles_on = {
		{ name = "(lzr_laser_sskull_top.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_sskull_bottom.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_sskull_side.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_sskull_side.png^lzr_laser_fixed.png^[transformFX)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_sskull_back.png^lzr_laser_fixed.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_sskull_front.png^lzr_laser_fixed.png^lzr_laser_sskull_on.png)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
	},
	__tiles_takable_off = {
		{ name = "lzr_laser_sskull_top.png", backface_culling = true },
		{ name = "lzr_laser_sskull_bottom.png", backface_culling = true },
		{ name = "lzr_laser_sskull_side.png", backface_culling = true },
		{ name = "lzr_laser_sskull_side.png^[transformFX", backface_culling = true },
		{ name = "lzr_laser_sskull_back.png", backface_culling = true },
		{ name = "lzr_laser_sskull_front.png", backface_culling = true },
	},
	__tiles_takable_on = {
		{ name = "lzr_laser_sskull_top.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_sskull_bottom.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_sskull_side.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "(lzr_laser_sskull_side.png^[transformFX)^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_sskull_back.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
		{ name = "lzr_laser_sskull_front.png^lzr_laser_sskull_on.png^[opacity:"..OPACITY_SKULL_GONE, backface_culling = true },
	},
	__use_texture_alpha_off = "clip",
	__use_texture_alpha_on = "blend",
	__light_source_on = 5,
	groups = { skull = 2, laser_block = 1 },
	__sounds_off = {
		footstep = { name = "lzr_laser_skull_footstep", gain = 0.3 },
		dug = { name = "lzr_laser_skull_dug", gain = 0.6 },
		place = { name = "lzr_laser_skull_place", gain = 0.6 },
		_rotate = { name = "lzr_laser_skull_rotate", gain = 0.3 },
	},
	__sounds_on = {
		footstep = "",
		dug = { name = "lzr_laser_skull_ghostly_dug", gain = 0.5 },
		place = { name = "lzr_laser_skull_place", gain = 0.6 },
		_rotate = { name = "lzr_laser_skull_rotate", gain = 0.3 },
	},
}, { allow_take = true, not_walkable_if_on = true, not_pointable_if_on = true, register_color = false, group = "skull_shy" })

minetest.register_node("lzr_laser:barricade", {
	description = S("Barricade"),
	_tt_help = S("Burns on laser contact"),
	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 = { barricade = 1, laser_block = 1, breakable = 1, flammable = 1, laser_destroys = 2 },
	sounds = lzr_sounds.node_sound_sticks_defaults(),
	_lzr_active = "lzr_laser:barricade_on",
})

minetest.register_node("lzr_laser:barricade_on", {
	description = S("Burning Barricade"),
	_tt_help = S("Ignites neighboring barricades and burns up after 1 second"),
	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 = { laser_block = 1, barricade = 2, breakable = 1, not_in_creative_inventory = 1 },
	sounds = lzr_sounds.node_sound_sticks_defaults({
		footstep = {name="lzr_sounds_sticks_footstep", gain=1.0, pitch = 1.2},
	}),
	drop = "",
	_lzr_inactive = "lzr_laser:barricade",
})

-- The bomb is manually registered and not using the register_element
-- function.
-- TODO: Use register_element to register bomb

local def_bomb_takable = {
	description = S("Bomb"),
	paramtype2 = "facedir",
	_tt_help = S("Explodes when fuse gets ignited by laser or fire").."\n"..
		S("Destroys cracked blocks in a 3×3×3 area"),
	tiles = {"lzr_laser_bomb_top.png", "lzr_laser_bomb_bottom.png", "lzr_laser_bomb_side.png"},
	groups = { bomb = 1, breakable = 1, laser_block = 1, laser_destroys = 3, flammable = 2, rotatable = 1, takable = 1 },
	sounds = lzr_sounds.node_sound_metal_defaults(),
	_lzr_active = "lzr_laser:bomb_takable_on",
	_lzr_fixed = "lzr_laser:bomb_fixed",
	_lzr_rotatable = "lzr_laser:bomb_rotatable",
	_lzr_takable = "lzr_laser:bomb_takable",
}

local def_bomb_takable_on = {
	description = S("Ignited bomb"),
	paramtype2 = "facedir",
	_tt_help = S("Will explode soon").."\n"..
		S("Destroys cracked blocks in a 3×3×3 area"),
	light_source = 3,
	tiles = {
		{ name="lzr_laser_bomb_top_ignited.png", animation={
			type = "vertical_frames",
			aspect_w = 16,
			aspect_h = 16,
			length = 1.0,
		},},
		"lzr_laser_bomb_bottom.png",
		"lzr_laser_bomb_side.png",
	},
	groups = { bomb = 2, breakable = 1, laser_block = 1, not_in_creative_inventory = 1, rotatable = 3 },
	sounds = lzr_sounds.node_sound_metal_defaults(),
	drop = "",
	_lzr_inactive = "lzr_laser:bomb_takable",
	_lzr_fixed = "lzr_laser:bomb_fixed_on",
	_lzr_rotatable = "lzr_laser:bomb_rotatable_on",
	_lzr_takable = "lzr_laser:bomb_takable_on",
}

local def_bomb_fixed = table.copy(def_bomb_takable)
def_bomb_fixed.tiles = {"lzr_laser_bomb_top.png^lzr_laser_fixed.png", "lzr_laser_bomb_bottom.png^lzr_laser_fixed.png", "lzr_laser_bomb_side.png^lzr_laser_fixed.png"}
def_bomb_fixed.description = S("@1 (iron screws)", S("Bomb"))
def_bomb_fixed.groups.takable = nil
def_bomb_fixed.groups.rotatable = 3
def_bomb_fixed.groups.screws = 2
def_bomb_fixed._lzr_active = "lzr_laser:bomb_fixed_on"

local def_bomb_fixed_on = table.copy(def_bomb_takable_on)
def_bomb_fixed_on.description = S("@1 (iron screws)", S("Ignited Bomb"))
def_bomb_fixed_on.tiles = {
	{ name="lzr_laser_bomb_top_ignited.png^lzr_laser_bomb_top_ignited_fixed.png", animation={
		type = "vertical_frames",
		aspect_w = 16,
		aspect_h = 16,
		length = 1.0,
	},},
	"lzr_laser_bomb_bottom.png^lzr_laser_fixed.png",
	"lzr_laser_bomb_side.png^lzr_laser_fixed.png",
}
def_bomb_fixed_on.groups.screws = 2
def_bomb_fixed_on._lzr_inactive = "lzr_laser:bomb_fixed"

local def_bomb_rotatable = table.copy(def_bomb_takable)
def_bomb_rotatable.tiles = {"lzr_laser_bomb_top.png^lzr_laser_fixed_soft.png", "lzr_laser_bomb_bottom.png^lzr_laser_fixed_soft.png", "lzr_laser_bomb_side.png^lzr_laser_fixed_soft.png"}
def_bomb_rotatable.description = S("@1 (rotatable)", S("Bomb"))
def_bomb_rotatable.groups.takable = nil
def_bomb_rotatable.groups.rotatable = 1
def_bomb_rotatable.groups.screws = 1
def_bomb_rotatable._lzr_active = "lzr_laser:bomb_rotatable_on"

local def_bomb_rotatable_on = table.copy(def_bomb_takable_on)
def_bomb_rotatable_on.description = S("@1 (copper screws)", S("Ignited Bomb"))
def_bomb_rotatable_on.groups.screws = 1
def_bomb_rotatable_on.tiles = {
	{ name="lzr_laser_bomb_top_ignited.png^lzr_laser_bomb_top_ignited_fixed_soft.png", animation={
		type = "vertical_frames",
		aspect_w = 16,
		aspect_h = 16,
		length = 1.0,
	},},
	"lzr_laser_bomb_bottom.png^lzr_laser_fixed_soft.png",
	"lzr_laser_bomb_side.png^lzr_laser_fixed_soft.png",
}
def_bomb_rotatable_on._lzr_inactive = "lzr_laser:bomb_rotatable"

minetest.register_node("lzr_laser:bomb_fixed", def_bomb_fixed)
minetest.register_node("lzr_laser:bomb_fixed_on", def_bomb_fixed_on)
minetest.register_node("lzr_laser:bomb_rotatable", def_bomb_rotatable)
minetest.register_node("lzr_laser:bomb_rotatable_on", def_bomb_rotatable_on)
minetest.register_node("lzr_laser:bomb_takable", def_bomb_takable)
minetest.register_node("lzr_laser:bomb_takable_on", def_bomb_takable_on)


-- Aliases for the pre-laser color era
minetest.register_alias("lzr_laser:mirror_takable_on", "lzr_laser:mirror_takable_on_1")
minetest.register_alias("lzr_laser:hollow_barrel_takable_on", "lzr_laser:hollow_barrel_takable_on_1")

-- Aliases for the pre-rotatable era
minetest.register_alias("lzr_laser:mirror", "lzr_laser:mirror_rotatable")
minetest.register_alias("lzr_laser:mirror_on", "lzr_laser:mirror_rotatable_on_1")
minetest.register_alias("lzr_laser:hollow_barrel", "lzr_laser:hollow_barrel_rotatable")
minetest.register_alias("lzr_laser:hollow_barrel_on", "lzr_laser:hollow_barrel_rotatable_on_1")
minetest.register_alias("lzr_laser:crystal", "lzr_laser:crystal_fixed")
minetest.register_alias("lzr_laser:crystal_on", "lzr_laser:crystal_fixed_on_1")
minetest.register_alias("lzr_laser:transmissive_mirror_00", "lzr_laser:transmissive_mirror_00_rotatable")
minetest.register_alias("lzr_laser:transmissive_mirror_01", "lzr_laser:transmissive_mirror_01_rotatable")
minetest.register_alias("lzr_laser:transmissive_mirror_10", "lzr_laser:transmissive_mirror_10_rotatable")
minetest.register_alias("lzr_laser:transmissive_mirror_11", "lzr_laser:transmissive_mirror_11_rotatable")
minetest.register_alias("lzr_laser:detector", "lzr_laser:detector_fixed")
minetest.register_alias("lzr_laser:detector_on", "lzr_laser:detector_fixed_on")
minetest.register_alias("lzr_laser:emitter", "lzr_laser:emitter_red_fixed")
minetest.register_alias("lzr_laser:emitter_on", "lzr_laser:emitter_red_fixed_on")
minetest.register_alias("lzr_laser:skull_cursed", "lzr_laser:skull_cursed_fixed")
minetest.register_alias("lzr_laser:skull_cursed_on", "lzr_laser:skull_cursed_fixed_on")
minetest.register_alias("lzr_laser:skull_shy", "lzr_laser:skull_shy_fixed")
minetest.register_alias("lzr_laser:skull_shy_on", "lzr_laser:skull_shy_fixed_on")
