Official Morphing Grid API
This is the official documentation on the morphinggrid api. This api can be used to create power rangers.

I. Required setup for a Power Rangers mod
	A. Registering a ranger type.
		--You must do this otherwise errors will be thrown in many circumstances
		--It basically registers the team that the rangers in the mod will belong to.
		--Currently, there can only be one ranger type for each mod.
		morphinggrid.register_rangertype("modname", {
			description = "Ranger team name, I.E. Mighty Morphin",
			weapons = {"modname:weaponone", "modname:weapontwo"} --list of all weapons used by the team
		})

	B. Registering a ranger.
		--This shows an example of how to register a ranger.
		morphinggrid.register_ranger("modname:red_ranger", {
			description = "Red Ranger",
			heal = 100, --The amount that the armor heals
			use = 100, --The number used to calculate the amount of times the armor can be hit before your armor gets destroyed. hit times = 65535 / use.
			color = "red", Defines which color the ranger is. Can currently choose from green, white, black, pink, blue, yellow, red, silver, and gold.
			weapons = {"modname:weaponone", "modname:weapontwo"}, --A list of weapons that belongs to the specified ranger.
			ranger_groups = {}, --Add leader if the specified ranger is the leader.
		
			-- You can create a morpher here. This is optional but if you do not create one here you will have to create one using morphinggrid.register_morpher().
			morpher = {
					type = "craftitem",
					name = "modname:red_ranger_morpher",
					inventory_image = "red_ranger_morpher.png",
					description = "Red Ranger Morpher",
		  
				--create a recipe for the morpher here if desired
				recipe = {
					type="shapeless",
					recipe = {"morphinggrid:energy", "default:steel_ingot"}
				},
			}
		})
    	
		--More options for registering a ranger
			
			--Additional Fields
				has_connection = <bool> --Determines wether the morpher is connected to the morphing grid. An empty morpher's definition would have this set to false.
				prevents_respawn = <bool> --By default, it is false. Setting it to true will prevent the player from respawning if killed.
				after_morph = <function(player, morph_info)> --do something when a player morphs into this ranger.
				after_demorph = <function(player, demorph_info)> --do something when a player demorphs from being this ranger.
				
				--default contains auto generated filenames. Whatever filenames are not specified will still be auto generated. Do not include < or > in the filenames.
				armor_textures = {
					helmet = { armor="<filename>", preview="<filename>", inventory="<filename>" },
					chestplate = { armor="<filename>", preview="<filename>", inventory="<filename>" },
					leggings = { armor="<filename>", preview="<filename>", inventory="<filename>" },
					boots = { armor="<filename>", preview="<filename>", inventory="<filename>" }
				}

				
				--This is an optional field. The commands specified here will be useable when using the chat command '/ranger <command>' when morphed.
				ranger_commands = {
					command_name = {
						description = "An example of a command that displays a chat command.",
						params = "<text>",
						func = function(name, text) --'name' is the name of the player. 'text' is the input that the player gives.
							return true, name.." said: "..text, itemstack --You can return the bool result, and a message.
						end
					}
				},

				
				--By default, if you do not specify this, right clicking the morpher will morph the ranger.
				--If you want to do something special, such as only morph if a condition is true,
				--you can put a function here and this is what happens instead when you
				--right click the morpher.
				--This is an example of how you would morph with the morph_func_override
				morph_func_override = function(user)
					local mycondition = true
					if mycondition then
						local ranger = morphinggrid.get_ranger("modname:red_ranger") --get the rangers definition from it's registration name
						morphinggrid.morph(user, ranger) --morph the player
					end
				end
			
			--Groups
				leader = 1 --lets the morphinggrid know that the specified ranger is the leader of it's group.
				hidden = 1 --prevents the ranger from being listed openly. This is sort of the same as not having an item show in the creative inventory.
			
