-- Register infobooks

local S = minetest.get_translator("lzr_infobooks")

-- This help category is for blocks that interact with lasers and
-- have an active and inactive state.
local laser_blocks = {
	{ "lzr_laser:laser_001", false, S("Laser"), S("Lasers are beams of light that extend in a straight line until they collide with a block or are deflected. Lasers can interact with many different blocks and come in seven different colors.") },
	{ "lzr_laser:emitter_red_takable", nil, S("Emitter"), S("This block emits a laser when turned on. Emitters come in different colors.") },
	{ "lzr_laser:detector_takable", nil, S("Detector (colorless)"), S("This block has a hole and will be activated when a laser goes into it. It may then trigger something.") },
	{ "lzr_laser:detector_red_takable", nil, S("Detector (colored)"), S("Like the colorless detector, it will be activated when a laser goes into the hole. But colored detectors require the laser to be of the same color.") },
	{ "lzr_laser:mirror_takable", nil, nil, S("When a laser hits the mirror, it will make a turn.") },
	{ "lzr_laser:double_mirror_00_takable", "lzr_laser:double_mirror_10_takable", nil, S("The double mirror has a reflective surface on two sides, so it can deflect two lasers at once.") },
	{ "lzr_laser:transmissive_mirror_00_takable", "lzr_laser:transmissive_mirror_01_takable", nil, S("When a laser hits the angled part of a beam splitter, it splits it into two. One part of the laser goes straight through while the other part will be deflected.") .."\n" .. S("Shooting a laser at the backside of this block won’t to anything.") },
	{ "lzr_laser:crystal_takable", "lzr_laser:crystal_takable_on_1", nil, S("When a laser goes into a crystal, it will spread in all directions.") },
	{ "lzr_laser:pillar_crystal_takable", "lzr_laser:pillar_crystal_takable_on_1", nil, S("Pillar crystals spread lasers in three other directions.") },
	{ "lzr_laser:triangle_crystal_takable", "lzr_laser:triangle_crystal_takable_on_1", nil, S("Triangle crystals spread lasers in two other directions.") },
	{ "lzr_laser:mixer_takable", "lzr_laser:mixer_takable_on_1", nil, S("The mixer has two inputs to the left and right and one output at the front. When two lasers go into the input, a laser with their colors mixed will go out of the output.").."\n"..S("When only one input is used, the same laser will go out of the output unchanged.") },
	{ "lzr_laser:skull_shy_takable", nil, nil, S("Skulls have holes on each side and lasers fired at it will go straight through.").."\n"..S("This skull is called “shy” because it’s afraid of lasers. When any laser touches it, it will become ghostly and cannot be touched or walked on.") },
	{ "lzr_laser:skull_cursed_takable", nil, nil, S("Skulls have holes on each side and lasers fired at it will go straight through.").."\n"..S("The cursed skulls have lost their physical form and cannot be touched or walked on. They can be brought back into the physical realm by firing a laser at it.") },
	{ "lzr_laser:bomb_takable", nil, nil, S("The bomb will blow up cracked stone and other fragile blocks when its fuse has been ignited by a laser or fire. The explosion affects all blocks in an area of 3×3×3 blocks. If the explosion hits another bomb, the other bomb will also explode.") },
	{ "lzr_laser:barricade", "lzr_laser:barricade_on", nil, S("Barricades are flammable and burn up when hit by a laser. They also ignite all flammable blocks and fuses next to them.") },
	{ "lzr_laser:hollow_barrel_takable", nil, nil, S("A simple obstacle with an opening where a laser can go through.") },
}

local lb_pages = {}
for i=1, #laser_blocks do
	local basename = laser_blocks[i][1]
	local nodedef = minetest.registered_nodes[basename]
	if not nodedef then
		error("Missing nodedef for "..basename)
	end
	local title = laser_blocks[i][3] or nodedef.description or "???"
	local text = laser_blocks[i][4] or nodedef._tt_help or "???"
	local item_image
	if laser_blocks[i][2] ~= false then
		item_image = { basename, laser_blocks[i][2] or basename.."_on" }
	else
		item_image = { basename }
	end
	table.insert(lb_pages, { title = title, text = text, item_image = item_image })
