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,
}

-- Special string to mark a tile in a laser element as the 'laser tile'
-- for lzr_laser.register_element. The laser tile is the
-- the tile (texture) that represents the laser itself. This string
-- will be replaced automatically by the appropriate laser texture
-- on registration
lzr_laser.LASER_TILE = "<<<LASER>>>"

-- Update the lasers within level bounds, if neccessary
lzr_laser.full_laser_update_if_needed = function(extra_state)
	local state = lzr_gamestate.get_state()
	if state ~= lzr_gamestate.LEVEL and state ~= lzr_gamestate.LEVEL_COMPLETE and state ~= lzr_gamestate.LEVEL_TEST and state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV then
		return false
	end
	local minpos, maxpos = lzr_world.get_level_bounds()
	lzr_laser.full_laser_update(minpos, maxpos, extra_state)
	return true
end

minetest.register_on_placenode(function(pos, newnode, placer)
	local state = lzr_gamestate.get_state()
	if state ~= lzr_gamestate.LEVEL and state ~= lzr_gamestate.LEVEL_COMPLETE and state ~= lzr_gamestate.LEVEL_TEST and state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV then
		return
	end
	lzr_laser.full_laser_update_if_needed()
end)
minetest.register_on_dignode(function(pos, oldnode, placer)
	local state = lzr_gamestate.get_state()
	if state ~= lzr_gamestate.LEVEL and state ~= lzr_gamestate.LEVEL_COMPLETE and state ~= lzr_gamestate.LEVEL_TEST and state ~= lzr_gamestate.EDITOR and state ~= lzr_gamestate.DEV then
		return
	end
	do
		lzr_laser.full_laser_update_if_needed()
	end
end)

local element_on_place = function(itemstack, placer, pointed_thing)
	-- 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