II. Registrations (Look above for 'ranger' registration.)
	
	A. Registering a morpher.
		--morphers allow players to morph into (or 'become') a power ranger. Normally, each morpher would connect to a specific ranger within the morphinggrid, but custom functionality is possible.
		--this is an example of how to register a morpher. The table can be built the same way as any craftitem or tool which means you can also use fields accepted by their definitions.
		morphinggrid.register_morpher("modname:red_morpher", {
			type = "craftitem", --determines what the morpher derives from. can also be "tool". It is "craftitem" by default, if this field is not entered.
			description = "Red Morpher",
			ranger = "modname:red" --The ranger this morpher morphs the player into. This is not needed if using 'morph_func_override', otherwise it is required.
			
			--This field is optional. It overrides the default function that happens when the morpher is left clicked.
			--Without it, by default, the morpher will morph the player to the ranger in the 'ranger' field.
			--If you use this field, the 'ranger' field can by nil. But it is still good to keep the 'ranger' field so it is documnted.
			morph_func_override = function(user, itemstack)
				morphinggrid.morph(user, "modname:red") --You will need to manually morph the player while using this function.
				return itemstack --This is optional. Return an itemstack to modify the morpher. Using the on_use() field will override this if it's return value is not nil.
			end
		})
		
		
		--Additinal fields
		
		register_griditem = <boolean> --Privides the ability to also register the item as a griditem if desired.
		morpher_slots = <table> --If registered, an extra command called 'slots' will be added to morpher_commands. 'slots' allows player to esssetially insert objects into their morphers. For example - putting a powercoin into your morpher and taking it out.
		morpher_commands = <table> --A list of commands that can be executed per individual morpher. The table is built almost just like a chatcommand definition but it does not have a
						--privs field.
		

		--Additional field's usage
		
		--registers a morpher as a griditem
		regsiter_griditem = true

		
		--registers morpher slots functionality
		--this is an example of a morpher that has a powercoin, and what happens when it is removed.
		morpher_slots = {
			amount = 1, --the amount of stack slots available
			--'input_load' should return a boolean type determining if the operation failed or not, then a table of the itemstacks that are loaded when the formspec appears or is refreshed, in the order that they appear.
			load_input = function(morpher) --'morpher' is the itemstack of the morpher of that the player is executing the command from.
				return true, {ItemStack("mighty_morphin:mastodon_powercoin")}
			end
			--'output' should return a boolean type determining if the operation failed or not (if it is true, the formspec will be refreshed) then, the new itemstack of the morpher of that the player is executing the command from.
			output = function(itemstack, slots) --'itemstack' is the itemstack of the executed morpher command, 'slots' is a table of ItemStacks.
				if slots[1]:get_name() == "" then --checks if there is no item.
					return true, ItemStack("mighty_morphin:empty_morpher") --the current morpher will be replaced with an empty one
				end
				return false, itemstack
			end
		}

		
		--The command's name is the name of a table inside the 'commands' table below.
		--'help' is a pre-existing command that cannot be overrided.
		morpher_commands = {
			command_name = {
				description = "An example of a command that displays a chat command.",
				params = "<text>",
				func = function(name, text, itemstack) --'name' is the name of the player. 'text' is the input that the player gives. 'itemstack' is an itemstack of the morpher.
					return true, name.." said: "..text, itemstack --You can return the bool result, a message, and an itemstack. Returning an itemstack replaces the morpher that
																  --you are executing the command from. It could either be the morpher you are holding, or the morpher in your
																  --'morphers' inventory that is in the single slot.
				end
			}
		}
	
	B. Regisering a girditem (formally called 'Grid Item')
		--griditems are supposed to be what connects to the morphinggrid.
		--they usually contain energy that links to the Morphing Grid so a player can morph into a specific ranger.
		--griditems can be tools, craftitems, or nodes.
		--this is an example of how to register a griditem.
		morphinggrid.register_griditem("mighty_morphin:mastodon_powercoin", {
			type = "craftitem",
			description = "Mastodon Powercoin",
			inventory_image = "mastodon_powercoin.png",
		})

		--Additional fields
			
			griditem_commands = <table> --A list of commands that can be executed per individual griditem. The table is built almost just like a chatcommand definition but it does not have a
						--privs field.


		--Additional field's usage
		
		--The command's name is the name of a table inside the 'commands' table below.
		--'help' is a pre-existing command that cannot be overrided.
		griditem_commands = {
			command_name = {
				description = "An example of a command that displays a chat command.",
				params = "<text>",
				func = function(name, text, itemstack) --'name' is the name of the player. 'text' is the input that the player gives. 'itemstack' is the itemstack of the griditem.
					return true, name.." said: "..text, itemstack --You can return the bool result, a message, and an itemstack. Returning an itemstack replaces the griditem that
											--you are executing the command from.
				end
			}
		}
		
			