end

lzr_infobooks.register_book("laser_blocks", {
	title = S("Laser Blocks"),
	color = "#8a0505",
	pages = lb_pages,
})

-- This help category is for all noteworthy blocks (relevant for gameplay)
-- that don't directly interact with lasers or don't have an active/inactive state.
local other_blocks = {
	{ "lzr_core:stone", "lzr_core:wood", S("Solid Block"), S("Most blocks are solid. This includes stone, wood, tree trunks, furniture, dirt, sand, and more. Solid blocks cannot be destroyed, picked up, rotated or moved.") },
	{ "lzr_core:stone_cracked", "lzr_core:cave_stone_cracked", S("Cracked Stone"), S("Cracked stone will be destroyed by explosions.") },
	{ "lzr_levers:lever_stone_off", "lzr_levers:lever_stone_on", S("Lever"), S("Punch the lever to trigger something.") },
	{ "lzr_laser:crate_takable", nil, nil, S("Unlike the darker heavy crate, a light crate can be picked up and carried around. You can use it to reach higher places.") },
	{ "lzr_laser:crate_old", "lzr_decor:barrel_old", S("Old Wood"), S("Old crates and barrels fall into pieces at the slightest touch. Explosions also destroy them.") },
	{ "lzr_decor:iron_lightbox_off", "lzr_decor:iron_lightbox", S("Light Box"), S("Light boxes create light. They may be turned on or off by detectors.") },
	{ "lzr_decor:cobweb", nil, nil, S("Cobwebs make it impossible to jump while inside them. You also can’t touch anything behind a cobweb.").."\n"..S("Cobwebs can be destroyed with lasers.") },
	{ "lzr_decor:dense_cobweb", nil, nil, S("You cannot jump inside a dense cobweb, nor touch anything behind one.").."\n"..S("Unlike regular cobwebs, dense cobwebs cannot be destroyed with lasers.") },
}
local ob_pages = {}
for i=1, #other_blocks do
	local basename = other_blocks[i][1]
	local basename2 = other_blocks[i][2]
	local nodedef = minetest.registered_nodes[basename]
	if not nodedef then
		error("Missing nodedef for "..basename)
	end
	local nodedef2 = minetest.registered_nodes[basename2]
	if basename2 and not nodedef2 then
		error("Missing nodedef for "..basename2)
	end
	local title = other_blocks[i][3] or nodedef.description or "???"
	local text = other_blocks[i][4] or nodedef._tt_help or "???"
	local item_image
	if basename2 then
		item_image = { basename, basename2 }
	else
		item_image = basename
	end
	table.insert(ob_pages, { title = title, text = text, item_image = item_image })
end

lzr_infobooks.register_book("other_blocks", {
	title = S("Other Blocks"),
	color = "#8a4505",
	pages = ob_pages,
})

lzr_infobooks.register_book("goal", {
	title = S("Goal of the Game"),
	color = "#8a4f05",
	pages = {
		{
			title = S("Find Gold"),
			text = S("The goal of every level is to find all gold blocks."),
			item_image = "lzr_treasure:gold_block",
		},
		{
			title = S("Treasure Chests"),
			text = S("You can find treasure chests all over the world. You can find a gold block inside.").."\n"..
				S("To open an unlocked chest, just punch it."),
			item_image = {"lzr_treasure:chest_wood_unlocked", "lzr_treasure:chest_wood_open_gold"},
		},
		{
			title = S("Breaking the lock"),
			text = S("Many chests are locked. To break the lock, you need to solve a laser puzzle by sending lasers into detectors."),
			item_image = { "lzr_treasure:chest_wood_locked", "lzr_laser:detector_fixed" },
		},
		{
			title = S("Blocks"),
			text = S("There are many blocks that will help or hinder you to reach the goal. Mirrors deflect lasers, mixers mix the laser color, barricades block the way, skulls may create new passages, and so on.").."\n"..
				S("Some blocks can be picked up by punching. Some blocks can only be rotated with a tool.").."\n\n"..
				S("More information about blocks can be found in other books."),
			item_image = { "lzr_laser:mirror_takable", "lzr_laser:barricade" },
		},
	},
})