--[[ Register a 'laser element', i.e. a collection of nodes that
are supposed to interact with lasers. Each of these nodes is called a 'laser block'.

A single laser element may register many many nodes, depending on its
complexity.

Laser elements may have the following properties:
* They may be in an active or inactive state
* They may be rotated by the player, or not (must be enabled with `options.allow_rotate`)
* They may be takable by the player, or not (must be enabled with `options.allow_take`)
  (note: all takable nodes are also rotatable by implication)
* For the active state, by default a node for every laser color will be added
* All its nodes share a new group that identify it as part of a laser element (in `options.group`)

The registered nodes follow these naming conventions:

* `<basename>_fixed`: This is the base node. Inactive, cannot be taken or rotated
* `<basename>_fixed_on_<color>`: Active, cannot be taken or rotated.
* `<basename>_rotatable`: Inactive, can be rotated but not be taken
* `<basename>_rotatable_on_<color>`: Active, can be rotated but not be taken
* `<basename>_takable`: Inactive, can be taken and rotated
* `<basename>_takable_on_<color>`: Active, can be taken and rotated.

`<color>` is a colorcode from 1 to lzr_globals.MAX_COLORCODE.

If `options.register_color` is false, the active nodes won't have a `<color>`
suffix. Instead, they will be:

* `<basename>_fixed_on`: Active, non-takable, not-rotatable
* `<basename>_rotatable_on`: Active, rotatable, non-takable
* `<basename>_takable_on`: Active, rotatable, takable

Groups:

The following groups will be added automatically to the nodes:

* `[options.group]=1` to the inactive nodes
* `[options.group]=2` to the active nodes
* `breakable=1` to every node
* `takable=1` to nodes that can be taken by the player
* `rotatable=1` to nodes that can be rotated by the player
* `rotatable=3` to nodes that can only be rotated in the editor
* `not_in_creative_inventory=1` to active nodes by default
* `laser_block_color=<colorcode>` to active nodes with a laser color (not if `options.register_color` is false)
* `screws=X` if the node receives screws

Parameters:

* basename: Itemstring identifier of the base node
	May be preceded by a color to force a node name, as handled by minetest.register_node
* def: Definition table to define the node. All fields of the Luanti node definition can be applied here,
  but note that some fields will be overwritten.
  To use after_dig_node, you must specify it as __after_dig_node.
  To use after_place_node, you must specify it as __after_place_node.
  In addition, the following fields are special:
	* description: Description of the base node. The other node variants
	  will have automatic variations added
	* __tiles_off: `tiles` table for the inactive and non-takable state
	* __tiles_on: `tiles` table for the active and non-takable state
	* __tiles_rotatable_off: `tiles` table for the inactive and rotatable state
	* __tiles_rotatable_on: `tiles` table for the active and rotatable state
	* __tiles_takable_off: `tiles` table for the inactive and non-takable state
	* __tiles_takable_on: `tiles` table for the active and non-takable state
	* __sounds_off: `sounds` table for the inactive state
	* __sounds_on: `sounds` table for the inactive state
	* __overlay_tiles_off: `overlay_tiles` table for the inactive and non-takable state
	* __overlay_tiles_on: `overlay_tiles` table for the active and non-takable state
	* __overlay_tiles_rotatable_off: `overlay_tiles` table for the inactive and rotatable state
	* __overlay_tiles_rotatable_on: `overlay_tiles` table for the active and rotatable state
	* __overlay_tiles_takable_off: `overlay_tiles` table for the inactive and non-takable state
	* __overlay_tiles_takable_on: `overlay_tiles` table for the active and non-takable state
	* __mesh_off: `mesh` value in inactive state
	* __mesh_on: `mesh` value in active state
	* __tt_help_off: `_tt_help` field for the inactive state (see `tt` mod)
	* __tt_help_on: `_tt_help` field for the active state (see `tt` mod)
	* __light_source_on: `light_source` value for the nodes in active state (default: 0)
	* __light_source_off: `light_source` value for the nodes in inactive state (default: 0)
	* __use_texture_alpha_on: `on_texture_alpha` value for the nodes in active state
	* __use_texture_alpha_off: `on_texture_alpha` value for the nodes in inactive state
	* __after_dig_node: You must use this instead of `after_dig_node`
	* __after_place_node: You must use this instead of `after_place_node`
	* __on_place: You must use this instead of `on_place`
	* __lzr_next_color: Contains the basename of the node with the next color in a
	  sequence of colors, for the color change tool (use only if register_colors is false)
	* __lzr_prev_color: Same as __lzr_next_color, but for the previous color
	* __drop: Overwrite node drop, must be a stingle string. Only works for nodes that were
          registered as elements. Put in the element basename here, without suffixes like `_fixed`.
	  Can't be used together with options.keep_state_on_take.

Note about tiles: If your laser element has a laser in it, use the special tile `lzr_laser.LASER_TILE`
in the `tiles_*` tables to represent the tiles texture. This function will replace
it with the appropriate laser color.

* options: Additional options to further specify the laser element.
	All fields are optional except 'group'. List of fields:
	* group: Mandatory name of group that identifies the nodes as
	  part of the laser element to add. Will add [group]=1 for inactive nodes
	  and [group]=2 for active nodes. Will also be added as
          the `_lzr_element_group` field to all nodes
	* not_walkable_if_off: If true, the element turns non-walkable
	  if inactive (default: false)
	* not_walkable_if_on: If true, the element turns non-walkable
	  if active (default: false)
	* not_pointable_if_off: If true, the element turns non-pointable
	  if inactive (default: false)
	* not_pointable_if_on: If true, the element turns non-pointable
	  if active (default: false)
	* allow_take: If true, add 'takable' nodes (default: false)
	* keep_state_on_take: If true, element will keep its on/off state
	  when taken by player (can't be used together with __drop field) (default: false)
	* activate: If true, add 'activated' nodes (default: true)
	* inactive: If set, override define the node name of the element in inactive
	  state, without the '_fixed', '_rotatable', '_takable' suffix
          (default: nil)
	* active_in_creative: If true, allow the 'active' nodes to
	  be shown in the “Creative inventory” (read: level editor)
	  (default: false)
	* register_colors: If true, all laser color variants will
	  be added for the active node state, registering a node
	  for every colorcode (default: true)
	* explicit_inactive_description: By default, the word "active" will be added
	  to the description to active nodes and nothing to inactive nodes.
	  Set this to true to add the word "inactive" for inactive nodes as well.
	  (default: false)
]]

lzr_laser.register_element = function(basename, def, options)
	local real_basename = basename
	if string.sub(basename, 1, 1) == ":" then
		real_basename = string.sub(basename, 2, -1)
	end

	local special_fields = {
		"__tiles_off",
		"__tiles_on",
		"__tiles_rotatable_off",
		"__tiles_rotatable_on",
		"__tiles_takable_off",
		"__tiles_takable_on",
		"__sounds_off",
		"__sounds_on",
		"__overlay_tiles_off",
		"__overlay_tiles_on",
		"__overlay_tiles_rotatable_off",
		"__overlay_tiles_rotatable_on",
		"__overlay_tiles_takable_off",
		"__overlay_tiles_takable_on",
		"__tt_help_off",
		"__tt_help_on",
		"__mesh_off",
		"__mesh_on",
		"__light_source_on",
		"__light_source_off",
		"__use_texture_alpha_on",
		"__use_texture_alpha_off",
		"__after_dig_node",
		"__after_place_node",
		"__on_place",
		"__lzr_next_color",
		"__lzr_prev_color",
		"__drop",
	}


	local def_core = table.copy(def)
	def_core._lzr_element_group = options.group


	if def_core.groups and (def_core.groups.sender or def_core.groups.receiver) then
		-- The following two functions transport the 'trigger_id' in
		-- metadata from a trigger node when it
		-- transforms from node to item form and vice-versa.
		-- Also update the trigger location.

		-- When a trigger node is converted to an item
		def_core.preserve_metadata = function(pos, oldnode, oldmeta, drops)
			if not oldmeta and not oldmeta.trigger_id then
				return
			end
			local trigger_id = oldmeta.trigger_id
			local gs = lzr_gamestate.get_state()
			if gs ~= lzr_gamestate.LEVEL and gs ~= lzr_gamestate.LEVEL_TEST then
				lzr_triggers.remove_trigger(trigger_id)
				return
			end
			-- Copy the item's trigger_id into all of the node's drops.
			-- This assumes the node has no 'weird' drops
			-- like an additional item.
			for d=1, #drops do
				local dmeta = drops[d]:get_meta()
				dmeta:set_string("trigger_id", trigger_id)
			end

			-- Update trigger location
			lzr_triggers.set_trigger_location(trigger_id, "player")
		end
		-- When a trigger node has been placed back by the player
		def_core.after_place_node = function(pos, placer, itemstack, pointed_thing)
			local state = lzr_gamestate.get_state()
			if state == lzr_gamestate.EDITOR then
				local minpos, maxpos = lzr_world.get_level_bounds()
				if vector.in_area(pos, minpos, maxpos) then
					local trigger_id = lzr_triggers.add_trigger(pos)
					if trigger_id then
						local meta = minetest.get_meta(pos)
						meta:set_string("trigger_id", trigger_id)
					end
				end
				return
			elseif state == lzr_gamestate.LEVEL or state == lzr_gamestate.LEVEL_TEST then
				local minpos, maxpos = lzr_world.get_level_bounds()
				if not vector.in_area(pos, minpos, maxpos) then
					minetest.log("error", "[lzr_laser] Placed a trigger node out of level bounds at: "..minetest.pos_to_string(pos))
					return
				end

				-- Copy item's trigger_id into node
				local imeta = itemstack:get_meta()
				local nmeta = minetest.get_meta(pos)
				local trigger_id = imeta:get_string("trigger_id")
				nmeta:set_string("trigger_id", trigger_id)

				-- Update trigger location
				if lzr_triggers.trigger_exists(trigger_id) then
					lzr_triggers.set_trigger_location(trigger_id, pos)
				else
					minetest.log("error", "[lzr_laser] Tried to set trigger location of non-existing trigger at: "..minetest.pos_to_string(pos))
					return
				end
			end

			if def.__after_place_node then
				def.__after_place_node(pos, placer, itemstack, pointed_thing)
			end
		end

		if def_core.groups.sender then
			def_core.after_dig_node = function(pos, oldnode, oldmeta, pointed_thing)
				-- If a sender was dug, we have to pass its information to the laser update function
				-- so it can update the trigger state properly. If we don't do this,
				-- the laser update function will see the map incorrectly.
				if lzr_laser.is_laser_block_active(oldnode.name) then
					local triggered = false
					if oldmeta and oldmeta.fields and oldmeta.fields.trigger_id then
						local trigger_id = oldmeta.fields.trigger_id
						local trigger = lzr_triggers.get_trigger(trigger_id)
						if trigger then
							local extra_state = {
								removed_active_sender_trigger_id = trigger_id,
								removed_active_sender_pos = pos,
								removed_active_sender_send_to = lzr_triggers.get_receivers(trigger_id),
								removed_active_sender_sender_type = trigger.sender_type,
							}
							lzr_laser.full_laser_update_if_needed(extra_state)
							triggered = true
						end
					end
					if not triggered then
						local gs = lzr_gamestate.get_state()
						if gs == lzr_gamestate.LEVEL or gs == lzr_gamestate.LEVEL_TEST then
							minetest.log("error", "[lzr_laser] Dug node at "..minetest.pos_to_string(pos).." should be a trigger but appears not to be one")
						end
					end
				end

				if def.__after_dig_node then
					def.__after_dig_node(pos, oldnode, oldmeta, pointed_thing)
				end
			end
		end
	end

	local def_spec = {}
	for s=1, #special_fields do
		local field = special_fields[s]
		def_spec[field] = def[field]
		def_core[field] = nil
	end

	if not def_core.groups then
		def_core.groups = {}
	end

	if not options then options = {} end
	if options.not_walkable_if_off then
		def_core.walkable = false
	end
	if options.not_pointable_if_off then
		def_core.pointable = false
	end
	def_core._lzr_fixed = real_basename.."_fixed"
	if options.allow_take then
		def_core._lzr_takable = real_basename.."_takable"
	end
	if options.allow_rotate then
		def_core._lzr_rotatable = real_basename.."_rotatable"
	end

	if options.allow_take or options.allow_rotate then
		def_core.groups.screws = 2
		if options.explicit_inactive_description then
			--~ Annotation for a block @1
			def_core.description = S("@1 (iron screws, inactive)", def.description)
		else
			--~ Annotation for a block @1
			def_core.description = S("@1 (iron screws)", def.description)
		end
	else
		if options.explicit_inactive_description then
			--~ Annotation for a block @1
			def_core.description = S("@1 (inactive)", def.description)
		else
			def_core.description = def.description
		end
	end
	def_core.tiles = def_spec.__tiles_off
	def_core.overlay_tiles = def_spec.__overlay_tiles_off
	def_core.mesh = def_spec.__mesh_off
	if def_spec.__sounds_off then
		def_core.sounds = def_spec.__sounds_off
	end
	if def_spec.__tt_help_off then
		def_core._tt_help = def_spec.__tt_help_off
	end
	if def_spec.__use_texture_alpha_off then
		def_core.use_texture_alpha = def_spec.__use_texture_alpha_off
	end

	def_core.groups.breakable = 1

	if options.allow_rotate or options.allow_take then
		def_core.groups.rotatable = 3
	end
	local groupname
	if options.group then
		groupname = options.group
	else
		error("[lzr_laser] lzr_laser.register_element: No group name specified in options for laser element with basename: "..tostring(basename))
	end
	def_core.groups[groupname] = 1
	if def.__on_place then
		def_core.on_place = def.__on_place
	else
		def_core.on_place = element_on_place
	end
	if options.inactive ~= nil then
		def_core._lzr_inactive = options.inactive .. "_fixed"
		if def_spec.__drop then
			def_core.drop = def_spec.__drop .. "_fixed"
		elseif not options.keep_state_on_take then
			def_core.drop = options.inactive .. "_fixed"
		end
	else
		if def_spec.__drop then
			def_core.drop = def_spec.__drop .. "_fixed"
		end
		def_core._lzr_inactive = real_basename.."_fixed"
	end
	if options.activate ~= false then
		def_core._lzr_active = real_basename .. "_fixed_on"
	end
	if def.__lzr_next_color then
		def_core._lzr_next_color = def.__lzr_next_color.."_fixed"
		def_core._lzr_prev_color = def.__lzr_prev_color.."_fixed"
	end

	minetest.register_node(basename.."_fixed", def_core)

	local def_core_on
	if options.activate ~= false then
		def_core_on = table.copy(def_core)

		if def.__lzr_next_color then
			def_core_on._lzr_next_color = def.__lzr_next_color.."_fixed_on"
			def_core_on._lzr_prev_color = def.__lzr_prev_color.."_fixed_on"
		end
		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
		if options.not_pointable_if_off then
			def_core_on.pointable = true
		elseif options.not_pointable_if_on then
			def_core_on.pointable = false
		end

		if options.allow_take or options.allow_rotate then
			def_core.groups.screws = 2
			--~ Annotation for a block @1
			def_core_on.description = S("@1 (iron screws, active)", def.description)
		else
			--~ Annotation for a block @1
			def_core_on.description = S("@1 (active)", def.description)
		end
		def_core_on._lzr_inactive = real_basename.."_fixed"
		def_core_on.tiles = def_spec.__tiles_on
		def_core_on.overlay_tiles = def_spec.__overlay_tiles_on
		def_core_on.mesh = def_spec.__mesh_on
		if def_spec.__sounds_on then
			def_core_on.sounds = def_spec.__sounds_on
		end
		if def_spec.__tt_help_on then
			def_core_on._tt_help = def_spec.__tt_help_on
		end
		if def_spec.__use_texture_alpha_on then
			def_core_on.use_texture_alpha = def_spec.__use_texture_alpha_on
		end
		if def_spec.__drop then
			def_core_on.drop = def_spec.__drop.."_fixed"
		elseif not options.keep_state_on_take then
			def_core_on.drop = real_basename.."_fixed"
		end
		def_core_on._lzr_fixed = real_basename.."_fixed_on"
		if options.allow_take then
			def_core_on._lzr_takable = real_basename.."_takable_on"
		end
		def_core_on.groups[groupname] = 2
		if not options.active_in_creative then
			def_core_on.groups.not_in_creative_inventory = 1
			def_core_on.inventory_image = nil
			def_core_on.wield_image = nil
		end
		def_core_on.light_source = def_spec.__light_source_on

		if options.register_color == false then
			def_core_on._lzr_fixed = real_basename.."_fixed_on"
			if options.allow_take then
				def_core_on._lzr_takable = real_basename.."_takable_on"
			end
			if options.allow_rotate then
				def_core_on._lzr_rotatable = real_basename.."_rotatable_on"
			end
			minetest.register_node(basename.."_fixed_on", def_core_on)
		else
			for c=1, lzr_globals.MAX_COLORCODE do
				local def_core_on_c = table.copy(def_core_on)
				if options.allow_take or options.allow_rotate then
					def_core_on_c.groups.screws = 2
					--~ Annotation for a block @1. @2 is a color code uniquely identifying the active laser color in that block
					def_core_on_c.description = S("@1 (iron screws, active, @2)", def.description, lzr_laser.dirstring_to_colstring(tostring(c)))
				else
					--~ Annotation for a block @1. @2 is a color code uniquely identifying the active laser color in that block
					def_core_on_c.description = S("@1 (active, @2)", def.description, lzr_laser.dirstring_to_colstring(tostring(c)))
				end
				for t=1, #def_core_on_c.tiles do
					if def_core_on_c.tiles[t] == lzr_laser.LASER_TILE then
						def_core_on_c.tiles[t] = colortiles[c]
					end
				end
				def_core_on_c.groups.laser_block_color = c

				def_core_on_c._lzr_fixed = real_basename.."_fixed_on_"..c
				if options.allow_take then
					def_core_on_c._lzr_takable = real_basename.."_takable_on_"..c
				end
				if options.allow_rotate then
					def_core_on_c._lzr_rotatable = real_basename.."_rotatable_on_"..c
				end

				minetest.register_node(basename.."_fixed_on_"..c, def_core_on_c)
			end
		end
	end

	if options.allow_rotate then
		local def_rotatable = table.copy(def_core)

		def_rotatable._lzr_fixed = real_basename.."_fixed"
		if options.allow_take then
			def_rotatable._lzr_takable = real_basename.."_takable"
		end
		def_rotatable._lzr_rotatable = real_basename.."_rotatable"

		def_rotatable.tiles = def_spec.__tiles_rotatable_off
		def_rotatable.overlay_tiles = def_spec.__overlay_tiles_rotatable_off
		def_rotatable.groups.rotatable = 1
		def_rotatable.groups.screws = 1
		if options.explicit_inactive_description then
			--~ Annotation for a block @1
			def_rotatable.description = S("@1 (copper screws, inactive)", def.description)
		else
			--~ Annotation for a block @1
			def_rotatable.description = S("@1 (copper screws)", def.description)
		end
		if options.inactive ~= nil then
			def_rotatable._lzr_inactive = options.inactive.."_rotatable"
			if def_spec.__drop then
				def_rotatable.drop = def_spec.__drop.."_rotatable"
			elseif not options.keep_state_on_take then
				def_rotatable.drop = options.inactive.."_rotatable"
			end
		else
			def_rotatable._lzr_inactive = real_basename.."_rotatable"
			if def_spec.__drop then
				def_rotatable.drop = def_spec.__drop.."_rotatable"
			end
		end
		if def.__lzr_next_color then
			def_rotatable._lzr_next_color = def.__lzr_next_color.."_rotatable"
			def_rotatable._lzr_prev_color = def.__lzr_prev_color.."_rotatable"
		end
		if options.activate ~= false then
			def_rotatable._lzr_active = real_basename.."_rotatable_on"
		end

		minetest.register_node(basename.."_rotatable", def_rotatable)

		if options.activate ~= false then
			local def_rotatable_on = table.copy(def_core_on)

			if def.__lzr_next_color then
				def_rotatable_on._lzr_next_color = def.__lzr_next_color.."_rotatable_on"
				def_rotatable_on._lzr_prev_color = def.__lzr_prev_color.."_rotatable_on"
			end
			def_rotatable_on.tiles = def_spec.__tiles_rotatable_on
			def_rotatable_on.overlay_tiles = def_spec.__overlay_tiles_rotatable_on
			def_rotatable_on.light_source = def_spec.__light_source_on
			def_rotatable_on.groups.rotatable = 1
			def_rotatable_on.groups.screws = 1
			if not options.active_in_creative then
				def_rotatable_on.groups.not_in_creative_inventory = 1
				def_rotatable_on.inventory_image = nil
				def_rotatable_on.wield_image = nil
			end
			--~ Annotation for a block @1
			def_rotatable_on.description = S("@1 (copper screws, active)", def.description)
			if def_spec.__drop then
				def_rotatable_on.drop = def_spec.__drop .."_rotatable"
			elseif not options.keep_state_on_take then
				def_rotatable_on.drop = real_basename.."_rotatable"
			end
			def_rotatable_on._lzr_inactive = real_basename.."_rotatable"
			def_rotatable_on._lzr_active = real_basename.."_rotatable_on"

			if options.register_color == false then
				def_rotatable_on._lzr_fixed = real_basename.."_fixed_on"
				if options.allow_take then
					def_rotatable_on._lzr_takable = real_basename.."_takable_on"
				end
				def_rotatable_on._lzr_rotatable = real_basename.."_rotatable_on"

				minetest.register_node(basename.."_rotatable_on", def_rotatable_on)
			else
				for c=1, lzr_globals.MAX_COLORCODE do
					local def_rotatable_on_c = table.copy(def_rotatable_on)
					--~ Annotation for a block @1
					def_rotatable_on_c.description = S("@1 (copper screws, active, @2)", def.description, lzr_laser.dirstring_to_colstring(tostring(c)))
					for t=1, #def_rotatable_on_c.tiles do
						if def_rotatable_on_c.tiles[t] == lzr_laser.LASER_TILE then
							def_rotatable_on_c.tiles[t] = colortiles[c]
						end
					end

					def_rotatable_on_c.groups.laser_block_color = c

					def_rotatable_on_c._lzr_fixed = real_basename.."_fixed_on_"..c
					if options.allow_take then
						def_rotatable_on_c._lzr_takable = real_basename.."_takable_on_"..c
					end
					def_rotatable_on_c._lzr_rotatable = real_basename.."_rotatable_on_"..c

					minetest.register_node(basename.."_rotatable_on_"..c, def_rotatable_on_c)
				end
			end
		end
	end

	if options.allow_take then
		local def_takable = table.copy(def_core)

		def_takable._lzr_fixed = real_basename.."_fixed"
		if options.allow_rotate then
			def_takable._lzr_rotatable = real_basename.."_rotatable"
		end
		def_takable._lzr_takable = real_basename.."_takable"

		def_takable.tiles = def_spec.__tiles_takable_off
		def_takable.overlay_tiles = def_spec.__overlay_tiles_takable_off
		def_takable.groups.takable = 1
		-- takable nodes are also always rotatable by implication
		def_takable.groups.rotatable = 1
		if options.explicit_inactive_description then
			--~ Annotation for a block @1
			def_takable.description = S("@1 (inactive)", def.description)
		else
			def_takable.description = def.description
		end
		if options.inactive ~= nil then
			def_takable._lzr_inactive = options.inactive.."_takable"
			if def_spec.__drop then
				def_takable.drop = def_spec.__drop.."_takable"
			elseif not options.keep_state_on_take then
				def_takable.drop = options.inactive.."_takable"
			end
		else
			if def_spec.__drop then
				def_takable.drop = def_spec.__drop.."_takable"
			end
			def_takable._lzr_inactive = real_basename.."_takable"
		end
		if def.__lzr_next_color then
			def_takable._lzr_next_color = def.__lzr_next_color.."_takable"
			def_takable._lzr_prev_color = def.__lzr_prev_color.."_takable"
		end
		if options.activate ~= false then
			def_takable._lzr_active = real_basename.."_takable_on"
		end

		minetest.register_node(basename.."_takable", def_takable)

		if options.activate ~= false then
			local def_takable_on = table.copy(def_core_on)

			if def.__lzr_next_color then
				def_takable_on._lzr_next_color = def.__lzr_next_color.."_takable_on"
				def_takable_on._lzr_prev_color = def.__lzr_prev_color.."_takable_on"
			end
			def_takable_on.tiles = def_spec.__tiles_takable_on
			def_takable_on.overlay_tiles = def_spec.__overlay_tiles_takable_on
			def_takable_on.light_source = def_spec.__light_source_on
			def_takable_on.groups.takable = 1
			def_takable_on.groups.rotatable = 1
			if not options.active_in_creative then
				def_takable_on.groups.not_in_creative_inventory = 1
				def_takable_on.inventory_image = nil
				def_takable_on.wield_image = nil
			end
			def_takable_on.description = S("@1 (active)", def.description)
			if def_spec.__drop then
				def_takable_on.drop = def_spec.__drop.."_takable"
			elseif not options.keep_state_on_take then
				def_takable_on.drop = real_basename.."_takable"
			end
			def_takable_on._lzr_inactive = real_basename.."_takable"
			def_takable_on._lzr_active = real_basename.."_takable_on"

			if options.register_color == false then
				def_takable_on._lzr_fixed = real_basename.."_fixed_on"
				if options.allow_rotate then
					def_takable_on._lzr_rotatable = real_basename.."_rotatable_on"
				end
				def_takable_on._lzr_takable = real_basename.."_takable_on"

				minetest.register_node(basename.."_takable_on", def_takable_on)
			else
				for c=1, lzr_globals.MAX_COLORCODE do
					local def_takable_on_c = table.copy(def_takable_on)
					--~ Annotation for a block @1. @2 is a color core uniquely identifying this block's laser color
					def_takable_on_c.description = S("@1 (active, @2)", def.description, lzr_laser.dirstring_to_colstring(tostring(c)))
					for t=1, #def_takable_on_c.tiles do
						if def_takable_on_c.tiles[t] == lzr_laser.LASER_TILE then
							def_takable_on_c.tiles[t] = colortiles[c]
						end
					end

					def_takable_on_c.groups.laser_block_color = c

					def_takable_on_c._lzr_fixed = real_basename.."_fixed_on_"..c
					if options.allow_rotate then
						def_takable_on_c._lzr_rotatable = real_basename.."_rotatable_on_"..c
					end
					def_takable_on_c._lzr_takable = real_basename.."_takable_on_"..c

					minetest.register_node(basename.."_takable_on_"..c, def_takable_on_c)
				end
			end
		end
	end
end