II. Weapons/Arsenal
	
	A. Registering a firearm, tool, craftitem, or node as a weapon so it can be summoned.
		--this table can be added to any firearm, tool, craftitem, or node definition.
		--if you are registering a firearm using 'morphinggrid.register_firearm()' you still must add this table.
		--example
		minetest.register_tool("modname:item_name", {
			description = "Weapon Name",
			...
			--Add this table to your firearm, tool, craftitem, or node definition.
			ranger_weapon = {
				weapon_key = "desired_weapon_keyword", --enter the desired weapon key that will be entered while typing the chat command '/summon_weapon <weapon_key>'
				rangers = { "modname:red_ranger" } --a list of rangers this weapon belongs to. Of coarse there can be multiple rangers.
			}
		})
		
		
		--Additional fields
		
		required_weapons = <table> --table of item strings of registered weapons which are required to summon the weapon.
		ignore_requirments_on_can_summon = true --By default this value is true. If set to false, the 'can_summon' field will be only be executed if the player is morphed into the
							--correct ranger and has the required weapons in their inventory.
		--By default, you can summon the weapon if you are morphed as the listed rangers but if you want to have a custom reason
		--that determines if it can be summoned or not, add 'can_summon' to the 'ranger_weapon' table and make it equel to a
		--function. There must be a return value of true or false.
		can_summon = <function> --By default this function overrides the 'rangers' field and you need to check if the player is morphed into the desired ranger to summon the weapon.
					--To change this behavior, see the 'ignore_requirments_on_can_summon' field.
					
		--Additional field's usage
		required_weapons = { "modname:weapon_name_1", "modname:weapon_name_2" } --add as many as you want. If there is nothing here then the weapon will be summoned.
		ignore_requirments_on_can_summon = false
		can_summon = function(player, ranger) --<player> = player summoning the weapon, <ranger> = definition of the players current morph status. If not morphed it is nil.
			return true --return true to summon, false to not summon.
		end
		
	B. Registering a firearm.
		
		--This is an example of how to register a firearm. A firearm is also a registered tool so any field that is used in a tool's definition will also work here.
		morphinggrid.register_firearm("modname:item_name", {
			description = "Weapon Name",
			distance = 30, --Maximum distance that a shot can be fired.
			--tool_capabilities are required otherwise an exception will be thrown when shooting the firearm. Here is everything needed.
			tool_capabilities = {
				full_punch_interval = 1,
				damage_groups = { fleshy = 10 } --The amount of 'fleshy' is the amount of hp that will be taken away from the target. fleshy is required.
			}
		})
		
		--Additional fields
		
		particle_texture = <filename> --The texture that is used for the default particle.
		particle_override = <function> --Overrides the default function that contains 'minetest.add_particlespawner()' so you can create your own particles.
		on_shot_fired = <function> --Happens when a shot is fired.
		
		--Additional field's usage
		particle_texture = "filename.png"
		particle_override = function(player, ranger) --<player> = player firing the firearm, <ranger> = definition of the players current morph status. If not morphed it is nil.
			--minetest.add_particlespawner(<particle_spawner_definition>)
		end
		on_shot_fired = function(itemstack, player, pointed_thing, shot_successful) --<item_stack> = item stack, <player> = player who shot the firearm, <pointed_thing> = object that got shot, <shot_successful> = returns true if the shot was successful, false if not.
			
		end
  