lzr_infobooks.register_book("pirate_sense", {
	title = S("Pirate Sense"),
	color = "#303030",
	pages =	{
	{
		title = S("Pirate Sense"),
		text = S("The pirate sense reveals the connections between blocks like detectors and chests.").."\n"..
			S("This can be very useful to find important blocks in more complex locations.").."\n"..
			S("The pirate sense works on any block that triggers something, or is triggered."),
		image = "lzr_gui_pirate_sense.png",
	},{
		title = S("How to use"),
		text = S("To use the pirate sense, select your empty hand, then press the [Place] key on the block you want to reveal the connections for.").."\n"..
			S("If there are connections, symbols will appear at that block and all its connections for a brief moment.").."\n"..
			S("The symbols will be shown permanently if you hold down [Sneak] before activating the pirate sense.").."\n\n"..
			S("To remove the symbols, press [Place] with your empty hand into air, or any block that doesn’t have connections."),
		item_image = "lzr_treasure:chest_wood_locked",
	},{
		title = S("Symbols"),
		text = S("When using the pirate sense, you will see the following symbols:").."\n\n"..
			S("Green outgoing arrows stand for blocks that trigger something. For example, detectors.").."\n"..
			S("Red inwards arrows stand for blocks that can be triggered. For example, chests.").."\n"..
			S("A lantern at the corner or edge of the screen is shown while the pirate sense is active."),
		image = { "lzr_triggers_icon_sender.png", "lzr_triggers_icon_receiver.png", "lzr_gui_pirate_sense.png" },
	},
	},
})

lzr_infobooks.register_book("screws", {
	title = S("Screws"),
	color = "#afafaf",
	pages = {
		{
			title = S("Screws"),
			text = S("While you can pick up some blocks, some blocks have screws in their corners. Blocks with screws cannot be picked up."),
			image = "[inventorycube{lzr_laser_fixed.png{lzr_laser_fixed.png{lzr_laser_fixed.png",
		},
		{
			title = S("Screw types"),
			text = S("There are two types of screws:").."\n\n"..
				S("Copper screws (brown): Block can be rotated.").."\n"..
				S("Iron screws (gray): Block cannot be rotated."),
			image = {
				"[inventorycube{lzr_laser_fixed_soft.png{lzr_laser_fixed_soft.png{lzr_laser_fixed_soft.png",
				"[inventorycube{lzr_laser_fixed.png{lzr_laser_fixed.png{lzr_laser_fixed.png",
			},
		},
	},
})

local color_page_texture = "lzr_infobooks_colors.png"
local colormix_page_texture = "lzr_infobooks_colormix.png"
local colormix2_page_texture = "lzr_infobooks_colormix2.png"
do
	local colors = {
		{ "red", lzr_laser.LASER_COLORS[lzr_globals.COLOR_RED] },
		{ "green", lzr_laser.LASER_COLORS[lzr_globals.COLOR_GREEN] },
		{ "blue", lzr_laser.LASER_COLORS[lzr_globals.COLOR_BLUE] },
		{ "yellow", lzr_laser.LASER_COLORS[lzr_globals.COLOR_YELLOW] },
		{ "cyan", lzr_laser.LASER_COLORS[lzr_globals.COLOR_CYAN] },
		{ "magenta", lzr_laser.LASER_COLORS[lzr_globals.COLOR_MAGENTA] },
		{ "white", lzr_laser.LASER_COLORS[lzr_globals.COLOR_WHITE] },
	}
	for c=1, #colors do
		color_page_texture = color_page_texture .. "^(lzr_infobooks_colors_"..colors[c][1]..".png^[multiply:#"..colors[c][2]..")"
		if colors[c][1] ~= "white" then
			colormix_page_texture = colormix_page_texture .. "^(lzr_infobooks_colormix_"..colors[c][1]..".png^[multiply:#"..colors[c][2]..")"
		end
		if colors[c][1] ~= "yellow" and colors[c][1] ~= "magenta" and colors[c][1] ~= "cyan" then
			colormix2_page_texture = colormix2_page_texture .. "^(lzr_infobooks_colormix2_"..colors[c][1]..".png^[multiply:#"..colors[c][2]..")"
		end
	end
