code:
package com.lilithsthrone.game.character.body.types;

import java.util.ArrayList;
import java.util.List;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.valueEnums.CoveringModifier;
import com.lilithsthrone.game.character.body.valueEnums.CoveringPattern;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.ColourListPresets;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.0
 * @version 0.2.11
 * @author Innoxia
 */
public enum BodyCoveringType {

	// Skin shades go light->dark

	HUMAN(BodyCoveringTemplateFactory.createTopSkin(
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.FRECKLED,
					CoveringPattern.FRECKLED_FACE),
			Colour.humanSkinColours)),

	FOX_FUR("a layer of",
			false,
			"fur",
			"fur",
			Util.newArrayListOfValues(
					CoveringModifier.FLUFFY,
					CoveringModifier.SMOOTH),
			null,
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.MARKED),
			CoveringPattern.allHairCoveringPatterns,
			Colour.naturalFurColours,
			Colour.allCoveringColours,
			Colour.naturalFurColours,
			Colour.allCoveringColours),

	ANGEL(BodyCoveringTemplateFactory.createTopSkin(
			Util.newArrayListOfValues(CoveringPattern.NONE),
			Colour.humanSkinColours)),

	ANGEL_FEATHER("a layer of",
			true,
			"feathers",
			"feather",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			CoveringPattern.allHairCoveringPatterns,
			Util.newArrayListOfValues(Colour.COVERING_WHITE),
			Util.mergeLists(Colour.dyeFeatherColours, Colour.naturalFeatherColours),
			Util.newArrayListOfValues(Colour.COVERING_WHITE),
			Util.mergeLists(Colour.dyeFeatherColours, Colour.naturalFeatherColours)),

	DEMON_COMMON(BodyCoveringTemplateFactory.createTopSkin(
			Util.newArrayListOfValues(CoveringPattern.NONE),
			Colour.demonSkinColours)),

	DEMON_FEATHER("a layer of",
			true,
			"feathers",
			"feather",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			CoveringPattern.allHairCoveringPatterns,
			Util.newArrayListOfValues(Colour.COVERING_BLACK),
			Util.mergeLists(Colour.dyeFeatherColours, Colour.naturalFeatherColours),
			Util.newArrayListOfValues(Colour.COVERING_BLACK),
			Util.mergeLists(Colour.dyeFeatherColours, Colour.naturalFeatherColours)),

	BAT_SKIN(BodyCoveringTemplateFactory.createBottomSkin(Colour.humanSkinColours)),

	BAT_FUR(BodyCoveringTemplateFactory.createFurSkin(
			Util.newArrayListOfValues(CoveringModifier.SHORT),
			Util.newArrayListOfValues(CoveringPattern.NONE))),

	CANINE_FUR(BodyCoveringTemplateFactory.createFurSkin(
			Util.newArrayListOfValues(
					CoveringModifier.FLUFFY,
					CoveringModifier.SHORT,
					CoveringModifier.SHAGGY),
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.MARKED,
					CoveringPattern.SPOTTED))),

	LYCAN_FUR(BodyCoveringTemplateFactory.createFurSkin(Util.newArrayListOfValues(CoveringModifier.SHAGGY), null)),

	FELINE_FUR(BodyCoveringTemplateFactory.createFurSkin(
			Util.newArrayListOfValues(
					CoveringModifier.SMOOTH,
					CoveringModifier.SHORT,
					CoveringModifier.FLUFFY),
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.MOTTLED,
					CoveringPattern.SPOTTED,
					CoveringPattern.MARKED,
					CoveringPattern.STRIPED,
					CoveringPattern.HIGHLIGHTS))),

	SQUIRREL_FUR(BodyCoveringTemplateFactory.createFurSkin(Util.newArrayListOfValues(CoveringModifier.SMOOTH), null)),

	RAT_SKIN(BodyCoveringTemplateFactory.createBottomSkin(Colour.ratSkinColours)),

	RAT_FUR(BodyCoveringTemplateFactory.createFurSkin(Util.newArrayListOfValues(CoveringModifier.SMOOTH), null)),

	RABBIT_FUR(BodyCoveringTemplateFactory.createFurSkin(Util.newArrayListOfValues(CoveringModifier.SMOOTH), null)),

	HORSE_HAIR("a layer of",
			false,
			"hair",
			"hair",
			Util.newArrayListOfValues(CoveringModifier.SHORT),
			null,
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.MOTTLED,
					CoveringPattern.SPOTTED,
					CoveringPattern.MARKED,
					CoveringPattern.STRIPED),
			CoveringPattern.allHairCoveringPatterns,
			Colour.naturalFurColours,
			Colour.allCoveringColours,
			Colour.naturalFurColours,
			Colour.allCoveringColours),

	REINDEER_FUR(BodyCoveringTemplateFactory.createFurSkin(
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			Util.newArrayListOfValues(CoveringPattern.NONE))),

	BOVINE_FUR(BodyCoveringTemplateFactory.createFurSkin(
			Util.newArrayListOfValues(
					CoveringModifier.SHORT,
					CoveringModifier.SMOOTH),
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.MOTTLED,
					CoveringPattern.SPOTTED,
					CoveringPattern.MARKED))),

	DILDO("a layer of", // This colour is set in GameCharacter's getCovering method, based on the colour of the dildo equipped.
			false,
			"silicone",
			"silicone",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			null,
			null,
			ColourListPresets.ALL,
			null,
			ColourListPresets.ALL,
			null),

	PENIS(BodyCoveringTemplateFactory.createPenisSkin()),

	ANUS(BodyCoveringTemplateFactory.createOrificeSkin(CoveringPattern.ORIFICE_ANUS)),

	MOUTH(BodyCoveringTemplateFactory.createOrificeSkin(CoveringPattern.ORIFICE_MOUTH)),

	NIPPLES(BodyCoveringTemplateFactory.createOrificeSkin(CoveringPattern.ORIFICE_NIPPLE)),

	NIPPLES_CROTCH(BodyCoveringTemplateFactory.createOrificeSkin(CoveringPattern.ORIFICE_NIPPLE)),

	VAGINA(BodyCoveringTemplateFactory.createOrificeSkin(CoveringPattern.ORIFICE_VAGINA)),

	FIRE(BodyCoveringTemplateFactory.createElemental("flames", CoveringModifier.BLAZING,
					Colour.COVERING_ORANGE,
					Colour.COVERING_BLUE_LIGHT)),

	FIRE_HAIR(BodyCoveringTemplateFactory.createElemental("flames", CoveringModifier.BLAZING,
			Colour.COVERING_ORANGE,
			Colour.COVERING_BLUE_LIGHT)),

	WATER(BodyCoveringTemplateFactory.createElemental("water", CoveringModifier.SHIMMERING,
			Colour.COVERING_BLUE,
			Colour.COVERING_BLUE_LIGHT)),

	WATER_HAIR(BodyCoveringTemplateFactory.createElemental("water", CoveringModifier.SHIMMERING,
			Colour.COVERING_BLUE,
			Colour.COVERING_BLUE_LIGHT)),

	ICE(BodyCoveringTemplateFactory.createElemental("ice", CoveringModifier.SHIMMERING, Colour.COVERING_BLUE_LIGHT)),

	ICE_HAIR(BodyCoveringTemplateFactory.createElemental("ice", CoveringModifier.SHIMMERING, Colour.COVERING_BLUE_LIGHT)),

	AIR(BodyCoveringTemplateFactory.createElemental("vapours", CoveringModifier.SWIRLING, Colour.COVERING_BLUE_LIGHT)),

	AIR_HAIR(BodyCoveringTemplateFactory.createElemental("vapours", CoveringModifier.SWIRLING, Colour.COVERING_BLUE_LIGHT)),

	STONE(BodyCoveringTemplateFactory.createElemental("stone", CoveringModifier.MATTE, Colour.COVERING_GREY)),

	STONE_HAIR(BodyCoveringTemplateFactory.createElemental("stone", CoveringModifier.MATTE, Colour.COVERING_GREY)),

	RUBBER(BodyCoveringTemplateFactory.createElemental("rubber", CoveringModifier.GLOSSY, Colour.COVERING_BLACK)),

	RUBBER_HAIR(BodyCoveringTemplateFactory.createElemental("rubber", CoveringModifier.GLOSSY, Colour.COVERING_BLACK)),

	ARCANE(BodyCoveringTemplateFactory.createElemental("energy", CoveringModifier.SWIRLING, Colour.COVERING_PINK)),

	ARCANE_HAIR(BodyCoveringTemplateFactory.createElemental("energy", CoveringModifier.SWIRLING, Colour.COVERING_PINK)),

	SLIME(BodyCoveringTemplateFactory.createSlime(CoveringPattern.NONE, CoveringPattern.allStandardCoveringPatterns)),

	SLIME_EYE(BodyCoveringTemplateFactory.createSlime(CoveringPattern.EYE_IRISES,
			Util.newArrayListOfValues(CoveringPattern.EYE_IRISES_HETEROCHROMATIC))),

	SLIME_PUPILS(BodyCoveringTemplateFactory.createSlime(CoveringPattern.EYE_PUPILS,
			Util.newArrayListOfValues(CoveringPattern.EYE_PUPILS_HETEROCHROMATIC))),

	SLIME_SCLERA(BodyCoveringTemplateFactory.createSlime(CoveringPattern.EYE_SCLERA,
			Util.newArrayListOfValues(CoveringPattern.EYE_SCLERA_HETEROCHROMATIC))),

	SLIME_HAIR(BodyCoveringTemplateFactory.createSlime(CoveringPattern.NONE, CoveringPattern.allHairCoveringPatterns)),

	SLIME_BODY_HAIR(BodyCoveringTemplateFactory.createSlime(CoveringPattern.NONE, CoveringPattern.allHairCoveringPatterns)),

	SLIME_ANUS(BodyCoveringTemplateFactory.createSlime(CoveringPattern.ORIFICE_ANUS, null)),

	SLIME_MOUTH(BodyCoveringTemplateFactory.createSlime(CoveringPattern.ORIFICE_MOUTH, null)),

	SLIME_NIPPLES(BodyCoveringTemplateFactory.createSlime(CoveringPattern.ORIFICE_NIPPLE, null)),

	SLIME_NIPPLES_CROTCH(BodyCoveringTemplateFactory.createSlime(CoveringPattern.ORIFICE_NIPPLE, null)),

	SLIME_VAGINA(BodyCoveringTemplateFactory.createSlime(CoveringPattern.ORIFICE_VAGINA, null)),

	SLIME_PENIS(BodyCoveringTemplateFactory.createSlime(CoveringPattern.NONE, CoveringPattern.allStandardCoveringPatterns)),

	FEATHERS("a layer of",
			true,
			"feathers",
			"feather",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.MOTTLED,
					CoveringPattern.SPOTTED,
					CoveringPattern.MARKED,
					CoveringPattern.STRIPED),
			CoveringPattern.allHairCoveringPatterns,
			Colour.naturalFeatherColours,
			Colour.dyeFeatherColours,
			Colour.naturalFeatherColours,
			Colour.dyeFeatherColours),

	ALLIGATOR_SCALES("a layer of",
			true,
			"scales",
			"scale",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			CoveringPattern.allScalesCoveringPatterns,
			Colour.naturalScaleColours,
			Colour.allCoveringColours,
			Colour.naturalScaleColours,
			Colour.allCoveringColours),

	// MISC:

	HORN("a layer of",
			false,
			"keratin",
			"keratin",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			null,
			CoveringPattern.allScalesCoveringPatterns,
			Colour.hornColours,
			Colour.dyeHornColours,
			Colour.hornColours,
			Colour.dyeHornColours),

	ANTLER_REINDEER("a layer of",
			false,
			"velvet",
			"velvet",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			null,
			CoveringPattern.allScalesCoveringPatterns,
			Colour.antlerColours,
			Colour.dyeAntlerColours,
			Colour.antlerColours,
			Colour.dyeAntlerColours),

	TONGUE("a layer of",
			false,
			"skin",
			"skin",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			null,
			Util.newArrayListOfValues(
					CoveringPattern.NONE,
					CoveringPattern.HIGHLIGHTS,
					CoveringPattern.STRIPED,
					CoveringPattern.SPOTTED,
					CoveringPattern.MOTTLED,
					CoveringPattern.MARKED),
			Util.newArrayListOfValues(Colour.ORIFICE_INTERIOR),
			Colour.allSkinColours,
			Util.newArrayListOfValues(Colour.ORIFICE_INTERIOR),
			Colour.allSkinColours),

	// HAIR:

	HAIR_HUMAN(BodyCoveringTemplateFactory.createHeadHair(CoveringModifier.SMOOTH)),

	HAIR_ANGEL(BodyCoveringTemplateFactory.createHeadHair(CoveringModifier.SILKEN)),

	HAIR_FOX_FUR("a layer of",
			false,
			"hair",
			"hair",
			Util.newArrayListOfValues(
					CoveringModifier.FURRY),
			null,
			Util.newArrayListOfValues(
					CoveringPattern.NONE),
			CoveringPattern.allHairCoveringPatterns,
			Colour.naturalHairColours,
			Colour.allCoveringColours,
			Colour.naturalHairColours,
			Colour.allCoveringColours),

	HAIR_DEMON(BodyCoveringTemplateFactory.createHeadHair(CoveringModifier.SILKEN)),

	HAIR_CANINE_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.FURRY)),

	HAIR_LYCAN_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.FURRY)),

	HAIR_FELINE_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.FURRY)),

	HAIR_HORSE_HAIR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.COARSE)),

	HAIR_REINDEER_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.COARSE)),

	HAIR_BOVINE_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.COARSE)),

	HAIR_SQUIRREL_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.FURRY)),

	HAIR_RAT_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.FURRY)),

	HAIR_RABBIT_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.FURRY)),

	HAIR_BAT_FUR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.FURRY)),

	HAIR_HARPY("a plume of",
			true,
			"feathers",
			"feather",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			CoveringPattern.allHairCoveringPatterns,
			null,
			Colour.naturalFeatherColours,
			Colour.dyeFeatherColours,
			Colour.naturalFeatherColours,
			Colour.dyeFeatherColours),

	HAIR_SCALES_ALLIGATOR(BodyCoveringTemplateFactory.createFurHeadHair(CoveringModifier.COARSE)), //Why do alligators have hair?!

	// BODY HAIR:

	BODY_HAIR_HUMAN(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.COARSE)),

	BODY_HAIR_ANGEL(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.SILKEN)),

	BODY_HAIR_DEMON(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.SILKEN)),

	BODY_HAIR_CANINE_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.FURRY)),

	BODY_HAIR_LYCAN_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.FURRY)),

	BODY_HAIR_FOX_FUR("a layer of",
			false,
			"hair",
			"hair",
			Util.newArrayListOfValues(
					CoveringModifier.FURRY),
			null,
			null,
			CoveringPattern.allHairCoveringPatterns,
			Colour.naturalHairColours,
			Colour.allCoveringColours,
			Colour.naturalHairColours,
			Colour.allCoveringColours),

	BODY_HAIR_FELINE_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.FURRY)),

	BODY_HAIR_HORSE_HAIR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.COARSE)),

	BODY_HAIR_REINDEER_HAIR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.COARSE)),

	BODY_HAIR_BOVINE_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.COARSE)),

	BODY_HAIR_SQUIRREL_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.FURRY)),

	BODY_HAIR_RAT_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.FURRY)),

	BODY_HAIR_RABBIT_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.FURRY)),

	BODY_HAIR_BAT_FUR(BodyCoveringTemplateFactory.createBodyHair(CoveringModifier.FURRY)),

	BODY_HAIR_HARPY("a plume of",
			true,
			"feathers",
			"feather",
			Util.newArrayListOfValues(CoveringModifier.FLUFFY),
			null,
			null,
			CoveringPattern.allHairCoveringPatterns,
			Colour.naturalFeatherColours,
			Colour.dyeFeatherColours,
			Colour.naturalFeatherColours,
			Colour.dyeFeatherColours),

	BODY_HAIR_SCALES_ALLIGATOR("a crest of",
			false,
			"scales",
			"scale",
			Util.newArrayListOfValues(CoveringModifier.SMOOTH),
			null,
			null,
			CoveringPattern.allStandardCoveringPatterns,
			Colour.naturalScaleColours,
			Colour.allCoveringColours,
			Colour.naturalScaleColours,
			Colour.allCoveringColours),

	// EYES:

	EYE_HUMAN(BodyCoveringTemplateFactory.createEyeIrisesHeterochromiaNaturallyOccurring()),

	EYE_ANGEL(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_DEMON_COMMON(BodyCoveringTemplateFactory.createEyeIrisesWithCustomColors(
			Colour.naturalDemonIrisColours, Colour.dyeDemonIrisColours, true)),

	EYE_DOG_MORPH(BodyCoveringTemplateFactory.createEyeIrisesHeterochromiaNaturallyOccurring()),

	EYE_LYCAN(BodyCoveringTemplateFactory.createEyeIrisesWithCustomColors(
			Colour.naturalPredatorIrisColours, Colour.dyePredatorIrisColours, true)),

	EYE_FOX_MORPH("a pair of",
			true,
			"eyes",
			"eye",
			Util.newArrayListOfValues(
					CoveringModifier.EYE),
			null,
			Util.newArrayListOfValues(
					CoveringPattern.EYE_IRISES),
			Util.newArrayListOfValues(
					CoveringPattern.EYE_IRISES_HETEROCHROMATIC),
			Colour.naturalPredatorIrisColours,
			Colour.dyePredatorIrisColours,
			Colour.naturalPredatorIrisColours,
			Colour.dyePredatorIrisColours),

	EYE_FELINE(BodyCoveringTemplateFactory.createEyeIrisesWithCustomColors(
			Colour.naturalPredatorIrisColours, Colour.dyePredatorIrisColours, true)),

	EYE_SQUIRREL(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_RAT(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_RABBIT(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_BAT(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_ALLIGATOR_MORPH(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_HORSE_MORPH(BodyCoveringTemplateFactory.createEyeIrisesHeterochromiaNaturallyOccurring()),

	EYE_REINDEER_MORPH(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_COW_MORPH(BodyCoveringTemplateFactory.createEyeIrisesHeterochromiaNaturallyOccurring()),

	EYE_HARPY(BodyCoveringTemplateFactory.createEyeIrises()),

	EYE_PUPILS("a pair of",
			true,
			"pupils",
			"pupil",
			Util.newArrayListOfValues(CoveringModifier.EYE),
			null,
			Util.newArrayListOfValues(CoveringPattern.EYE_PUPILS),
			Util.newArrayListOfValues(CoveringPattern.EYE_PUPILS_HETEROCHROMATIC),
			Colour.naturalPupilColours,
			Colour.dyePupilColours,
			Colour.naturalPupilColours,
			Colour.dyePupilColours),

	EYE_SCLERA("a pair of",
			true,
			"sclerae",
			"sclera",
			Util.newArrayListOfValues(CoveringModifier.EYE),
			null,
			Util.newArrayListOfValues(CoveringPattern.EYE_SCLERA),
			Util.newArrayListOfValues(CoveringPattern.EYE_SCLERA_HETEROCHROMATIC),
			Colour.naturalScleraColours,
			Colour.dyeScleraColours,
			Colour.naturalScleraColours,
			Colour.dyeScleraColours),

	// Fluids:

	CUM("",
			false,
			"cum",
			"cum",
			Util.newArrayListOfValues(CoveringModifier.FLUID),
			null,
			Util.newArrayListOfValues(CoveringPattern.FLUID),
			null,
			Util.newArrayListOfValues(Colour.COVERING_WHITE),
			Util.newArrayListOfValues(
					Colour.COVERING_CLEAR,
					Colour.COVERING_BROWN,
					Colour.COVERING_BLACK,
					Colour.COVERING_RED,
					Colour.COVERING_BLUE,
					Colour.COVERING_PURPLE,
					Colour.COVERING_GREEN),
			null,
			null),

	GIRL_CUM("",
			false,
			"girlcum",
			"girlcum",
			Util.newArrayListOfValues(CoveringModifier.FLUID),
			null,
			Util.newArrayListOfValues(CoveringPattern.FLUID),
			null,
			Util.newArrayListOfValues(Colour.COVERING_CLEAR),
			Util.newArrayListOfValues(
					Colour.COVERING_WHITE,
					Colour.COVERING_BROWN,
					Colour.COVERING_BLACK,
					Colour.COVERING_RED,
					Colour.COVERING_BLUE,
					Colour.COVERING_PURPLE,
					Colour.COVERING_GREEN),
			null,
			null),

	MILK("",
			false,
			"milk",
			"milk",
			Util.newArrayListOfValues(CoveringModifier.FLUID),
			null,
			Util.newArrayListOfValues(CoveringPattern.FLUID),
			null,
			Util.newArrayListOfValues(Colour.COVERING_WHITE),
			Util.newArrayListOfValues(
					Colour.COVERING_CLEAR,
					Colour.COVERING_BROWN,
					Colour.COVERING_BLACK,
					Colour.COVERING_RED,
					Colour.COVERING_BLUE,
					Colour.COVERING_PURPLE,
					Colour.COVERING_GREEN),
			null,
			null),

	// Makeup:

	MAKEUP_BLUSHER("a layer of",
			false,
			"blusher",
			"blusher",
			Util.newArrayListOfValues(CoveringModifier.MAKEUP),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			null,
			Util.newArrayListOfValues(Colour.COVERING_NONE),
			Colour.allMakeupColours,
			null,
			null),

	MAKEUP_EYE_LINER("a layer of",
			false,
			"eye liner",
			"eye liner",
			Util.newArrayListOfValues(CoveringModifier.MAKEUP),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			null,
			Util.newArrayListOfValues(Colour.COVERING_NONE),
			Colour.allMakeupColours,
			null,
			null),

	MAKEUP_EYE_SHADOW("a layer of",
			false,
			"eye shadow",
			"eye shadow",
			Util.newArrayListOfValues(
					CoveringModifier.MATTE,
					CoveringModifier.SPARKLY,
					CoveringModifier.METALLIC),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			null,
			Util.newArrayListOfValues(Colour.COVERING_NONE),
			Colour.allMakeupColours,
			null,
			null),

	MAKEUP_LIPSTICK("a layer of",
			false,
			"lipstick",
			"lipstick",
			Util.newArrayListOfValues(
					CoveringModifier.GLOSSY,
					CoveringModifier.MATTE,
					CoveringModifier.SPARKLY,
					CoveringModifier.METALLIC),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			Util.newArrayListOfValues(
					CoveringPattern.SPOTTED,
					CoveringPattern.STRIPED),
			Util.newArrayListOfValues(Colour.COVERING_NONE),
			Colour.allMakeupColours,
			null,
			Colour.allMakeupColours),

	MAKEUP_NAIL_POLISH_HANDS("a layer of",
			false,
			"nail polish",
			"nail polish",
			Util.newArrayListOfValues(
					CoveringModifier.SMOOTH,
					CoveringModifier.SPARKLY,
					CoveringModifier.METALLIC),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			Util.newArrayListOfValues(
					CoveringPattern.SPOTTED,
					CoveringPattern.STRIPED),
			Util.newArrayListOfValues(Colour.COVERING_NONE),
			Colour.allMakeupColours,
			null,
			Colour.allMakeupColours),

	MAKEUP_NAIL_POLISH_FEET("a layer of",
			false,
			"nail polish",
			"nail polish",
			Util.newArrayListOfValues(
					CoveringModifier.SMOOTH,
					CoveringModifier.SPARKLY,
					CoveringModifier.METALLIC),
			null,
			Util.newArrayListOfValues(CoveringPattern.NONE),
			Util.newArrayListOfValues(
					CoveringPattern.SPOTTED,
					CoveringPattern.STRIPED),
			Util.newArrayListOfValues(Colour.COVERING_NONE),
			Colour.allMakeupColours,
			null,
			Colour.allMakeupColours);

	private String determiner;
	private String namePlural;
	private String nameSingular;
	private List<CoveringModifier> naturalModifiers;
	private List<CoveringModifier> extraModifiers;
	private List<Colour> naturalColoursPrimary;
	private List<Colour> dyeColoursPrimary;
	private List<Colour> naturalColoursSecondary;
	private List<Colour> dyeColoursSecondary;
	private List<Colour> allColours;
	private List<Colour> allPrimaryColours;
	private List<Colour> allSecondaryColours;
	private List<CoveringPattern> naturalPatterns;
	private List<CoveringPattern> dyePatterns;
	private List<CoveringPattern> allPatterns;
	private boolean isDefaultPlural;

	private BodyCoveringType(BodyCoveringTemplate template) {
		determiner = template.determiner;
		namePlural = template.namePlural;
		nameSingular = template.nameSingular;
		naturalModifiers = template.naturalModifiers;
		extraModifiers = template.extraModifiers;
		naturalColoursPrimary = template.naturalColoursPrimary;
		dyeColoursPrimary = template.dyeColoursPrimary;
		naturalColoursSecondary = template.naturalColoursSecondary;
		dyeColoursSecondary = template.dyeColoursSecondary;
		naturalPatterns = template.naturalPatterns;
		dyePatterns = template.dyePatterns;
		isDefaultPlural = template.isDefaultPlural;

		allPatterns = new ArrayList<>();
		allPatterns.addAll(naturalPatterns);
		allPatterns.addAll(dyePatterns);

//		allColours = new ArrayList<>();
//		allColours.addAll(naturalColoursPrimary);
//		allColours.addAll(dyeColoursPrimary);
//		allColours.addAll(naturalColoursSecondary);
//		allColours.addAll(dyeColoursSecondary);
//
//		allPrimaryColours = new ArrayList<>();
//		allPrimaryColours.addAll(naturalColoursPrimary);
//		allPrimaryColours.addAll(dyeColoursPrimary);
//
//		allSecondaryColours = new ArrayList<>();
//		allSecondaryColours.addAll(naturalColoursSecondary);
//		allSecondaryColours.addAll(dyeColoursSecondary);

		allColours = new ArrayList<>();
		allPrimaryColours = new ArrayList<>();
		allSecondaryColours = new ArrayList<>();
		for(Colour c : this.naturalColoursPrimary) {
			allColours.add(c);
			allPrimaryColours.add(c);
		}
		for(Colour c : this.dyeColoursPrimary) {
			if(!allColours.contains(c)) {
				allColours.add(c);
			}
			if(!allPrimaryColours.contains(c)) {
				allPrimaryColours.add(c);
			}
		}
		for(Colour c : this.naturalColoursSecondary) {
			allColours.add(c);
			allSecondaryColours.add(c);
		}
		for(Colour c : this.dyeColoursSecondary) {
			if(!allColours.contains(c)) {
				allColours.add(c);
			}
			if(!allSecondaryColours.contains(c)) {
				allSecondaryColours.add(c);
			}
		}
	}

	private BodyCoveringType(
			String determiner,
			boolean isDefaultPlural,
			String namePlural,
			String nameSingular,
			List<CoveringModifier> naturalModifiers,
			List<CoveringModifier> extraModifiers,
			List<CoveringPattern> naturalPatterns,
			List<CoveringPattern> dyePatterns,
			List<Colour> naturalColoursPrimary,
			List<Colour> dyeColoursPrimary,
			List<Colour> naturalColoursSecondary,
			List<Colour> dyeColoursSecondary) {

		this.determiner = determiner;
		this.namePlural = namePlural;
		this.nameSingular=nameSingular;
		this.isDefaultPlural = isDefaultPlural;

		if(naturalModifiers == null) {
			this.naturalModifiers = new ArrayList<>();
		} else {
			this.naturalModifiers = naturalModifiers;
		}

		if(extraModifiers == null) {
			this.extraModifiers = new ArrayList<>();
		} else {
			this.extraModifiers = extraModifiers;
		}

		if(naturalPatterns == null) {
			this.naturalPatterns = new ArrayList<>();
			this.naturalPatterns.add(CoveringPattern.NONE);
		} else {
			this.naturalPatterns = naturalPatterns;
		}

		this.dyePatterns = new ArrayList<>();
		if(dyePatterns != null) {
			this.dyePatterns.addAll(dyePatterns);
			this.dyePatterns.removeAll(this.naturalPatterns);
		}

		allPatterns = new ArrayList<>();
		if(naturalPatterns == null) {
			allPatterns.add(CoveringPattern.NONE);
		} else {
			allPatterns.addAll(this.naturalPatterns);
		}

		if(dyePatterns != null) {
			allPatterns.addAll(this.dyePatterns);
		}

		if(naturalColoursPrimary == null) {
			this.naturalColoursPrimary = new ArrayList<>();
		} else {
			this.naturalColoursPrimary = naturalColoursPrimary;
		}
		if(dyeColoursPrimary == null) {
			this.dyeColoursPrimary = new ArrayList<>();
		} else {
			this.dyeColoursPrimary = dyeColoursPrimary;
		}

		if(naturalColoursSecondary == null) {
			this.naturalColoursSecondary = new ArrayList<>();
		} else {
			this.naturalColoursSecondary = naturalColoursSecondary;
		}
		if(dyeColoursSecondary == null) {
			this.dyeColoursSecondary = new ArrayList<>();
		} else {
			this.dyeColoursSecondary = dyeColoursSecondary;
		}

		allColours = new ArrayList<>();
		allPrimaryColours = new ArrayList<>();
		allSecondaryColours = new ArrayList<>();
		for(Colour c : this.naturalColoursPrimary) {
			allColours.add(c);
			allPrimaryColours.add(c);
		}
		for(Colour c : this.dyeColoursPrimary) {
			if(!allColours.contains(c)) {
				allColours.add(c);
			}
			if(!allPrimaryColours.contains(c)) {
				allPrimaryColours.add(c);
			}
		}
		for(Colour c : this.naturalColoursSecondary) {
			allColours.add(c);
			allSecondaryColours.add(c);
		}
		for(Colour c : this.dyeColoursSecondary) {
			if(!allColours.contains(c)) {
				allColours.add(c);
			}
			if(!allSecondaryColours.contains(c)) {
				allSecondaryColours.add(c);
			}
		}
	}

	/**
	 * Use instead of <i>valueOf()</i>.
	 */
	public static BodyCoveringType getTypeFromString(String value) {
		if(value.equals("IMP")) {
			value = "DEMON_COMMON";

		} else if(value.equals("HAIR_IMP")) {
			value = "HAIR_DEMON";

		} else if(value.equals("BODY_HAIR_IMP")) {
			value = "BODY_HAIR_DEMON";

		} else if(value.equals("EYE_IMP")) {
			value = "EYE_DEMON_COMMON";
		}
		return valueOf(value);
	}

	public String getDeterminer(GameCharacter gc) {
		return determiner;
	}

	public boolean isDefaultPlural() {
		return isDefaultPlural;
	}

	public String getNameSingular(GameCharacter gc) {
		return nameSingular;
	}

	public String getNamePlural(GameCharacter gc) {
		return namePlural;
	}

	public String getName(GameCharacter gc){
		if(isDefaultPlural()) {
			return getNamePlural(gc);
		} else {
			return getNameSingular(gc);
		}
	}

	public List<CoveringPattern> getNaturalPatterns() {
		return naturalPatterns;
	}

	public List<CoveringPattern> getDyePatterns() {
		return dyePatterns;
	}

	public List<CoveringPattern> getAllPatterns() {
		return allPatterns;
	}

	public List<Colour> getNaturalColoursPrimary() {
		return naturalColoursPrimary;
	}

	public List<Colour> getDyeColoursPrimary() {
		return dyeColoursPrimary;
	}

	public List<Colour> getNaturalColoursSecondary() {
		return naturalColoursSecondary;
	}

	public List<Colour> getDyeColoursSecondary() {
		return dyeColoursSecondary;
	}

	public List<Colour> getAllColours() {
		return allColours;
	}

	public List<Colour> getAllPrimaryColours() {
		return allPrimaryColours;
	}

	public List<Colour> getAllSecondaryColours() {
		return allSecondaryColours;
	}

	public BodyCoveringType getBodyCoveringType() {
		return this;
	}

	public List<CoveringModifier> getNaturalModifiers() {
		return naturalModifiers;
	}

	public List<CoveringModifier> getExtraModifiers() {
		return extraModifiers;
	}
}


code:
package com.lilithsthrone.game.character.body.types;

/**
 * @since 0.1.69.9
 * @version 0.3.1
 * @author Innoxia
 */
public enum BodyPartType {

	GENERIC("generic"),

	ANTENNA("antenna"),
	ARM("arms"),
	ASS("ass"),
	ANUS("anus"),

	BREAST("breasts"),
	NIPPLES("nipples"),
	MILK("milk"),

	BREAST_CROTCH("crotch-breasts"),
	NIPPLES_CROTCH("crotch-nipples"),
	MILK_CROTCH("crotch-breast-milk"),

	EAR("ears"),
	EYE("eyes"),
	FACE("face"),
	MOUTH("mouth"),
	HAIR("hair"),
	HORN("horns"),
	LEG("legs"),
	PENIS("penis"),
	SECOND_PENIS("second penis"),
	TESTICLES("testicles"),
	CUM("cum"),
	SKIN("skin"),
	TAIL("tail"),
	TENTACLE("tentacle"),
	TONGUE("tongue"),
	CLIT("clitoris"),
	VAGINA("vagina"),
	GIRL_CUM("girl cum"),
	WING("wings");

	private String name;
	private BodyPartType(String name){
		this.name=name;
	}
	public String getName() {
		return name;
	}

}

repo_name: klorpa/Pedo

license: unknown


code:
package com.lilithsthrone.game.character.body.types;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.Body;
import com.lilithsthrone.game.character.race.Race;
import com.lilithsthrone.game.inventory.clothing.BodyPartClothingBlock;

/**
 * @since 0.1.0
 * @version 0.1.83
 * @author Innoxia
 */
public interface BodyPartTypeInterface {

	public boolean isDefaultPlural();

	/** @return Pronoun for this body part. (They, it) */
	public default String getPronoun(){
		if(isDefaultPlural()) {
			return "they";
		} else {
			return "it";
		}
	}

	/**
	 * @return Determiner for this body part. (Returns an empty string if a default 'a' or 'an' should be used.)
	 */
	public String getDeterminer(GameCharacter gc);

	/** @return The default name of this body part. */
	public default String getName(GameCharacter gc){
		if(isDefaultPlural()) {
			return getNamePlural(gc);
		} else {
			return getNameSingular(gc);
		}
	}

	/** @return The singular name of this body part. */
	public String getNameSingular(GameCharacter gc);

	/** @return The plural name of this body part. */
	public String getNamePlural(GameCharacter gc);

	/** @return The name of this body part with its descriptor. */
	public default String getName(boolean withDescriptor, GameCharacter gc) {
		return (getDescriptor(gc).length() > 0 ? getDescriptor(gc) + " " : "") + getName(gc);
	}

	/** A 1-word descriptor that best describes this body part. */
	public String getDescriptor(GameCharacter gc);

	/**
	 * BodyCoveringType when assigned to a character should be checked through their appropriate methods!
	 * @param body The body that this covering type is a part of.
	 * @return The type of skin that is covering this body part. */
	public BodyCoveringType getBodyCoveringType(Body body);

	/**
	 * BodyCoveringType when assigned to a character should be checked through their appropriate methods!
	 */
	public default BodyCoveringType getBodyCoveringType(GameCharacter gc) {
		return getBodyCoveringType(gc.getBody());
	}

	/** @return The race of this body part. */
	public Race getRace();

	//TODO
//	/** @return The description of this body part as seen in the character view screen. */
//	public String getBodyDescription(GameCharacter owner);

	//TODO
//	/** @return The description of this body part being changed. */
//	public String getTransformationDescription(GameCharacter owner);

	/** @return The name that should be used when describing this body part in the context of transformations. */
	public default String getTransformName() {
		if(getRace()==null) {
			return "";
		}

		switch(getRace()){
			case ANGEL:
				return "angelic";
			case CAT_MORPH:
				return "feline";
			case DEMON:
				return "demonic";
			case DOG_MORPH:
				return "canine";
			case COW_MORPH:
				return "bovine";
			case SQUIRREL_MORPH:
				return "squirrel";
			case ALLIGATOR_MORPH:
				return "alligator";
			case HARPY:
				return "harpy";
			case HORSE_MORPH:
				return "equine";
			case REINDEER_MORPH:
				return "reindeer";
			case HUMAN:
				return "human";
			case WOLF_MORPH:
				return "wolf";
			case FOX_MORPH:
				return "fox";
			case BAT_MORPH:
				return "bat";
			case RAT_MORPH:
				return "rat";
			case RABBIT_MORPH:
				return "rabbit";
			case ELEMENTAL:
				return "elemental";
			case NONE:
				return "none";
			case SLIME:
				return "slime";
		}
		return "";
	}

	/**
	 * @return A BodyPartClothingBlock object which defines how this BodyPartInterface is blocking InventorySlots. Returns null if it doesn't affect inventorySlots in any way.
	 */
	public default BodyPartClothingBlock getBodyPartClothingBlock() {
		return null;
	}
}


code:
package com.lilithsthrone.game.character.body.types;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.lilithsthrone.game.character.race.Race;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.0
 * @version 0.3.1
 * @author Innoxia
 */
public class BreastType {

	// Only used for when lacking crotch breasts:
	public static AbstractBreastType NONE = new AbstractBreastType(BodyCoveringType.HUMAN,
			Race.NONE,
			NippleType.HUMAN,
			FluidType.MILK_HUMAN,
			"",
			"",
			"[npc.She] no longer [npc.has] [style.boldShrink([npc.crotchBoobs])]!",
			""){
	};

	public static AbstractBreastType HUMAN = new AbstractBreastType(BodyCoveringType.HUMAN,
			Race.HUMAN,
			NippleType.HUMAN,
			FluidType.MILK_HUMAN,
			"[npc.She] now [npc.has] [style.boldHuman(human)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldHuman(human milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] human, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldHuman(human)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldHuman(human milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] human, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType ANGEL = new AbstractBreastType(BodyCoveringType.ANGEL,
			Race.ANGEL,
			NippleType.ANGEL,
			FluidType.MILK_ANGEL,
			"[npc.She] now [npc.has] [style.boldAngel(angelic)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldAngel(angel milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] angelic, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldAngel(angelic)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldAngel(angel milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] angelic, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType DEMON_COMMON = new AbstractBreastType(BodyCoveringType.DEMON_COMMON,
			Race.DEMON,
			NippleType.DEMON_COMMON,
			FluidType.MILK_DEMON_COMMON,
			"[npc.She] now [npc.has] [style.boldDemon(demonic)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldDemon(demon milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] demonic, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldDemon(demonic)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldDemon(demon milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] demonic, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType DOG_MORPH = new AbstractBreastType(BodyCoveringType.CANINE_FUR,
			Race.DOG_MORPH,
			NippleType.DOG_MORPH,
			FluidType.MILK_DOG_MORPH, // Emergency backup supply. We're on the dog's milk.
			"[npc.She] now [npc.has] [style.boldDogMorph(canine)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldDogMorph(dog milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] canine, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldDogMorph(canine)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldDogMorph(dog milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] canine, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType WOLF_MORPH = new AbstractBreastType(BodyCoveringType.LYCAN_FUR,
			Race.WOLF_MORPH,
			NippleType.WOLF_MORPH,
			FluidType.MILK_WOLF_MORPH,
			"[npc.She] now [npc.has] [style.boldWolfMorph(lupine)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldWolfMorph(wolf milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] lupine, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldWolfMorph(lupine)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldWolfMorph(wolf milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] lupine, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType FOX_MORPH = new AbstractBreastType(BodyCoveringType.FOX_FUR,
			Race.FOX_MORPH,
			NippleType.FOX_MORPH,
			FluidType.MILK_FOX_MORPH,
			"[npc.She] now [npc.has] [style.boldFoxMorph(vulpine)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldFoxMorph(fox milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] vulpine, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldFoxMorph(vulpine)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldFoxMorph(fox milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] vulpine, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType COW_MORPH = new AbstractBreastType(BodyCoveringType.BOVINE_FUR,
			Race.COW_MORPH,
			NippleType.COW_MORPH,
			FluidType.MILK_COW_MORPH,
			"[npc.She] now [npc.has] [style.boldCowMorph(bovine)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldCowMorph(cow milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] bovine, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldCowMorph(bovine)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldCowMorph(cow milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] bovine, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType CAT_MORPH = new AbstractBreastType(BodyCoveringType.FELINE_FUR,
			Race.CAT_MORPH,
			NippleType.CAT_MORPH,
			FluidType.MILK_CAT_MORPH,
			"[npc.She] now [npc.has] [style.boldCatMorph(feline)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldCatMorph(cat milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] feline, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldCatMorph(feline)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldCatMorph(cat milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] feline, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType SQUIRREL_MORPH = new AbstractBreastType(BodyCoveringType.SQUIRREL_FUR,
			Race.SQUIRREL_MORPH,
			NippleType.SQUIRREL_MORPH,
			FluidType.MILK_SQUIRREL_MORPH,
			"[npc.She] now [npc.has] [style.boldSquirrelMorph(squirrel-like)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldSquirrelMorph(squirrel milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] squirrel-like, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldSquirrelMorph(squirrel-like)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldSquirrelMorph(squirrel milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] squirrel-like, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType RAT_MORPH = new AbstractBreastType(BodyCoveringType.RAT_FUR,
			Race.RAT_MORPH,
			NippleType.RAT_MORPH,
			FluidType.MILK_RAT_MORPH, // Rats?! I'm outraged! You promised me dog or higher!
			"[npc.She] now [npc.has] [style.boldRatMorph(rat-like)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldRatMorph(rat milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] rat-like, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldRatMorph(rat-like)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldRatMorph(rat milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] rat-like, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType RABBIT_MORPH = new AbstractBreastType(BodyCoveringType.RABBIT_FUR,
			Race.RABBIT_MORPH,
			NippleType.RABBIT_MORPH,
			FluidType.MILK_RABBIT_MORPH,
			"[npc.She] now [npc.has] [style.boldRabbitMorph(rabbit-like)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldRabbitMorph(rabbit milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] rabbit-like, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldRabbitMorph(rabbit-like)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldRabbitMorph(rabbit milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] rabbit-like, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType BAT_MORPH = new AbstractBreastType(BodyCoveringType.BAT_FUR,
			Race.BAT_MORPH,
			NippleType.BAT_MORPH,
			FluidType.MILK_BAT_MORPH,
			"[npc.She] now [npc.has] [style.boldBatMorph(bat-like)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldBatMorph(bat milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] bat-like, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldBatMorph(bat-like)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldBatMorph(bat milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] bat-like, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType ALLIGATOR_MORPH = new AbstractBreastType(BodyCoveringType.ALLIGATOR_SCALES,
			Race.ALLIGATOR_MORPH,
			NippleType.ALLIGATOR_MORPH,
			FluidType.MILK_ALLIGATOR_MORPH, // Hmm
			"[npc.She] now [npc.has] [style.boldGatorMorph(reptilian)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldGatorMorph(alligator milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] reptilian, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldGatorMorph(reptilian)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldGatorMorph(alligator milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] reptilian, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType HORSE_MORPH = new AbstractBreastType(BodyCoveringType.HORSE_HAIR,
			Race.HORSE_MORPH,
			NippleType.HORSE_MORPH,
			FluidType.MILK_HORSE_MORPH,
			"[npc.She] now [npc.has] [style.boldHorseMorph(equine)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldHorseMorph(horse milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] equine, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldHorseMorph(equine)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldHorseMorph(horse milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] equine, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType REINDEER_MORPH = new AbstractBreastType(BodyCoveringType.REINDEER_FUR,
			Race.REINDEER_MORPH,
			NippleType.REINDEER_MORPH,
			FluidType.MILK_REINDEER_MORPH,
			"[npc.She] now [npc.has] [style.boldReindeerMorph(reindeer-like)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldReindeerMorph(reindeer milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] reindeer-like, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldReindeerMorph(reindeer-like)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldReindeerMorph(reindeer milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] reindeer-like, [npc.crotchNipplesFullDescription]."){
	};

	public static AbstractBreastType HARPY = new AbstractBreastType(BodyCoveringType.FEATHERS,
			Race.HARPY,
			NippleType.HARPY,
			FluidType.MILK_HARPY, // hmm
			"[npc.She] now [npc.has] [style.boldHarpy(avian)], [npc.nipplesFullDescription], and when lactating, [npc.she] will produce [style.boldHarpy(harpy milk)].",
			"On each of [npc.her] [npc.breastSize] [npc.breasts], [npc.she] [npc.has] [npc.nipplesPerBreast] avian, [npc.nipplesFullDescription].",
			"[npc.She] now [npc.has] [style.boldHarpy(avian)], [npc.crotchNipplesFullDescription], and when lactating, [npc.she] will produce [style.boldHarpy(harpy milk)].",
			"On each of [npc.her] [npc.crotchBoobSize] [npc.crotchBoobs], [npc.she] [npc.has] [npc.crotchNipplesPerBreast] avian, [npc.crotchNipplesFullDescription]."){
	};

	private static List<AbstractBreastType> allBreastTypes;
	private static Map<AbstractBreastType, String> breastToIdMap = new HashMap<>();
	private static Map<String, AbstractBreastType> idToBreastMap = new HashMap<>();

	static {
		allBreastTypes = new ArrayList<>();

		// Add in hard-coded breast types:
		Field[] fields = BreastType.class.getFields();

		for(Field f : fields){
			if (AbstractBreastType.class.isAssignableFrom(f.getType())) {

				AbstractBreastType ct;
				try {
					ct = ((AbstractBreastType) f.get(null));

					breastToIdMap.put(ct, f.getName());
					idToBreastMap.put(f.getName(), ct);

					allBreastTypes.add(ct);

				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static AbstractBreastType getBreastTypeFromId(String id) {
		if(id.equals("IMP")) {
			return BreastType.DEMON_COMMON;
		}
		if(id.equals("LYCAN")) {
			return BreastType.WOLF_MORPH;
		}

		id = Util.getClosestStringMatch(id, idToBreastMap.keySet());
		return idToBreastMap.get(id);
	}

	public static String getIdFromBreastType(AbstractBreastType breastType) {
		return breastToIdMap.get(breastType);
	}

	public static List<AbstractBreastType> getAllBreastTypes() {
		return allBreastTypes;
	}

	private static Map<Race, List<AbstractBreastType>> typesMap = new HashMap<>();
	public static List<AbstractBreastType> getBreastTypes(Race r) {
		if(typesMap.containsKey(r)) {
			return typesMap.get(r);
		}

		List<AbstractBreastType> types = new ArrayList<>();
		for(AbstractBreastType type : BreastType.getAllBreastTypes()) {
			if(type.getRace()==r) {
				types.add(type);
			}
		}
		typesMap.put(r, types);
		return types;
	}

}


code:
package com.lilithsthrone.game.character.body.types;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.lilithsthrone.game.character.race.Race;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.0
 * @version 0.3.2
 * @author Innoxia
 */
public class EarType {

	public static AbstractEarType HUMAN = new AbstractEarType(BodyCoveringType.HUMAN,
			Race.HUMAN,
			false,
			"human",
			"ear",
			"ears",
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues("soft", "feminine"),
			"The hot itching feeling passes after a few moments, leaving [npc.herHim] with normal-looking human ears.
"
				+ "[npc.Name] now [npc.has] [style.boldHuman(human ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of normal, human ears, which are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)]#IF(npc.isPiercedEar()), and which have been pierced#ENDIF.") {
	};

	public static AbstractEarType ANGEL = new AbstractEarType(BodyCoveringType.ANGEL,
			Race.ANGEL,
			false,
			"angel",
			"ear",
			"ears",
			Util.newArrayListOfValues("pointed", "delicate", "angelic"),
			Util.newArrayListOfValues("soft", "feminine", "pointed", "delicate", "angelic"),
			"The hot itching feeling passes after a few moments, leaving [npc.herHim] with delicate, humanoid ears, with long, pointed tips.
"
				+ "[npc.Name] now [npc.has] [style.boldAngel(pointed, angelic ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of pointed angelic ears, which are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)]#IF(npc.isPiercedEar()), and which have been pierced#ENDIF.") {
	};

	public static AbstractEarType DEMON_COMMON = new AbstractEarType(BodyCoveringType.DEMON_COMMON,
			Race.DEMON,
			false,
			"demonic",
			"ear",
			"ears",
			Util.newArrayListOfValues("pointed", "demonic"),
			Util.newArrayListOfValues("soft", "feminine", "pointed", "demonic"),
			"The hot itching feeling passes after a few moments, leaving [npc.herHim] with delicate, humanoid ears, with long, pointed tips.
"
				+ "[npc.Name] now [npc.has] [style.boldDemon(pointed, demonic ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of pointed demonic ears, which are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)]#IF(npc.isPiercedEar()), and which have been pierced#ENDIF.") {
	};

	public static AbstractEarType DOG_MORPH = new AbstractEarType(BodyCoveringType.CANINE_FUR,
			Race.DOG_MORPH,
			true,
			"floppy dog",
			"ear",
			"ears",
			Util.newArrayListOfValues("floppy", "furry", "fur-coated", "dog-like"),
			Util.newArrayListOfValues("feminine", "floppy", "furry", "fur-coated", "dog-like"),
			"They quickly expand in size, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new dog-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldDog(floppy, dog-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of floppy,#IF(npc.isPiercedEar()) pierced,#ENDIF dog-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType DOG_MORPH_POINTED = new AbstractEarType(BodyCoveringType.CANINE_FUR,
			Race.DOG_MORPH,
			false,
			"pointed dog",
			"ear",
			"ears",
			Util.newArrayListOfValues("pointed", "furry", "fur-coated", "dog-like"),
			Util.newArrayListOfValues("feminine", "pointed", "furry", "fur-coated", "dog-like"),
			"They quickly grow into upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new dog-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldDog(pointed, dog-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of pointed,#IF(npc.isPiercedEar()) pierced,#ENDIF dog-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType DOG_MORPH_FOLDED = new AbstractEarType(BodyCoveringType.CANINE_FUR,
			Race.DOG_MORPH,
			false,
			"folded dog",
			"ear",
			"ears",
			Util.newArrayListOfValues("folded", "furry", "fur-coated", "dog-like"),
			Util.newArrayListOfValues("feminine", "folded", "furry", "fur-coated", "dog-like"),
			"They quickly grow into upright points, before folding over at the top and shifting to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new dog-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldDog(folded, dog-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of folded,#IF(npc.isPiercedEar()) pierced,#ENDIF dog-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType LYCAN = new AbstractEarType(BodyCoveringType.LYCAN_FUR,
			Race.WOLF_MORPH,
			false,
			"wolf",
			"ear",
			"ears",
			Util.newArrayListOfValues("furry", "fur-coated", "wolf-like"),
			Util.newArrayListOfValues("feminine", "furry", "fur-coated", "wolf-like"),
			"They quickly grow into large, upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new wolf-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldWolf(large, wolf-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of upright,#IF(npc.isPiercedEar()) pierced,#ENDIF wolf-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType FOX_MORPH = new AbstractEarType(BodyCoveringType.FOX_FUR,
			Race.FOX_MORPH,
			false,
			"fox",
			"ear",
			"ears",
			Util.newArrayListOfValues("pointed", "furry", "fur-coated", "fox-like"),
			Util.newArrayListOfValues("feminine", "pointed", "furry", "fur-coated", "fox-like"),
			"They quickly grow into large, upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new fox-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldFox(pointed, fox-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of pointed,#IF(npc.isPiercedEar()) pierced,#ENDIF fox-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType FOX_MORPH_BIG = new AbstractEarType(BodyCoveringType.FOX_FUR,
			Race.FOX_MORPH,
			true,
			"fennec fox",
			"ear",
			"ears",
			Util.newArrayListOfValues("pointed", "furry", "fur-coated", "large", "fennec-fox-like"),
			Util.newArrayListOfValues("feminine", "pointed", "furry", "fur-coated", "large", "fennec-fox-like"),
			"They quickly grow into massive, upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new fox-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldFox(massive, fennec-fox-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of massive,#IF(npc.isPiercedEar()) pierced,#ENDIF fennec-fox-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType COW_MORPH = new AbstractEarType(BodyCoveringType.BOVINE_FUR,
			Race.COW_MORPH,
			false,
			"cow",
			"ear",
			"ears",
			Util.newArrayListOfValues("furry", "fur-coated", "cow-like"),
			Util.newArrayListOfValues("feminine", "furry", "fur-coated", "cow-like"),
			"They quickly take on a distinctly bovine shape by growing out and narrowing down into long, slightly-folded ovals."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new cow-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldCow(cow-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of #IF(npc.isPiercedEar()) pierced,#ENDIF cow-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType CAT_MORPH = new AbstractEarType(BodyCoveringType.FELINE_FUR,
			Race.CAT_MORPH,
			false,
			"cat",
			"ear",
			"ears",
			Util.newArrayListOfValues("furry", "fur-coated", "cat-like"),
			Util.newArrayListOfValues("feminine", "furry", "fur-coated", "cat-like"),
			"They quickly grow into upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new cat-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldCat(cat-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of #IF(npc.isPiercedEar()) pierced,#ENDIF cat-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType CAT_MORPH_TUFTED = new AbstractEarType(BodyCoveringType.FELINE_FUR,
			Race.CAT_MORPH,
			false,
			"tufted cat",
			"ear",
			"ears",
			Util.newArrayListOfValues("tufted", "furry", "fur-coated", "cat-like"),
			Util.newArrayListOfValues("feminine", "tufted", "furry", "fur-coated", "cat-like"),
			"They quickly grow into upright points, which are topped off with a small patch of sensitive fur, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new cat-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldCat(tufted, cat-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of tufted,#IF(npc.isPiercedEar()) pierced,#ENDIF cat-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType SQUIRREL_MORPH = new AbstractEarType(BodyCoveringType.SQUIRREL_FUR,
			Race.SQUIRREL_MORPH,
			false,
			"squirrel",
			"ear",
			"ears",
			Util.newArrayListOfValues("furry", "fur-coated", "squirrel-like"),
			Util.newArrayListOfValues("feminine", "furry", "fur-coated", "squirrel-like"),
			"They quickly grow into small, upright ovals, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new squirrel-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldSquirrel(squirrel-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of#IF(npc.isPiercedEar()) pierced,#ENDIF squirrel-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType RAT_MORPH = new AbstractEarType(BodyCoveringType.RAT_FUR,
			Race.RAT_MORPH,
			false,
			"rat",
			"ear",
			"ears",
			Util.newArrayListOfValues("rat-like"),
			Util.newArrayListOfValues("feminine", "rat-like"),
			"They quickly grow into small, upright ovals, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new rat-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldRat(rat-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of #IF(npc.isPiercedEar()) pierced,#ENDIF rat-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType RABBIT_MORPH = new AbstractEarType(BodyCoveringType.RABBIT_FUR,
			Race.RABBIT_MORPH,
			true,
			"upright rabbit",
			"ear",
			"ears",
			Util.newArrayListOfValues("upright", "furry", "fur-coated", "rabbit-like"),
			Util.newArrayListOfValues("feminine", "upright", "furry", "fur-coated", "rabbit-like"),
			"They quickly grow into large, upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new rabbit-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldRabbit(upright, rabbit-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of upright,#IF(npc.isPiercedEar()) pierced,#ENDIF rabbit-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType RABBIT_MORPH_FLOPPY = new AbstractEarType(BodyCoveringType.RABBIT_FUR,
			Race.RABBIT_MORPH,
			true,
			"floppy rabbit",
			"ear",
			"ears",
			Util.newArrayListOfValues("floppy", "furry", "fur-coated", "rabbit-like"),
			Util.newArrayListOfValues("feminine", "floppy", "furry", "fur-coated", "rabbit-like"),
			"They quickly grow into large, upright points, and shift to sit higher up than a normal pair of human ears would, before suddenly collapsing and flopping down on either side of [npc.her] head."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new rabbit-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldRabbit(floppy, rabbit-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of floppy,#IF(npc.isPiercedEar()) pierced,#ENDIF rabbit-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType BAT_MORPH = new AbstractEarType(BodyCoveringType.BAT_FUR,
			Race.BAT_MORPH,
			false,
			"bat",
			"ear",
			"ears",
			Util.newArrayListOfValues("large", "bat-like"),
			Util.newArrayListOfValues("feminine", "large", "bat-like"),
			"They quickly grow into large, upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] [npc.verb(discover)] that [npc.she] can easily twitch [npc.her] new bat-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldBat(large, bat-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of large,#IF(npc.isPiercedEar()) pierced,#ENDIF bat-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType HORSE_MORPH = new AbstractEarType(BodyCoveringType.HORSE_HAIR,
			Race.HORSE_MORPH,
			false,
			"horse",
			"ear",
			"ears",
			Util.newArrayListOfValues("furry", "upright", "horse-like"),
			Util.newArrayListOfValues("feminine", "furry", "upright", "horse-like"),
			"They quickly grow into sturdy little upright points, and shift to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new horse-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldHorse(horse-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of upright,#IF(npc.isPiercedEar()) pierced,#ENDIF horse-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType REINDEER_MORPH = new AbstractEarType(BodyCoveringType.REINDEER_FUR,
			Race.REINDEER_MORPH,
			false,
			"reindeer",
			"ear",
			"ears",
			Util.newArrayListOfValues("furry", "reindeer-like"),
			Util.newArrayListOfValues("feminine", "furry", "reindeer-like"),
			"They quickly take on a distinctly reindeer-like shape by growing out and narrowing down into long, slightly-folded ovals, before shifting to sit higher up on [npc.her] head than a normal pair of human ears would."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] quickly grows to cover them,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, they're made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.she] experimentally [npc.verb(twitch)] [npc.her] new reindeer-like ears back and forth.
"
				+ "[npc.Name] now [npc.has] [style.boldReindeer(reindeer-like ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.She] [npc.has] a pair of#IF(npc.isPiercedEar()) pierced,#ENDIF reindeer-like ears, which are positioned high up on [npc.her] head and are [npc.materialCompositionDescriptor] [npc.earFullDescription(true)].") {
	};

	public static AbstractEarType ALLIGATOR_MORPH = new AbstractEarType(BodyCoveringType.ALLIGATOR_SCALES,
			Race.ALLIGATOR_MORPH,
			false,
			"alligator",
			"ear",
			"ears",
			Util.newArrayListOfValues("scaled", "scale-covered", "alligator-like"),
			Util.newArrayListOfValues("feminine", "scaled", "scale-covered", "alligator-like"),
			"They quickly shrink down into little nubs as most of the external cartilage shifts down into the sides of [npc.her] head."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] grow to cover [npc.her] now-fully-internal ears,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, [npc.her] now-fully-internal ears are made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.sheIs] left with the ears of an alligator-morph.
"
				+ "[npc.Name] now [npc.has] [style.boldAlligator(internal, scale-covered alligator ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.Her] ears are an internal part of [npc.her] head, and are covered by a fan of [npc.earFullDescription(true)]."
				+ "#IF(npc.isPiercedEar()) They have been cleverly pierced so as to allow [npc.herHim] to wear ear-specific jewellery.#ENDIF") {
	};

	public static AbstractEarType HARPY = new AbstractEarType(BodyCoveringType.FEATHERS,
			Race.HARPY,
			false,
			"harpy",
			"ear",
			"ears",
			Util.newArrayListOfValues("feathered", "feather-covered", "bird-like"),
			Util.newArrayListOfValues("feminine", "feathered", "feather-covered", "bird-like"),
			"They quickly shrink down into little nubs as most of the external cartilage shifts down into the sides of [npc.her] head."
				+ "#IF(npc.getBodyMaterial()==BODY_MATERIAL_FLESH)"
				+ " A layer of [npc.earFullDescriptionColour] grow to cover [npc.her] now-fully-internal ears,"
				+ "#ELSE"
				+ " Just like the rest of [npc.her] body, [npc.her] now-fully-internal ears are made out of [npc.earFullDescription],"
				+ "#ENDIF"
				+ " and as the transformation finishes, [npc.sheIs] left with a pair of beautifully-feathered harpy ears.
"
				+ "[npc.Name] now [npc.has] [style.boldHarpy(internal, feather-covered harpy ears)], which are [npc.materialCompositionDescriptor] [npc.earFullDescription].",
			"[npc.Her] ears are an internal part of [npc.her] head, and are covered by a fan of [npc.earFullDescription(true)]."
				+ "#IF(npc.isPiercedEar()) They have been cleverly pierced so as to allow [npc.herHim] to wear ear-specific jewellery.#ENDIF") {
	};

	private static List<AbstractEarType> allEarTypes;
	private static Map<AbstractEarType, String> earToIdMap = new HashMap<>();
	private static Map<String, AbstractEarType> idToEarMap = new HashMap<>();

	static {
		allEarTypes = new ArrayList<>();

		// Add in hard-coded ear types:
		Field[] fields = EarType.class.getFields();

		for(Field f : fields){
			if (AbstractEarType.class.isAssignableFrom(f.getType())) {

				AbstractEarType ct;
				try {
					ct = ((AbstractEarType) f.get(null));

					earToIdMap.put(ct, f.getName());
					idToEarMap.put(f.getName(), ct);

					allEarTypes.add(ct);

				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static AbstractEarType getEarTypeFromId(String id) {
		if(id.equals("IMP")) {
			return EarType.DEMON_COMMON;
		}
		id = Util.getClosestStringMatch(id, idToEarMap.keySet());
		return idToEarMap.get(id);
	}

	public static String getIdFromEarType(AbstractEarType earType) {
		return earToIdMap.get(earType);
	}

	public static List<AbstractEarType> getAllEarTypes() {
		return allEarTypes;
	}

	private static Map<Race, List<AbstractEarType>> typesMap = new HashMap<>();

	public static List<AbstractEarType> getEarTypes(Race r) {
		if(typesMap.containsKey(r)) {
			return typesMap.get(r);
		}

		List<AbstractEarType> types = new ArrayList<>();
		for(AbstractEarType type : EarType.getAllEarTypes()) {
			if(type.getRace()==r) {
				types.add(type);
			}
		}
		typesMap.put(r, types);
		return types;
	}
}


code:
package com.lilithsthrone.game.character.body.types;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.Body;
import com.lilithsthrone.game.character.body.valueEnums.EyeShape;
import com.lilithsthrone.game.character.race.Race;
import com.lilithsthrone.game.dialogue.utils.UtilText;

/**
 * @since 0.1.83
 * @version 0.2.11
 * @author Innoxia
 */
public enum EyeType implements BodyPartTypeInterface {
	HUMAN(BodyCoveringType.EYE_HUMAN, Race.HUMAN, 1, EyeShape.ROUND, EyeShape.ROUND),

	ANGEL(BodyCoveringType.EYE_ANGEL, Race.ANGEL, 1, EyeShape.ROUND, EyeShape.ROUND),

	COW_MORPH(BodyCoveringType.EYE_COW_MORPH, Race.COW_MORPH, 1, EyeShape.ROUND, EyeShape.ROUND),

	DEMON_COMMON(BodyCoveringType.EYE_DEMON_COMMON, Race.DEMON, 1, EyeShape.ROUND, EyeShape.VERTICAL),

	DOG_MORPH(BodyCoveringType.EYE_DOG_MORPH, Race.DOG_MORPH, 1, EyeShape.ROUND, EyeShape.ROUND),

	LYCAN(BodyCoveringType.EYE_LYCAN, Race.WOLF_MORPH, 1, EyeShape.ROUND, EyeShape.ROUND),

	FOX_MORPH(BodyCoveringType.EYE_FOX_MORPH, Race.FOX_MORPH, 1, EyeShape.ROUND, EyeShape.VERTICAL),

	CAT_MORPH(BodyCoveringType.EYE_FELINE, Race.CAT_MORPH, 1, EyeShape.ROUND, EyeShape.VERTICAL),

	SQUIRREL_MORPH(BodyCoveringType.EYE_SQUIRREL, Race.SQUIRREL_MORPH, 1, EyeShape.ROUND, EyeShape.ROUND),

	RAT_MORPH(BodyCoveringType.EYE_RAT, Race.RAT_MORPH, 1, EyeShape.ROUND, EyeShape.ROUND),

	RABBIT_MORPH(BodyCoveringType.EYE_RABBIT, Race.RABBIT_MORPH, 1, EyeShape.ROUND, EyeShape.ROUND),

	BAT_MORPH(BodyCoveringType.EYE_BAT, Race.BAT_MORPH, 1, EyeShape.ROUND, EyeShape.ROUND),

	ALLIGATOR_MORPH(BodyCoveringType.EYE_ALLIGATOR_MORPH, Race.ALLIGATOR_MORPH, 1, EyeShape.ROUND, EyeShape.VERTICAL),

	HORSE_MORPH(BodyCoveringType.EYE_HORSE_MORPH, Race.HORSE_MORPH, 1, EyeShape.ROUND, EyeShape.HORIZONTAL),

	REINDEER_MORPH(BodyCoveringType.EYE_REINDEER_MORPH, Race.REINDEER_MORPH, 1, EyeShape.ROUND, EyeShape.HORIZONTAL),

	HARPY(BodyCoveringType.EYE_HARPY, Race.HARPY, 1, EyeShape.ROUND, EyeShape.ROUND);

	private BodyCoveringType coveringType;
	private Race race;
	private int eyePairs;
	private EyeShape irisShape, pupilShape;

	private EyeType(BodyCoveringType coveringType, Race race, int eyePairs, EyeShape irisShape, EyeShape pupilShape) {
		this.coveringType = coveringType;
		this.race = race;
		this.eyePairs = eyePairs;
		this.irisShape = irisShape;
		this.pupilShape = pupilShape;
	}

	/**
	 * Use instead of <i>valueOf()</i>.
	 */
	public static EyeType getTypeFromString(String value) {
		if(value.equals("IMP")) {
			value = "DEMON_COMMON";
		}
		return valueOf(value);
	}

	@Override
	public String getDeterminer(GameCharacter gc) {
		return "a pair of";
	}

	@Override
	public boolean isDefaultPlural() {
		return true;
	}

	@Override
	public String getNameSingular(GameCharacter gc) {
		return "eye";
	}

	@Override
	public String getNamePlural(GameCharacter gc) {
		return "eyes";
	}

	@Override
	public String getDescriptor(GameCharacter gc) {
		switch(this){
			case ANGEL:
				return UtilText.returnStringAtRandom("angelic");
			case CAT_MORPH:
				return UtilText.returnStringAtRandom("cat-like");
			case COW_MORPH:
				return UtilText.returnStringAtRandom("cow-like");
			case DEMON_COMMON:
				return UtilText.returnStringAtRandom("demonic");
			case DOG_MORPH:
				return UtilText.returnStringAtRandom("dog-like");
			case SQUIRREL_MORPH:
				return UtilText.returnStringAtRandom("squirrel-like");
			case ALLIGATOR_MORPH:
				return UtilText.returnStringAtRandom("reptile-like");
			case HARPY:
				return UtilText.returnStringAtRandom("bird-like");
			case HORSE_MORPH:
				return UtilText.returnStringAtRandom("horse-like");
			case REINDEER_MORPH:
				return UtilText.returnStringAtRandom("reindeer-like");
			case HUMAN:
				return UtilText.returnStringAtRandom("");
			case LYCAN:
				return UtilText.returnStringAtRandom("wolf-like");
			case FOX_MORPH:
				return UtilText.returnStringAtRandom("fox-like");
			case RAT_MORPH:
				return UtilText.returnStringAtRandom("rat-like");
			case RABBIT_MORPH:
				return UtilText.returnStringAtRandom("rabbit-like");
			case BAT_MORPH:
				return UtilText.returnStringAtRandom("bat-like");
		}
		return "";
	}

	@Override
	public BodyCoveringType getBodyCoveringType(Body body) {
		return coveringType;
	}

	@Override
	public Race getRace() {
		return race;
	}

	public int getEyePairs() {
		return eyePairs;
	}

	public EyeShape getIrisShape() {
		return irisShape;
	}

	public EyeShape getPupilShape() {
		return pupilShape;
	}

	private static Map<Race, List<EyeType>> typesMap = new HashMap<>();
	public static List<EyeType> getEyeTypes(Race r) {
		if(typesMap.containsKey(r)) {
			return typesMap.get(r);
		}

		List<EyeType> types = new ArrayList<>();
		for(EyeType type : EyeType.values()) {
			if(type.getRace()==r) {
				types.add(type);
			}
		}
		typesMap.put(r, types);
		return types;
	}
}


code:
package com.lilithsthrone.game.character.body.types;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.Body;
import com.lilithsthrone.game.character.race.Race;
import com.lilithsthrone.game.dialogue.utils.UtilText;

/**
 * @since 0.1.0
 * @version 0.2.11
 * @author Innoxia
 */
public enum FaceType implements BodyPartTypeInterface {
	HUMAN(BodyCoveringType.HUMAN, MouthType.HUMAN, TongueType.HUMAN, Race.HUMAN),

	ANGEL(BodyCoveringType.ANGEL, MouthType.ANGEL, TongueType.ANGEL, Race.ANGEL),

	DEMON_COMMON(BodyCoveringType.DEMON_COMMON, MouthType.DEMON_COMMON, TongueType.DEMON_COMMON, Race.DEMON),

	DOG_MORPH(BodyCoveringType.CANINE_FUR, MouthType.DOG_MORPH, TongueType.DOG_MORPH, Race.DOG_MORPH),

	LYCAN(BodyCoveringType.LYCAN_FUR, MouthType.WOLF_MORPH, TongueType.WOLF_MORPH, Race.WOLF_MORPH),

	FOX_MORPH(BodyCoveringType.FOX_FUR, MouthType.FOX_MORPH, TongueType.FOX_MORPH, Race.FOX_MORPH),

	CAT_MORPH(BodyCoveringType.FELINE_FUR, MouthType.CAT_MORPH, TongueType.CAT_MORPH, Race.CAT_MORPH),

	CAT_MORPH_PANTHER(BodyCoveringType.FELINE_FUR, MouthType.CAT_MORPH, TongueType.CAT_MORPH, Race.CAT_MORPH),

	COW_MORPH(BodyCoveringType.BOVINE_FUR, MouthType.COW_MORPH, TongueType.COW_MORPH, Race.COW_MORPH),

	SQUIRREL_MORPH(BodyCoveringType.SQUIRREL_FUR, MouthType.SQUIRREL_MORPH, TongueType.SQUIRREL_MORPH, Race.SQUIRREL_MORPH),

	RAT_MORPH(BodyCoveringType.RAT_FUR, MouthType.RAT_MORPH, TongueType.RAT_MORPH, Race.RAT_MORPH),

	RABBIT_MORPH(BodyCoveringType.RABBIT_FUR, MouthType.RABBIT_MORPH, TongueType.RABBIT_MORPH, Race.RABBIT_MORPH),

	BAT_MORPH(BodyCoveringType.BAT_FUR, MouthType.BAT_MORPH, TongueType.BAT_MORPH, Race.BAT_MORPH),

	ALLIGATOR_MORPH(BodyCoveringType.ALLIGATOR_SCALES, MouthType.ALLIGATOR_MORPH, TongueType.ALLIGATOR_MORPH, Race.ALLIGATOR_MORPH),

	HORSE_MORPH(BodyCoveringType.HORSE_HAIR, MouthType.HORSE_MORPH, TongueType.HORSE_MORPH, Race.HORSE_MORPH),

	REINDEER_MORPH(BodyCoveringType.REINDEER_FUR, MouthType.REINDEER_MORPH, TongueType.REINDEER_MORPH, Race.REINDEER_MORPH),

	HARPY(BodyCoveringType.FEATHERS, MouthType.HARPY, TongueType.TENGU, Race.HARPY);

	private BodyCoveringType skinType;
	private MouthType mouthType;
	private TongueType tongueType;
	private Race race;

	private FaceType(BodyCoveringType skinType, MouthType mouthType, TongueType tongueType, Race race) {
		this.skinType = skinType;
		this.mouthType = mouthType;
		this.tongueType = tongueType;
		this.race = race;
	}

	/**
	 * Use instead of <i>valueOf()</i>.
	 */
	public static FaceType getTypeFromString(String value) {
		if(value.equals("IMP")) {
			value = "DEMON_COMMON";
		}
		return valueOf(value);
	}

	@Override
	public boolean isDefaultPlural() {
		return false;
	}

	@Override
	public String getDeterminer(GameCharacter gc) {
		return "";
	}

	@Override
	public String getNameSingular(GameCharacter gc) {
		switch(this){
			case ALLIGATOR_MORPH:
				return UtilText.returnStringAtRandom("snout", "face");
			case ANGEL:
			case DEMON_COMMON:
			case HARPY:
			case HORSE_MORPH:
			case HUMAN:
				return UtilText.returnStringAtRandom("face");
			case RAT_MORPH:
			case RABBIT_MORPH:
			case REINDEER_MORPH:
			case SQUIRREL_MORPH:
			case BAT_MORPH:
			case CAT_MORPH:
			case CAT_MORPH_PANTHER:
			case COW_MORPH:
			case DOG_MORPH:
			case LYCAN:
			case FOX_MORPH:
				return UtilText.returnStringAtRandom("muzzle", "face");
		}

		return "";
	}

	@Override
	public String getNamePlural(GameCharacter gc) {
		switch(this){
			case ALLIGATOR_MORPH:
				return UtilText.returnStringAtRandom("snouts", "faces");
			case ANGEL:
			case DEMON_COMMON:
			case HARPY:
			case HORSE_MORPH:
			case HUMAN:
				return UtilText.returnStringAtRandom("faces");
			case RAT_MORPH:
			case RABBIT_MORPH:
			case REINDEER_MORPH:
			case SQUIRREL_MORPH:
			case BAT_MORPH:
			case CAT_MORPH:
			case CAT_MORPH_PANTHER:
				return UtilText.returnStringAtRandom("muzzles", "faces");
			case COW_MORPH:
			case DOG_MORPH:
			case LYCAN:
			case FOX_MORPH:
				return UtilText.returnStringAtRandom("muzzles", "faces");
		}

		return "";
	}

	@Override
	public String getDescriptor(GameCharacter gc) {
		switch(this){
			case ANGEL:
				return UtilText.returnStringAtRandom("perfect", "flawless", "angelic");
			case CAT_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic cat-like", "cat-like", "feline");
			case CAT_MORPH_PANTHER:
				return UtilText.returnStringAtRandom("anthropomorphic cat-like", "cat-like", "feline");
			case COW_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic cow-like", "cow-like", "bovine");
			case DEMON_COMMON:
				return UtilText.returnStringAtRandom("perfect", "flawless", "demonic");
			case DOG_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic dog-like", "dog-like", "canine");
			case SQUIRREL_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic squirrel-like", "squirrel-like", "rodent");
			case ALLIGATOR_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic alligator-like", "alligator-like", "reptile");
			case HARPY:
				return UtilText.returnStringAtRandom("anthropomorphic bird-like", "bird-like");
			case HORSE_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic horse-like", "horse-like", "equine");
			case REINDEER_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic reindeer-like", "reindeer-like", "rangiferine");
			case HUMAN:
				return UtilText.returnStringAtRandom("");
			case LYCAN:
				return UtilText.returnStringAtRandom("anthropomorphic wolf-like", "wolf-like");
			case FOX_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic fox-like", "fox-like");
			case RAT_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic rat-like", "rat-like", "rodent");
			case BAT_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic bat-like", "bat-like");
			case RABBIT_MORPH:
				return UtilText.returnStringAtRandom("anthropomorphic rabbit-like", "rabbit-like");
		}
		return "";
	}

	@Override
	public String getTransformName() {
		switch(this){
			case ANGEL:
				return "angelic";
			case CAT_MORPH:
				return "feline";
			case CAT_MORPH_PANTHER:
				return "panther";
			case DEMON_COMMON:
				return "demonic";
			case DOG_MORPH:
				return "canine";
			case COW_MORPH:
				return "bovine";
			case SQUIRREL_MORPH:
				return "squirrel";
			case ALLIGATOR_MORPH:
				return "alligator";
			case HARPY:
				return "harpy";
			case HORSE_MORPH:
				return "equine";
			case REINDEER_MORPH:
				return "reindeer";
			case HUMAN:
				return "human";
			case LYCAN:
				return "wolf";
			case FOX_MORPH:
				return "fox";
			case BAT_MORPH:
				return "bat";
			case RAT_MORPH:
				return "rat";
			case RABBIT_MORPH:
				return "rabbit";
		}
		return "";
	}

	@Override
	public BodyCoveringType getBodyCoveringType(Body body) {
		return skinType;
	}

	@Override
	public Race getRace() {
		return race;
	}

	public MouthType getMouthType() {
		return mouthType;
	}

	public TongueType getTongueType() {
		return tongueType;
	}

	private static Map<Race, List<FaceType>> typesMap = new HashMap<>();
	public static List<FaceType> getFaceTypes(Race r) {
		if(typesMap.containsKey(r)) {
			return typesMap.get(r);
		}

		List<FaceType> types = new ArrayList<>();
		for(FaceType type : FaceType.values()) {
			if(type.getRace()==r) {
				types.add(type);
			}
		}
		typesMap.put(r, types);
		return types;
	}
}