III. Methods
	
	morphinggrid.demorph(player, demorph_settings) --demorphs a player. player = player ref. Returns two values; bool, demorph_info
	morphinggrid.get_morph_status(player) --returns the ranger name (i.e. 'modname:red_ranger') that the specified player is currently morphed as. returns nil if not morphed.
	morphinggrid.get_ranger(ranger_name) --returns the definition of a specified ranger. rangername = modname:ranger.
	morphinggrid.get_ranger_group(ranger_name, ranger_group) --returns the amount of the specified group. ranger_name = <string>, ranger_group = <string>.
	morphinggrid.get_rangertype(name) --returns the definition of a ranger type. name = modname
	morphinggrid.get_registered_rangers() --returns table of all registered ranger definitions.
	morphinggrid.get_registered_rangertypes() --returns a table of all registered ranger type definitions.
	morphinggrid.morph(player, ranger, morph_settings) --morphs a player. player = player ref, ranger = ranger definition. Returns two values; bool, morph_info
	morphinggrid.register_after_demorph(<function(player, ranger_name, demorph_info)>) --do something after a player demorphs.
	morphinggrid.register_after_morph(<function(player, ranger_name, morph_info)>) --do something after a player morphs.
	morphinggrid.register_firearm(name, firearm_definition) --register a firearm
	morphinggrid.register_griditem(name, griditem_definition) --register a griditem
	morphinggrid.register_morpher(name, morpher_definition) --register a morpher
	morphinggrid.register_ranger(name, ranger_definition) --register a ranger
	morphinggrid.register_rangertype(name, rangertype_definition) --register a ranger type
	
IV. Table Examples
	
	--You can optionally input this when using 'morphinggrid.morph'. All fields are optional.
	morph_settings = {
		morpher = <morpher_item_string> --So morph_info will know what morpher was used to morph the player. This can be empty. (*Deprecated. Use 'itemstack' instead)
		itemstack = <ItemStack> --Allows morph_info to know the ItemStack used, and for other features. This is highly reccomended but not required.
		hide_identity = <bool> --Set to true or false to show or hide the player's nametag.
		priv_bypass = <bool> --Set to true to bypass the requirment for the 'power_rangers' priv.
		chat_messages = <bool>, --If false, no chat messages will be shown.
		log_this = <bool> --if false, this morph will not be logged.
	}
	
	--You can optionally input this when using 'morphinggrid.demorph'. All fields are optional.
	demorph_settings = {
		voluntary = true, --Set this to true if the player intentionally wants to demorph.
		priv_bypass = <bool> --Set to true to bypass the requirment for the 'power_rangers' priv.
		chat_messages = <bool>, --If false, no chat messages will be shown.
		log_this = <bool> --If false, this demorph will not be logged.
	}
	
	--This is the second return value when using 'morphinggrid.morph'.
	morph_info = {
		type = "morph" --this will always be 'morph'. morph_info and demorph_info are logged in the same table so this can be used to determine if it is a morph or demorph.
		timestamp = <string> --shows what time the action happened.
		player = <player_obj_ref> --the player that did the action.
		ranger = <ranger_def> --the ranger that was morphed into
		pos = <pos> --the position that the player was at when the action took place.
		itemstack = <ItemStack> --the ItemStack that was used to morph, if there was one.
		reason = <string> --this can either be 'successful', 'not_stable', or 'no_permission'. 
			--'successful' returns when everything went right.
			--'not_stable' returns when the player could'nt morph becuase the powers were not stable.
			--'no_permission' returns when the player tried to morph without permission.
	}
	
	--This is the second return value when using 'morphinggrid.demorph'.
	demorph_info = {
		type = "demorph" --this will always be 'demorph'. morph_info and demorph_info are logged in the same table so this can be used to determine if it is a morph or demorph.
		timestamp = <string> --shows what time the action happened.
		player = <player_obj_ref> --the player that did the action.
		ranger = <ranger_def> --the ranger that was morphed into
		pos = <pos> --the position that the player was at when the action took place.
		reason = <string> --this can either be 'successful', 'not_stable', or 'no_permission'. 
			--'successful' returns when everything went right.
			--'not_stable' returns when the player demorphed because their powers were unstable.
			--'no_permission' returns when the player tried to demorph without permission.
	}
	
V. Glabal Tables
	
	morphinggrid.registered_rangertypes --Map of all ranger types indexed by name
	morphinggrid.registered_rangers --Map of all rangers indexed by name
	morphinggrid.registered_morphers --Map of all morphers indexed by same name as their item string name.
	morphinggrid.registered_weapons --Map of all weapons ('ranger_weapon' tables) indexed by their item string name.
	morphinggrid.registered_firearms --Map of all firearms indexed by their item string name.
	morphinggrid.morphing_log --Log of all morphs and demorphs since server startup.