end

lzr_infobooks.register_book("laser_color", {
	title = S("Laser Colors"),
	color = "#00b52e",
	pages = {
		{
			title = S("Laser Colors"),
			text = S("Lasers come in 7 different colors: Red, green, blue, yellow, cyan, magenta and white."),
			image = color_page_texture,
		},
		{
			title = S("Color Mixing"),
			text = S("When two lasers overlap or get mixed in a mixer, their colors will be combined.").."\n\n"..
				S("Red + Green → Yellow").."\n"..
				S("Red + Blue → Magenta").."\n"..
				S("Green + Blue → Cyan").."\n"..
				S("Red + Green + Blue → White"),
			image = { colormix_page_texture, colormix2_page_texture },
		},
	},
})

lzr_infobooks.register_book("hook", {
	title = S("Hook"),
	color = "#24948f",
	pages = {
		{
			title = S("Rotating Hook"),
			text = S("The rotating hook allows you to rotate blocks. Not all blocks can be rotated.").."\n\n"..
				S("Use the [Place] or [Punch] key to rotate.").."\n"..
				S("[Punch]: Rotate the block towards the edge you touched.").."\n"..
				S("[Place]: Rotate the block clockwise.").."\n"..
				S("Holding down [Sneak] will rotate in the reverse direction."),
			image = "lzr_hook_hook.png",
		},
		{
			title = S("Rotation arrows"),
			text = S("Arrows will appear on the surface of a block you can rotate. They show the possible rotation directions.").."\n\n"..
				S("Blue edge arrows: Rotation performed with the [Punch] key.").."\n"..
				S("Yellow center arrows: Rotation performed with the [Place] key.").."\n\n"..
				S("This feature can be turned off in the graphics settings."),
			image = { "(lzr_hook_overlay.png^[transformR270)^lzr_hook_overlay_rotate.png" },
		},

	},
})

lzr_infobooks.register_book("chests", {
	title = S("Chests"),
	color = "#884400",
	pages = {
		{
			title = S("Wooden Chest"),
			text = S("Wooden chests may be locked or unlocked. The lock can be broken by triggering the chests with detectors."),
			item_image = { "lzr_treasure:chest_wood_locked", "lzr_treasure:chest_wood_unlocked"},
		},{
			title = S("Onyx Chest"),
			text = S("Onyx chests behave like wooden chests but their lock is more difficult to crack: It may regenerate when it’s no longer being triggered by detectors."),
			item_image = { "lzr_treasure:chest_dark_locked", "lzr_treasure:chest_dark_unlocked"},
		},
	},
})

lzr_infobooks.register_bookshelf("how_to_play", {
	books = {
		"goal",
		"hook",
		"pirate_sense",
	},
})

lzr_infobooks.register_bookshelf("blocks", {
	books = {
		"laser_blocks",
		"chests",
		"other_blocks",
		"laser_color",
		"screws",
	},
})

-- Editor

lzr_infobooks.register_book("editor", {
	title = S("Level Editor"),
	color = "#888888",
	pages = {
		{
			title = S("Introduction"),
			text =
S("Use the level editor to create your own levels.").."\n\n"..
S("Levels created in the level editor will be stored in the world directory and count as custom levels for the special level pack “Single levels”.").."\n\n"..
S("Gold blocks you collect in custom levels do not count towards your loot."),
			item_image = { "lzr_decor:working_table" },
		},
		{
			title = S("Getting started"),
			text =
S("When you enter the editor, you start with an empty room, a large inventory, and a couple of basic tools and items. Hover the items in the inventory with the cursor and read the tooltip to learn what they do.").."\n\n"..

S("In the inventory menu, you can access all items with the “Get items” button, change the level settings and save and load levels.").."\n"..
S("In the “Get items” menu, the first items on page 1 are the most important ones, as those are the level editing tools."),
			item_image = { "lzr_decor:blanket_table" },
		},
		{
			title = S("Structure of a level"),
			text =
S("Each level is a cuboid that occupies a portion of the world. This cuboid is surrounded by boundary blocks for the floor, walls and ceiling.").."\n"..
S("When you look around, you will see 8 white crosses. These are the 8 corners of the level which contain the level contents.").."\n"..
S("The level boundaries will automatically be created when the level is loaded. Level boundaries replace air and water but not solid blocks. Also, water won’t be replaced by air, barrier or rain membrane nodes.").."\n\n"..
S("It’s allowed to choose air as level boundary, allowing the player to leave by walking away.").."\n\n"..
S("The player will start on a special block called “Start Block”, which must be placed somewhere in the level."),

			item_image = { "lzr_laser:crate" },
		},
		{
			title = S("Level settings"),
			text =
S("The level settings can be accessed in the inventory menu. The following settings exist:").."\n\n"..
S("• Name: The name of the level, as shown to the player. Can be empty for untitled levels").."\n"..
S("• Size: Size of the level along the X, Y and Z axes. This excludes the level boundary").."\n"..
S("• Advanced level settings: You can toggle some rarely used settings here (also known as “level flags”)").."\n"..
S("• Wall/Floor/Ceiling block: The itemstrings of the blocks that surround the level at the sides, bottom and top. Press the item buttons to select one of the recommended blocks. In case of an invalid block, the game uses wooden planks").."\n"..
S("• Goldie speech: What Goldie the Parrot will say. This only makes sense if you placed a parrot spawner").."\n"..
S("• Music: Select a music track or disable music").."\n"..
S("• Weather: Visual weather effects (no sounds)").."\n"..
S("• Sky: Affects the sky color, clouds, sun, moon, stars and the time of day").."\n"..
S("• Backdrop: The broader world that surrounds the level, purely for decoration. Ocean, Underground and Sky are at fixed locations. For Islands, you can select a world location with X/Y/Z coordinates").."\n\n"..

S("Warning: If you reduce the level size, blocks outside the new level area will be deleted.").."\n\n"..

S("Hint: To find a good spot for the Islands backdrop, use Fly Mode to fly through the world and debug mode to see your current world coordinates."),
			item_image = { "lzr_tools:ultra_pickaxe" },
		},
		{
			title = S("Level rules"),
			text =
S("Levels need to satisfy some requirements to be playable. The minimal requirements for every level are:").."\n\n"..

S("• Have at least one closed treasure chest (locked or unlocked)").."\n"..
S("• Have a way to obtain all treasures").."\n"..
S("• Have exactly one start block (the player starts here)").."\n"..
S("• Doesn’t contain any disallowed block").."\n"..
S("• Pass other internal validity checks").."\n\n"..

			S("You can perform a validity check with the “/editor_check” command. Levels are also automatically checked for validity each time you save."),


			item_image = { "lzr_teleporter:teleporter_off" },
		},
	}
})

lzr_infobooks.register_book("editor_files", {
	title = S("Files"),
	color = "#888888",
	pages = {
		{
			title = S("File format"),
			text =
S("Every custom level is stored in two files:").."\n\n"..

S("• <name>.mts: Minetest Schematic. This contains all the blocks").."\n"..
S("• <name>.csv: Level metadata, stored as Comma-Separated Values. Contains the level settings (except size) and the trigger information").."\n\n"..

S("The .mts file may also be edited by other tools that support this file format.").."\n\n"..

S("Translations of the level title and parrot speech can not be saved in the level editor. You need to create a level pack, which is far more technical. See LEVEL_PACKS.md in the Lazarr! installation directory."),
			item_image = { "lzr_laser:crate" },
		},
		{
			title = S("Saving and loading"),
			text =
S("To save the level, use the “Save level” button in your inventory.").."\n\n"..

S("Your level files (.mts and .csv) will be saved in the world directory.").."\n"..
S("Warning: If you delete your world, you will also lose all custom levels stored in the world.").."\n\n"..

S("It’s allowed to save unplayable levels. You can load them in the editor, but not play them. Any errors or warnings will appear in the chat.").."\n\n"..

S("Loading the level works in a similar fashion with “Load level”.").."\n\n"..

S("You may also use the commands “/editor_save” and “/editor_load” instead."),
			item_image = { "lzr_decor:bookshelf" },
		},
		{
			title = S("Autosave"),
			text =
S("To prevent loss of work, the editor will automatically save the level into “_AUTOSAVE_” at the following events:").."\n\n"..

S("• Leave the game").."\n"..
S("• Leave the editor").."\n"..
S("• Change level size or boundary nodes").."\n"..
S("• The game crashes (not guaranteed)").."\n\n"..

S("_AUTOSAVE_ will be overwritten whenever one of those events happens, so make sure to save your work into other files."),
			item_image = { "lzr_decor:multishelf" },
		},
	}
})

-- Special book that warns about WorldEdit; appears only if the WorldEdit mod was detected
if minetest.get_modpath("worldedit") then
	lzr_infobooks.register_book("editor_worldedit", {
		title = S("Using WorldEdit"),
		color = "#888888",
		pages = {
			{
				title = S("Using WorldEdit"),
				text =
				S("If you want to use the WorldEdit mod to edit levels, it must be used with care.").."\n\n"..
				S("Using WorldEdit may lead the level to be in an invalid state, which must be fixed manually."),
				item_image = { "lzr_tools:ultra_pickaxe" },
			},
			{
				title = S("Trigger invalidation"),
				text =
				S("If you’ve used WorldEdit commands to change any trigger node, this invalidates the triggers of the level, leading to an invalid state and the level might be saved with incorrect triggers. Use the “/editor_check“ command to check if the level is still valid.").."\n\n"..

				S("If the triggers have become invalid, you must manually call the command “/reset_triggers”. This puts the level in a valid state, but you lose all trigger information, so you have to manually add the triggers back."),
				item_image = { "lzr_decor:bonfire" },
			},
		}
	})
end

local list_sender_types = ""
local icons_sender_types = {}
local annotations_sender_types = {}
for s=0, lzr_triggers.MAX_SENDER_TYPE do
	list_sender_types = list_sender_types .. S("• @1: @2", lzr_triggers.SENDER_TYPE_NAMES[s], lzr_triggers.SENDER_TYPE_DESCRIPTIONS[s])
	if s < lzr_triggers.MAX_SENDER_TYPE then
		list_sender_types = list_sender_types .. "\n"
	end
	table.insert(icons_sender_types, lzr_triggers.SENDER_TYPE_ICONS[s])
	table.insert(annotations_sender_types, lzr_triggers.SENDER_TYPE_NAMES[s])
end

local list_receiver_types = ""
local icons_receiver_types = {}
local annotations_receiver_types = {}
for s=0, lzr_triggers.MAX_RECEIVER_TYPE do
	list_receiver_types = list_receiver_types .. S("• @1: @2", lzr_triggers.RECEIVER_TYPE_NAMES[s], lzr_triggers.RECEIVER_TYPE_DESCRIPTIONS[s])
	if s < lzr_triggers.MAX_RECEIVER_TYPE then
		list_receiver_types = list_receiver_types .. "\n"
	end
	table.insert(icons_receiver_types, lzr_triggers.RECEIVER_TYPE_ICONS[s])
	table.insert(annotations_receiver_types, lzr_triggers.RECEIVER_TYPE_NAMES[s])
end


lzr_infobooks.register_book("editor_triggers", {
	title = S("Triggers"),
	color = "#888888",
	pages = {
		{
			title = S("Overview"),
			text =
S("Triggers are wireless connections between blocks and are very important to proceed in levels.").."\n\n"..

S("Triggers are used for many things, but most importantly, they’re used to break the lock of locked chests.").."\n\n"..

S("These connections can be revealed with your pirate sense but there’s more going on behind the scenes.").."\n\n"..

S("The most basic way to add a trigger is to make a detector send signals to a locked chest. Once the detector is triggered, it breaks the lock. But triggers can be more complex."),
			image = { "lzr_triggers_icon_sender_receiver.png" },
		},
		{
			title = S("Quick start"),
			text =
S("Here’s a quick example on how to add a simple trigger into your level:").."\n\n"..

S("1. Place a detector and a locked chest").."\n"..
S("2. Get yourself a trigger tool and wield it").."\n"..
S("3. Make sure that “Sender Mode” is active of that tool (you can switch modes with the [Place] key)").."\n"..
S("4. Punch the detector (there should be now a message and a symbol at the locked chest)").."\n"..
S("5. Switch to Receiver Mode").."\n"..
S("6. Punch the chest").."\n\n"..

S("Now when the detector is activated by a laser, it will send a signal to the locked chest, causing it to break open in the game.").."\n\n"..
S("For convenience, the lock of wooden chests won’t actually break when triggered in the editor, so you don’t have to reset the chest when testing."),
			item_image = { "lzr_laser:trigger_tool" },
		},
	{
		title = S("Definition"),
		text =
S("Triggers are blocks that send signals towards other blocks to make them do something. Signals are instantaneous and wireless.").."\n\n"..

S("There are two types of triggers: Senders and receivers. Senders send signals to receivers. A sender can send to multiple receivers at once, and a receiver can receive signals from multiple senders at once.").."\n\n"..

S("These are the senders:").."\n"..
S("• Detector: May send signals to receivers when toggled").."\n"..
S("• Lever: May send signals to receivers when pulled").."\n\n"..

S("These are the receivers:").."\n"..
S("• Emitter: May toggle the laser when it receives a signal").."\n"..
S("• Locked chest: May break the lock when it receives a signal.").."\n"..
S("• Light box: May toggle the light when it receives a signal").."\n"..
S("• Locked door/trapdoor: May break when it receives a signal"),
		image = { "lzr_triggers_icon_sender.png", "lzr_triggers_icon_receiver.png" },
		image_annotations = { S("Sender"), S("Receiver") },
	},
	{
		title = S("Signal types, sender types and receiver types"),
		text =
S("Signals can have one of 3 possible types: ON, OFF and TOGGLE.").."\n\n"..

S("Signals may appear as colored particles between sender and receiver (note: these particles may be disabled in the game settings). ON is a solid blue particle. OFF is a circular orange particle. TOGGLE is a purple particle that combines both shapes.").."\n\n"..

S("Each sender has a sender type, which determines which signal type (ON, OFF or TOGGLE) is sent (if any) to its receivers, and when. The type of signal depends on the sender’s state (active or inactive).").."\n\n"..

S("Also, each receiver has a receiver type, which determines how it will react to an incoming signal."),
		image = { "lzr_laser_particle_signal_on.png", "lzr_laser_particle_signal_off.png", "lzr_laser_particle_signal_toggle.png" },
		image_annotations = {
			--~ Signal type
			S("ON"),
			--~ Signal type
			S("OFF"),
			--~ Signal type
			S("TOGGLE"),
		},
	},
	{
		title = S("Sender types"),
		text =
S("The default sender type is “Synchronized”. If the sender was activated, it sends an ON signal. If it was deactivated, it sends an OFF signal. Thus “synchronizing” the sender with the receiver. It also has a special meaning in combination with the “Synchronized” receiver types.").."\n\n"..

S("These are all available sender types:").."\n\n"..

list_sender_types,

		image = icons_sender_types,
		image_annotations = annotations_sender_types,
	},
	{
		title = S("Receiver types"),
		text =
S("The available receiver types are:").."\n\n"..

list_receiver_types.."\n\n"..

S("“Any” is the default receiver type. The receiver will react on any incoming signal. An ON signal activates the receiver, OFF deactivates it and TOGGLE toggles it.").."\n"..

S("“Synchronized AND” is useful if you require a number of detectors to be activated (or deactivated) at the same time. Just make sure that all senders are of type “Synchronized” or “Synchronized inverted”.").."\n\n"..

S("Note that all receiver types are compatible with all sender types, but not all combinations may be useful."),
		image = icons_receiver_types,
		image_annotations = annotations_receiver_types,
	},
}})

local editor_books = {
	"editor",
	"editor_triggers",
	"editor_files",
}
if minetest.get_modpath("worldedit") then
	table.insert(editor_books, "editor_worldedit")
end

lzr_infobooks.register_bookshelf("editor", {
	books = editor_books,
})
