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

import java.util.List;

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;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.3.2
 * @version 0.3.2
 * @author Innoxia
 */
public abstract class AbstractEarType implements BodyPartTypeInterface {

	private BodyCoveringType skinType;
	private Race race;

	private boolean ableToBeUsedAsHandlesInSex;

	private String transformationName;
	private String name;
	private String namePlural;

	private List<String> descriptorsMasculine;
	private List<String> descriptorsFeminine;

	private String earTransformationDescription;
	private String earBodyDescription;

	/**
	 * @param skinType What covers this ear type (i.e skin/fur/feather type).
	 * @param race What race has this ear type.
	 * @param ableToBeUsedAsHandlesInSex True if these ears are long and flexible enough to be grabbed and used as extra leverage during sex.
	 * @param transformationName The name that should be displayed when offering this ear type as a transformation. Should be something like "upright rabbit" or "floppy rabbit".
	 * @param name The singular name of the ear. This will usually just be "ear".
	 * @param namePlural The plural name of the ear. This will usually just be "ears".
	 * @param descriptorsMasculine The descriptors that can be used to describe a masculine form of this ear type.
	 * @param descriptorsFeminine The descriptors that can be used to describe a feminine form of this ear type.
	 * @param earTransformationDescription A paragraph describing a character's ears transforming into this ear type. Parsing assumes that the character already has this ear type and associated skin covering.
	 * @param earBodyDescription A sentence or two to describe this ear type, as seen in the character view screen. It should follow the same format as all of the other entries in the EarType class.
	 */
	public AbstractEarType(BodyCoveringType skinType,
			Race race,
			boolean ableToBeUsedAsHandlesInSex,
			String transformationName,
			String name,
			String namePlural,
			List<String> descriptorsMasculine,
			List<String> descriptorsFeminine,
			String earTransformationDescription, String earBodyDescription) {

		this.skinType = skinType;
		this.race = race;

		this.ableToBeUsedAsHandlesInSex = ableToBeUsedAsHandlesInSex;

		this.transformationName = transformationName;
		this.name = name;
		this.namePlural = namePlural;

		this.descriptorsMasculine = descriptorsMasculine;
		this.descriptorsFeminine = descriptorsFeminine;

		this.earTransformationDescription = earTransformationDescription;
		this.earBodyDescription = earBodyDescription;
	}

	public boolean isAbleToBeUsedAsHandlesInSex() {
		return ableToBeUsedAsHandlesInSex;
	}

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

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

	@Override
	public String getTransformName() {
		return transformationName;
	}

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

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

	@Override
	public String getDescriptor(GameCharacter gc) {
		if (gc.isFeminine()) {
			return Util.randomItemFrom(descriptorsFeminine);
		} else {
			return Util.randomItemFrom(descriptorsMasculine);
		}
	}

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

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

//	@Override
	public String getBodyDescription(GameCharacter owner) {
		return UtilText.parse(owner, earBodyDescription);
	}

//	@Override
	public String getTransformationDescription(GameCharacter owner) {
		return UtilText.parse(owner, earTransformationDescription);
	}
}


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

import java.util.List;

/**
 * @since 0.3.1
 * @version 0.3.1
 * @author Innoxia
 */
public abstract class AbstractFootType {

	private String typeName;

	private String footName;
	private String footNamePlural;

	private List<String> footDescriptorsMasculine;
	private List<String> footDescriptorsFeminine;

	private String toeSingularName;
	private String toePluralName;
	private List<String> toesDescriptorsMasculine;
	private List<String> toesDescriptorsFeminine;

	private String footjobName;

	private String footBodyDescription;

	private List<FootStructure> permittedFootStructures;

	/**
	 * @param typeName The name/singular descriptor of this foot type.
	 * @param footName The singular name of the foot, such as 'foot', 'paw', 'hoof', etc.
	 * @param footNamePlural The plural name of the foot, such as 'feet', 'paws', 'hoofs', etc.
	 * @param footDescriptorsMasculine The descriptors that can be used to describe a masculine form of this foot type.
	 * @param footDescriptorsFeminine The descriptors that can be used to describe a feminine form of this foot type
	 * @param toeSingularName The singular name of a toe attached to this foot type, such as 'toe', 'claw', etc.
	 * @param toePluralName The plural name of toes attached to this foot type, such as 'toes', 'claws', etc.
	 * @param toesDescriptorsMasculine The descriptors that can be used to describe a masculine form of the related toes.
	 * @param toesDescriptorsFeminine The descriptors that can be used to describe a feminine form of the related toes.
	 * @param footjobName The name of the action when using this foot type to give a footjob.
	 * @param footBodyDescription A sentence or two to describe this foot type, as seen in the character view screen. It should follow the same format as all of the other entries in the FootType class.
	 */
	public AbstractFootType(String typeName,
			String footName,
			String footNamePlural,
			List<String> footDescriptorsMasculine,
			List<String> footDescriptorsFeminine,
			String toeSingularName,
			String toePluralName,
			List<String> toesDescriptorsMasculine,
			List<String> toesDescriptorsFeminine,
			String footjobName,
			String footBodyDescription,
			List<FootStructure> permittedFootStructures) {

		this.typeName = typeName;

		this.footName = footName;
		this.footNamePlural = footNamePlural;
		this.footDescriptorsMasculine = footDescriptorsMasculine;
		this.footDescriptorsFeminine = footDescriptorsFeminine;

		this.toeSingularName = toeSingularName;
		this.toePluralName = toePluralName;
		this.toesDescriptorsMasculine = toesDescriptorsMasculine;
		this.toesDescriptorsFeminine = toesDescriptorsFeminine;

		this.footjobName = footjobName;

		this.footBodyDescription = footBodyDescription;

		this.permittedFootStructures = permittedFootStructures;
	}

	public String getTypeName() {
		return typeName;
	}

	public String getFootName() {
		return footName;
	}

	public String getFootNamePlural() {
		return footNamePlural;
	}

	public List<String> getFootDescriptorsMasculine() {
		return footDescriptorsMasculine;
	}

	public List<String> getFootDescriptorsFeminine() {
		return footDescriptorsFeminine;
	}

	public String getToeSingularName() {
		return toeSingularName;
	}

	public String getToePluralName() {
		return toePluralName;
	}

	public List<String> getToeDescriptorsMasculine() {
		return toesDescriptorsMasculine;
	}

	public List<String> getToeDescriptorsFeminine() {
		return toesDescriptorsFeminine;
	}

	public String getFootjobName() {
		return footjobName;
	}

	public String getFootBodyDescription() {
		return footBodyDescription;
	}

	public List<FootStructure> getPermittedFootStructures() {
		return permittedFootStructures;
	}
}

repo_name: klorpa/Pedo

license: unknown


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

import java.util.List;

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;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.3.1
 * @version 0.3.1
 * @author Innoxia
 */
public abstract class AbstractHornType implements BodyPartTypeInterface {

	private BodyCoveringType skinType;
	private Race race;

	private int defaultHornsPerRow;

	private String transformationName;
	private String name;
	private String namePlural;

	private List<String> descriptorsMasculine;
	private List<String> descriptorsFeminine;

	private String hornTransformationDescription;
	private String hornBodyDescription;

	/**
	 * @param skinType What covers this horn type (i.e skin/fur/feather type).
	 * @param race What race has this horn type.
	 * @param defaultHornsPerRow The number of horns per row by default for this horn type.
	 * @param transformationName The name that should be displayed when offering this horn type as a transformation. Should be something like "curved" or "straight".
	 * @param name The singular name of the horn. This will usually just be "horn" or "antler".
	 * @param namePlural The plural name of the horn. This will usually just be "horns" or "antlers".
	 * @param descriptorsMasculine The descriptors that can be used to describe a masculine form of this horn type.
	 * @param descriptorsFeminine The descriptors that can be used to describe a feminine form of this horn type.
	 * @param hornTransformationDescription A paragraph describing a character's horns transforming into this horn type. Parsing assumes that the character already has this horn type and associated skin covering.
	 * @param hornBodyDescription A sentence or two to describe this horn type, as seen in the character view screen. It should follow the same format as all of the other entries in the HornType class.
	 */
	public AbstractHornType(
			BodyCoveringType skinType,
			Race race,
			int defaultHornsPerRow,
			String transformationName,
			String name,
			String namePlural,
			List<String> descriptorsMasculine,
			List<String> descriptorsFeminine,
			String hornTransformationDescription,
			String hornBodyDescription) {

		this.skinType = skinType;
		this.race = race;

		this.transformationName = transformationName;
		this.name = name;
		this.namePlural = namePlural;

		this.defaultHornsPerRow = defaultHornsPerRow;

		this.descriptorsMasculine = descriptorsMasculine;
		this.descriptorsFeminine = descriptorsFeminine;

		this.hornTransformationDescription = hornTransformationDescription;
		this.hornBodyDescription = hornBodyDescription;
	}

	public boolean allowsFlight() {
		return false;
	}

	@Override
	public String getDeterminer(GameCharacter gc) {
		if(gc.getHornRows()==1) {
			if(gc.getHornsPerRow()==1) {
				return "a solitary";
			} else if(gc.getHornsPerRow()==2) {
				return "a pair of";
			} else if(gc.getHornsPerRow()==2) {
				return "a trio of";
			} else {
				return "a quartet of";
			}

		} else {
			if(gc.getHornsPerRow()==1) {
				return Util.intToString(gc.getHornRows())+" vertically-aligned";
			} else if(gc.getHornsPerRow()==2) {
				return Util.intToString(gc.getHornRows())+" pairs of";
			} else if(gc.getHornsPerRow()==2) {
				return Util.intToString(gc.getHornRows())+" trios of";
			} else {
				return Util.intToString(gc.getHornRows())+" quartets of";
			}
		}
	}

	public int getDefaultHornsPerRow() {
		return defaultHornsPerRow;
	}

	@Override
	public String getTransformName() {
		return transformationName;
	}

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

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

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

	@Override
	public String getDescriptor(GameCharacter gc) {
		if (gc.isFeminine()) {
			return Util.randomItemFrom(descriptorsFeminine);
		} else {
			return Util.randomItemFrom(descriptorsMasculine);
		}
	}

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

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

//	@Override
	public String getBodyDescription(GameCharacter owner) {
		return UtilText.parse(owner, hornBodyDescription);
	}

//	@Override
	public String getTransformationDescription(GameCharacter owner) {
		return UtilText.parse(owner, hornTransformationDescription);
	}
}


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.Ass;
import com.lilithsthrone.game.character.body.Body;
import com.lilithsthrone.game.character.body.BreastCrotch;
import com.lilithsthrone.game.character.body.Penis;
import com.lilithsthrone.game.character.body.Tail;
import com.lilithsthrone.game.character.body.Tentacle;
import com.lilithsthrone.game.character.body.Vagina;
import com.lilithsthrone.game.character.body.valueEnums.BreastShape;
import com.lilithsthrone.game.character.body.valueEnums.FluidModifier;
import com.lilithsthrone.game.character.body.valueEnums.GenitalArrangement;
import com.lilithsthrone.game.character.body.valueEnums.LabiaSize;
import com.lilithsthrone.game.character.body.valueEnums.LegConfiguration;
import com.lilithsthrone.game.character.race.AbstractRacialBody;
import com.lilithsthrone.game.character.race.Race;
import com.lilithsthrone.game.character.race.RaceStage;
import com.lilithsthrone.game.character.race.RacialBody;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Units;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.3.1
 * @version 0.3.1
 * @author Innoxia
 */
public abstract class AbstractLegType implements BodyPartTypeInterface {

	private BodyCoveringType skinType;
	private Race race;

	private FootStructure defaultFootStructure;
	private AbstractFootType footType;

	private String determiner;

	private String name;
	private String namePlural;

	private List<String> descriptorsMasculine;
	private List<String> descriptorsFeminine;

	private List<String> footDescriptorsMasculine;
	private List<String> footDescriptorsFeminine;

	private List<String> toeDescriptorsMasculine;
	private List<String> toeDescriptorsFeminine;

	private String legTransformationDescription;
	private String legBodyDescription;

	private List<LegConfiguration> allowedLegConfigurations;

	/**
	 * @param skinType What covers this leg type (i.e skin/fur/feather type).
	 * @param race What race has this leg type.
	 * @param defaultFootStructure The default foot structure for this leg type.
	 * @param footType The type of foot attached to this leg type.
	 * @param determiner Will usually be "a pair of".
	 * @param name The singular name of the leg. This will usually just be "leg".
	 * @param namePlural The plural name of the leg. This will usually just be "legs".
	 * @param descriptorsMasculine The descriptors that can be used to describe a masculine form of this leg type.
	 * @param descriptorsFeminine The descriptors that can be used to describe a feminine form of this leg type.
	 * @param footDescriptorsMasculine The additional descriptors that are applied to a masculine form of the associated foot type. (Base descriptors are in the AbstractFootType class.)
	 * @param footDescriptorsFeminine The additional descriptors that are applied to a feminine form of the associated foot type. (Base descriptors are in the AbstractFootType class.)
	 * @param toeDescriptorsMasculine The additional descriptors that are applied to a masculine form of the associated toe type. (Base descriptors are in the AbstractFootType class.)
	 * @param toeDescriptorsFeminine The additional descriptors that are applied to a feminine form of the associated toe type. (Base descriptors are in the AbstractFootType class.)
	 * @param legTransformationDescription A paragraph describing a character's legs transforming into this leg type. Parsing assumes that the character already has this leg type and associated skin covering.
	 * @param legBodyDescription A sentence or two to describe this leg type, as seen in the character view screen. It should follow the same format as all of the other entries in the LegType class.
	 * @param allowedLegConfigurations A list of LegConfigurations that are allowed for this LegType.
	 */
	public AbstractLegType(BodyCoveringType skinType,
			Race race,
			FootStructure defaultFootStructure,
			AbstractFootType footType,
			String determiner,
			String name,
			String namePlural,
			List<String> descriptorsMasculine,
			List<String> descriptorsFeminine,
			List<String> footDescriptorsMasculine,
			List<String> footDescriptorsFeminine,
			List<String> toeDescriptorsMasculine,
			List<String> toeDescriptorsFeminine,
			String legTransformationDescription,
			String legBodyDescription,
			List<LegConfiguration> allowedLegConfigurations) {

		this.skinType = skinType;
		this.race = race;

		this.defaultFootStructure = defaultFootStructure;
		this.footType = footType;

		this.determiner = determiner;

		this.name = name;
		this.namePlural = namePlural;

		this.descriptorsMasculine = descriptorsMasculine;
		this.descriptorsFeminine = descriptorsFeminine;

		this.footDescriptorsMasculine = footDescriptorsMasculine;
		this.footDescriptorsFeminine = footDescriptorsFeminine;

		this.toeDescriptorsMasculine = toeDescriptorsMasculine;
		this.toeDescriptorsFeminine = toeDescriptorsFeminine;

		this.legTransformationDescription = legTransformationDescription;
		this.legBodyDescription = legBodyDescription;

		this.allowedLegConfigurations = allowedLegConfigurations;
	}

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

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

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

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

	@Override
	public String getDescriptor(GameCharacter gc) {
		if (gc.isFeminine()) {
			return Util.randomItemFrom(descriptorsFeminine);
		} else {
			return Util.randomItemFrom(descriptorsMasculine);
		}
	}

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

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

	public AbstractFootType getFootType() {
		return footType;
	}

	public FootStructure getDefaultFootStructure() {
		return defaultFootStructure;
	}

	public String getFootNameSingular(GameCharacter gc) {
		return this.getFootType().getFootName();
	}

	public String getFootNamePlural(GameCharacter gc) {
		return this.getFootType().getFootNamePlural();
	}

	public String getFootDescriptor(GameCharacter gc) {
		if (gc.isFeminine()) {
			return Util.randomItemFrom(Util.mergeLists(this.getFootType().getFootDescriptorsFeminine(), footDescriptorsFeminine));
		} else {
			return Util.randomItemFrom(Util.mergeLists(this.getFootType().getFootDescriptorsMasculine(), footDescriptorsMasculine));
		}
	}

	public String getToeNameSingular(GameCharacter gc) {
		return this.getFootType().getToeSingularName();
	}

	public String getToeNamePlural(GameCharacter gc) {
		return this.getFootType().getToePluralName();
	}

	public String getToeDescriptor(GameCharacter gc) {
		if (gc.isFeminine()) {
			return Util.randomItemFrom(Util.mergeLists(this.getFootType().getToeDescriptorsFeminine(), toeDescriptorsFeminine));
		} else {
			return Util.randomItemFrom(Util.mergeLists(this.getFootType().getToeDescriptorsMasculine(), toeDescriptorsMasculine));
		}
	}

//	@Override
	public String getBodyDescription(GameCharacter owner) {
		return UtilText.parse(owner, legBodyDescription);
	}

//	@Override
	public String getTransformationDescription(GameCharacter owner) {
		return UtilText.parse(owner, legTransformationDescription);
	}

	/**
	 * Applies the related leg configuration transformation for this leg type, and returns a description of the changes.

	 *
	 * When overriding, consider:

	 * Ass.class (type)

	 * BreastCrotch.class (type)

	 * Tail.class (type)

	 * Tentacle.class (type)

	 * Penis.class (type, size, cloaca)

	 * Vagina.class (type, capacity, cloaca)

	 *
	 * @param configuration The leg configuration to be applied.
	 * @param character The character which is being transformed.
	 * @param applyEffects Whether the transformative effects should be applied. Pass in false to get the transformation description without applying any of the actual effects.
	 * @param applyFullEffects Pass in true if you want the additional transformations to include attribute changes (such as penis resizing, vagina capacity resetting, etc.).
	 *
	 * @return A description of the transformation.
	 */
	public String applyLegConfigurationTransformation(GameCharacter character, LegConfiguration legConfiguration, boolean applyEffects, boolean applyFullEffects) {
		StringBuilder bestialStringBuilder = new StringBuilder();

		if(character.getLegConfiguration()==legConfiguration && character.getLegType().equals(this)) {
			return "
"
						+ "[style.italicsDisabled(Nothing happens, as [npc.name] already [npc.has] [npc.a_legRace]'s lower body in the '"+legConfiguration.getName()+"' configuration...)]"
					+ "
";
		}

		bestialStringBuilder.append(handleLegConfigurationChanges(character.getBody(), legConfiguration, applyEffects, applyFullEffects));

		bestialStringBuilder.append("
");
		if(character.hasWings()) {
			if(legConfiguration.isWingsOnLegConfiguration()) {
				bestialStringBuilder.append("[style.italicsBestial([npc.Her] [npc.wingSize], [npc.wings+] are now located on the sides of [npc.her] "+legConfiguration.getName()+" body!)]");
			} else {
				bestialStringBuilder.append("[style.italicsBestial([npc.Her] [npc.wingSize], [npc.wings+] are now located on the back of [npc.her] upper body.)]");
			}
		} else {
			if(legConfiguration.isWingsOnLegConfiguration()) {
				bestialStringBuilder.append("[style.italicsBestial(If [npc.she] [npc.verb(grow)] any wings, they will be located on the sides of [npc.her] "+legConfiguration.getName()+" body!)]");
			} else {
				bestialStringBuilder.append("[style.italicsBestial(If [npc.she] [npc.verb(grow)] any wings, they will be located on the back of [npc.her] upper body.)]");
			}
		}
		bestialStringBuilder.append("
");

		if(applyEffects) {
			character.getBody().getLeg().setLegConfigurationForced(this, legConfiguration);
		}

		bestialStringBuilder.append("
"
									+ character.postTransformationCalculation()
								+ "
");

		return UtilText.parse(character, bestialStringBuilder.toString());
	}

	/**
	 * For use in modifying bodies without an attached character. Outside of the Subspecies class, you should probably always be calling the version of this method that takes in a GameCharacter.
	 *
	 * @param body The body to be modified.
	 * @param legConfiguration The LegConfiguration to be applied.
	 * @param applyFullEffects Pass in true if you want the additional transformations to include attribute changes (such as penis resizing, vagina capacity resetting, etc.).
	 */
	public void applyLegConfigurationTransformation(Body body, LegConfiguration legConfiguration, boolean applyFullEffects) {
		handleLegConfigurationChanges(body, legConfiguration, true, applyFullEffects);
		body.getLeg().setLegConfigurationForced(this, legConfiguration);
	}

	/**
	 * @param applyFullEffects Pass in true if you want the additional transformations to include attribute changes (such as penis resizing, vagina capacity resetting, etc.).
	 */
	private String handleLegConfigurationChanges(Body body, LegConfiguration legConfiguration, boolean applyEffects, boolean applyFullEffects) {

		String bestialRaceName = this.getRace().getName(true);
		String bestialRaceNameDeterminer = UtilText.generateSingularDeterminer(bestialRaceName);
		StringBuilder bestialStringBuilder = new StringBuilder();
		String bestialRaceNameWithDeterminer = bestialRaceNameDeterminer+" "+bestialRaceName;
		String raceColorString = this.getRace().getColour().toWebHexString();
		boolean feral = true;

		switch(legConfiguration) {
			case BIPEDAL:
				feral = false;
				if(applyEffects) {
					applyExtraLegConfigurationTransformations(body, body.getLeg().getLegConfiguration(), false, applyFullEffects); // revert feral parts based on current configuration
					// Changing back to bipedal reverts crotch-boobs based on preferences:
					AbstractRacialBody startingBodyType = RacialBody.valueOfRace(this.getRace());
					if(body.getRaceStage()!=RaceStage.GREATER || Main.getProperties().udders<2 || !body.getGender().isFeminine()) {
						body.setBreastCrotch(
								new BreastCrotch(
									BreastType.NONE,
									Util.randomItemFrom(startingBodyType.getBreastCrotchShapes()),
									startingBodyType.getBreastCrotchSize(),
									startingBodyType.getBreastCrotchLactationRate(),
									startingBodyType.getBreastCrotchCount(),
									startingBodyType.getBreastCrotchNippleSize(),
									startingBodyType.getBreastCrotchNippleShape(),
									startingBodyType.getBreastCrotchAreolaeSize(),
									startingBodyType.getNippleCountPerBreastCrotch(),
									startingBodyType.getBreastCrotchCapacity(),
									startingBodyType.getBreastCrotchElasticity(),
									startingBodyType.getBreastCrotchPlasticity(),
									true));
					}
					body.setGenitalArrangement(startingBodyType.getGenitalArrangement());
				}
				bestialStringBuilder.append(
						"
"
							+ "[npc.NamePos] lower body transforms back into a regular, bipedal configuration, with [npc.her] genitals shifting back to their normal position between [npc.her] [npc.legs].
"
							+ "[npc.Name] now [npc.has] [style.boldTfGeneric(bipedal)] "+this.getTransformName()+" legs, which are covered in [npc.legFullDescription]."
						+ "
");
				break;
			case ARACHNID:
				if(applyEffects) {
					applyExtraLegConfigurationTransformations(body, legConfiguration, true, applyFullEffects);
					body.setGenitalArrangement(GenitalArrangement.CLOACA);
				}
				bestialStringBuilder.append(
						"
"
							+ "[npc.NamePos] lower body transforms into that of a huge, eight-legged "+bestialRaceName+", with [npc.her] genitals and asshole shifting to sit within a cloaca on the underside of [npc.her] massive arachnid abdomen."
									+ " [style.italicsSex(As [npc.her] cloaca is only visible from below, [npc.she] [npc.do]n't feel embarrassed to have no clothing covering [npc.her] arachnid body.)]
"
							+ "[npc.Name] now [npc.has] the [style.boldTfGeneric(arachnid body)] of "+bestialRaceNameWithDeterminer+", which is covered in [npc.legFullDescription]."
						+ "
");
				break;
			case CEPHALOPOD:
				if(applyEffects) {
					applyExtraLegConfigurationTransformations(body, legConfiguration, false, applyFullEffects);
					body.setGenitalArrangement(GenitalArrangement.CLOACA);
				}
				bestialStringBuilder.append(
						"
"
							+ "[npc.NamePos] lower body transforms into that of a huge, eight-legged "+bestialRaceName+", with [npc.her] genitals and asshole shifting to sit within a cloaca located in the central underside of [npc.her] tentacles."
									+ " [style.italicsSex(As [npc.her] cloaca is only visible from below, [npc.she] [npc.do]n't feel embarrassed to have no clothing covering [npc.her] tentacled body.)]
"
							+ "[npc.Name] now [npc.has] the [style.boldTfGeneric(tentacled body)] of "+bestialRaceNameWithDeterminer+", which is covered in [npc.legFullDescription]."
						+ "
");
				break;
			case TAIL:
				if(applyEffects) {
					applyExtraLegConfigurationTransformations(body, legConfiguration, false, applyFullEffects);
					body.setGenitalArrangement(GenitalArrangement.CLOACA);
				}
				bestialStringBuilder.append(
						"
"
							+ "[npc.NamePos] lower body transforms into that of "+bestialRaceNameWithDeterminer+"'s tail, with [npc.her] genitals and asshole shifting to sit within a front-facing cloaca,"
									+ " located in the equivalent place as where [npc.her] regular intercrural genitalia would be.
"
							+ "[npc.Name] now [npc.has] the [style.boldTfGeneric(tail)] of "+bestialRaceNameWithDeterminer+", which is covered in [npc.legFullDescription]."
						+ "
");
				break;
			case TAIL_LONG:
				if(applyEffects) {
					applyExtraLegConfigurationTransformations(body, legConfiguration, false, applyFullEffects);
					body.setGenitalArrangement(GenitalArrangement.CLOACA);
				}
				bestialStringBuilder.append(
						"
"
							+ "[npc.NamePos] lower body transforms into that of a huge "+bestialRaceName+"'s tail, with [npc.her] genitals and asshole shifting to sit within a front-facing cloaca,"
									+ " located in the equivalent place as where [npc.her] regular intercrural genitalia would be.
"
							+ "[npc.Name] now [npc.has] the [style.boldTfGeneric(huge tail)] of "+bestialRaceNameWithDeterminer+", which is covered in [npc.legFullDescription]."
						+ "
");
				break;
			case TAUR:
				bestialStringBuilder.append(
						"
"
							+ "An extremely unsettling, tingling feeling starts to spread down into [npc.namePos] [npc.legs+],");

				if(applyEffects) {
					applyExtraLegConfigurationTransformations(body, legConfiguration, true, applyFullEffects);
					body.setGenitalArrangement(GenitalArrangement.NORMAL);
				}

				bestialStringBuilder.append(
						" and [npc.she] [npc.verb(let)] out an alarmed cry as [npc.she] [npc.verb(lose)] [npc.her] balance and [npc.verb(tumble)] to the floor."
							+ " Right before [npc.her] [npc.eyes], [npc.her] lower body shifts and transforms, with [npc.her] limbs pulling back into an intermediary mass of [npc.bodyMaterial],"
								+ " before almost immediately pushing back out in a quadrupedal configuration."
						+ "
"
						+ "
"
							+ "As this is happening, [npc.her] [npc.ass+] rapidly expands and pushes out, quickly forming the body to which [npc.her] four new legs are anchored."
							+ " Although it could be described as nothing less than extreme, the transformation is surprisingly pain-free, and the gasps and [npc.moans] that burst out from [npc.namePos] mouth are purely from shock and exertion."
						+ "
"
						+ "
"
							+ "After just a few moments more, [npc.namePos] lower body has completely transformed into that of "+bestialRaceNameWithDeterminer
								+", with [npc.her] genitals and asshole shifting to sit in the same place as that of a feral "+bestialRaceName+".
"
							+ "[npc.Name] now [npc.has] the [style.boldTfGeneric(lower body)] of "+bestialRaceNameWithDeterminer+", which is covered in [npc.legFullDescription(true)]."
						+ "
");
				break;
		}

		if(Main.getProperties().udders==0) {
			AbstractRacialBody startingBodyType = RacialBody.valueOfRace(this.getRace());
			body.setBreastCrotch(
					new BreastCrotch(
						BreastType.NONE,
						Util.randomItemFrom(startingBodyType.getBreastCrotchShapes()),
						startingBodyType.getBreastCrotchSize(),
						startingBodyType.getBreastCrotchLactationRate(),
						startingBodyType.getBreastCrotchCount(),
						startingBodyType.getBreastCrotchNippleSize(),
						startingBodyType.getBreastCrotchNippleShape(),
						startingBodyType.getBreastCrotchAreolaeSize(),
						startingBodyType.getNippleCountPerBreastCrotch(),
						startingBodyType.getBreastCrotchCapacity(),
						startingBodyType.getBreastCrotchElasticity(),
						startingBodyType.getBreastCrotchPlasticity(),
						true));
		}

		bestialStringBuilder.append("
"
				+ "[style.boldTfGeneric(Every part)] of [npc.her] lower body has transformed into that of a "+(feral?"feral "+bestialRaceName:"regular "+this.getRace().getName(false))+", meaning that [npc.she] now [npc.has]");

		List<String> partsList = new ArrayList<>();
		// Tail:
		if(body.getTail().getType()==TailType.NONE) {
			partsList.add(" [style.boldTfGeneric(no tail)]");
		} else {
			partsList.add(" [style.boldTfGeneric("
					+(body.getTail().getTailCount()==1
						?(body.getTail().getType()==TailType.HARPY?"a plume of ":UtilText.generateSingularDeterminer(body.getTail().getName(null))+" ")
						:Util.intToString(body.getTail().getTailCount())+" "+(body.getTail().getType()==TailType.HARPY?" plumes of ":""))
					+body.getTail().getName(null)+")]");
		}
		// Ass:
		partsList.add(bestialRaceNameWithDeterminer+"'s ass");
		// Crotch boobs:
		if((Main.getProperties().udders==1 && legConfiguration!=LegConfiguration.BIPEDAL) || (Main.getProperties().udders==2 && body.getRaceStage()==RaceStage.GREATER)) {
			if(body.getBreastCrotch().getType()!=BreastType.NONE && !legConfiguration.isBipedalPositionedCrotchBoobs()) {
				partsList.add("animal-like "+body.getBreastCrotch().getName(null));
			}
		}
		bestialStringBuilder.append(Util.stringsToStringList(partsList, false)+".");

		Penis penis = body.getPenis();
		if(penis.getType()!=PenisType.NONE) {
			if(feral) {
				bestialStringBuilder.append(" [npc.Her] cock has similarly transformed into that of a feral "+bestialRaceName+"'s, and not only produces musky, animal-like cum, but is also an impressive "+Units.size(penis.getRawSizeValue())+" long.");
			} else {
				bestialStringBuilder.append(" [npc.Her] cock has similarly transformed into that of a regular "+this.getRace().getName(false)+", and is "+Units.size(penis.getRawSizeValue())+" long.");
			}
		}

		Vagina vagina = body.getVagina();
		if(vagina.getType()!=VaginaType.NONE) {
			if(feral) {
				bestialStringBuilder.append(" [npc.Her] now-musky pussy is now also identical to that of "+bestialRaceNameWithDeterminer
						+", and has expanded to be "+vagina.getOrificeVagina().getCapacity().getDescriptor()+" in order to accommodate a matching feral cock.");
			} else {
				bestialStringBuilder.append(" [npc.Her] now-"+vagina.getOrificeVagina().getCapacity().getDescriptor()+" pussy has similarly transformed into that of a regular.");
			}
		}
		bestialStringBuilder.append("
");

		if(feral) {
			bestialStringBuilder.append("
Feeling as though it's only natural, [npc.she] [style.colourGood(no longer [npc.verb(get)] embarrassed)] about having [npc.her] [style.italicsBestial(animalistic genitals"
					+(legConfiguration.isBipedalPositionedCrotchBoobs()?(body.getBreastCrotch().getShape()==BreastShape.UDDERS?" or udders":" or crotch-boobs"):"")
					+ ")] on display!
");
		}

		if(legConfiguration.getLandSpeedModifier()>0) {
			bestialStringBuilder.append("
"
										+ "[npc.Her] new lower body is not as well-adapted to movement on land as a usual biped,"
											+ " and as a result, [style.colourTerrible([npc.she] [npc.verb(move)] slower than usual)] [style.colourEarth(while on land)]!"
									+ "
");
		} else if(legConfiguration.getLandSpeedModifier()<0) {
			bestialStringBuilder.append("
"
										+ "[npc.Her] new lower body is capable of speeds greater than that attainable by a usual biped,"
											+ " and as a result, [style.colourExcellent([npc.she] [npc.verb(move)] faster than usual)] [style.colourEarth(while on land)]!"
									+ "
");
		}

		if(legConfiguration.getWaterSpeedModifier()>0) {
			bestialStringBuilder.append("
"
										+ "[npc.SheIsFull] now a lot less capable at moving in water than a usual biped,"
											+ " and as a result, [style.colourTerrible([npc.she] [npc.verb(move)] slower than usual)] [style.colourWater(while in water)]!"
									+ "
");
		} else if(legConfiguration.getWaterSpeedModifier()<0) {
			bestialStringBuilder.append("
"
										+ "[npc.SheIsFull] now in possession of a body that's well-suited to moving in water,"
											+ " and as a result, [style.colourExcellent([npc.she] [npc.verb(move)] faster than usual)] [style.colourWater(while in water)]!"
									+ "
");
		}
		return bestialStringBuilder.toString();
	}

	// Setting parts is applied directly through body to circumvent transformation blocks
	/**
	 * @param applyFullEffects Pass in true if you want the additional transformations to include attribute changes (such as penis resizing, vagina capacity resetting, etc.).
	 **/
	private void applyExtraLegConfigurationTransformations(Body body, LegConfiguration legConfiguration, boolean largeGenitals, boolean applyFullEffects) {
		AbstractRacialBody startingBodyType = RacialBody.valueOfRace(this.getRace());

		boolean demon = body.getRace()==Race.DEMON;

		if(legConfiguration.getBestialParts().contains(Ass.class)) { // Ass (includes Anus):
			if(!applyFullEffects) {
				body.getAss().setType(null, (demon
						?AssType.DEMON_COMMON
						:startingBodyType.getAssType()));
			} else {
				body.setAss(
						new Ass(
							(demon
								?AssType.DEMON_COMMON
								:startingBodyType.getAssType()),
							(body.isFeminine()
								? startingBodyType.getFemaleAssSize()
								: startingBodyType.getMaleAssSize()),
							startingBodyType.getAnusWetness(),
							startingBodyType.getAnusCapacity(),
							startingBodyType.getAnusElasticity(),
							startingBodyType.getAnusPlasticity(),
							true));
			}
		}
		if(legConfiguration.getBestialParts().contains(BreastCrotch.class)) { // Crotch-boobs:
			if(!applyFullEffects) {
				body.getBreastCrotch().setType(null,
						(body.isFeminine()
							?(demon
								?BreastType.DEMON_COMMON
								:startingBodyType.getBreastType())
							:BreastType.NONE));

			} else {
				body.setBreastCrotch(
						new BreastCrotch(
						(body.isFeminine()
							?(demon
								?BreastType.DEMON_COMMON
								:startingBodyType.getBreastType())
							:BreastType.NONE),
						Util.randomItemFrom(startingBodyType.getBreastCrotchShapes()),
						startingBodyType.getBreastCrotchSize(),
						startingBodyType.getBreastCrotchLactationRate(),
						startingBodyType.getBreastCrotchCount(),
						startingBodyType.getBreastCrotchNippleSize(),
						startingBodyType.getBreastCrotchNippleShape(),
						startingBodyType.getBreastCrotchAreolaeSize(),
						startingBodyType.getNippleCountPerBreastCrotch(),
						startingBodyType.getBreastCrotchCapacity(),
						startingBodyType.getBreastCrotchElasticity(),
						startingBodyType.getBreastCrotchPlasticity(),
						true));
			}
		}
		if(legConfiguration.getBestialParts().contains(Tail.class)) { // Tail:
			if(body.getLeg().getType().getRace()==Race.DEMON) {
				body.setTail(new Tail(TailType.DEMON_HORSE));
			} else {
				if(body.getTail().getType().getRace()!=startingBodyType.getTailType().get(0).getRace()) {
					body.setTail(new Tail(startingBodyType.getTailType().get(0)));
				}
			}
		}
		if(legConfiguration.getBestialParts().contains(Tentacle.class)) { // Tentacle:
			body.setTentacle(new Tentacle(startingBodyType.getTentacleType()));
		}
		if(legConfiguration.getBestialParts().contains(Penis.class)) { // Penis (includes Testicle):
			if(!applyFullEffects) {
				if(body.getPenis().getType()!=PenisType.NONE && body.getPenis().getType()!=PenisType.DILDO) {
					body.getPenis().setType(null,
								(demon
									?PenisType.DEMON_COMMON
									:startingBodyType.getPenisType()));
				}

			} else {
				boolean virgin = body.getPenis().isVirgin();
				body.setPenis(body.getPenis().getType()!=PenisType.NONE && body.getPenis().getType()!=PenisType.DILDO
						? new Penis(
							(demon
								?PenisType.DEMON_COMMON
								:startingBodyType.getPenisType()),
							(int) (startingBodyType.getPenisSize()*(largeGenitals?2.5:1)),
							true,
							startingBodyType.getPenisGirth()+(largeGenitals?1:0),
							startingBodyType.getTesticleSize()+(largeGenitals?1:0),
							startingBodyType.getCumProduction()*(largeGenitals?4:1),
							startingBodyType.getTesticleQuantity())
						: new Penis(PenisType.NONE, 0, false, 0, 0, 0, 2));
				body.getPenis().getTesticle().getCum().addFluidModifier(null, FluidModifier.MUSKY);
				body.getPenis().setVirgin(virgin);
			}
		}
		if(legConfiguration.getBestialParts().contains(Vagina.class)) { // Vagina (includes Clitoris):
			if(!applyFullEffects) {
				if(body.getVagina().getType()!=VaginaType.NONE) {
					body.getVagina().setType(null,
								(demon
									?VaginaType.DEMON_COMMON
									:startingBodyType.getVaginaType()));
				}

			} else {
				boolean virgin = body.getVagina().getType()!=VaginaType.NONE?body.getVagina().getOrificeVagina().isVirgin():true;
				body.setVagina(
						body.getVagina().getType()!=VaginaType.NONE
							? new Vagina(
									(demon
										?VaginaType.DEMON_COMMON
										:startingBodyType.getVaginaType()),
									LabiaSize.getRandomLabiaSize().getValue(),
									startingBodyType.getClitSize(),
									startingBodyType.getVaginaWetness(),
									(float) (startingBodyType.getVaginaCapacity()*(largeGenitals?2.5:1)),
									startingBodyType.getVaginaElasticity(),
									startingBodyType.getVaginaPlasticity(),
									true)
							: new Vagina(VaginaType.NONE, 0, 0, 0, 0, 3, 3, true));
				body.getVagina().getGirlcum().addFluidModifier(null, FluidModifier.MUSKY);
				body.getVagina().getOrificeVagina().setVirgin(virgin);
			}
		}
 	}

	/**
	 * @param legConfiguration The configuration to check transformation availability of.
	 * @return True if this configuration is allowed for this LegType.
	 */
	public boolean isLegConfigurationAvailable(LegConfiguration legConfiguration) {
		return allowedLegConfigurations.contains(legConfiguration);
	}
}


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.83
 * @version 0.2.11
 * @author Innoxia
 */
public enum AntennaType implements BodyPartTypeInterface {
	NONE("", null, null);

	private BodyCoveringType skinType;
	private Race race;

	private AntennaType(String descriptor, BodyCoveringType skinType, Race race) {
		this.skinType = skinType;
		this.race = race;
	}

	/**
	 * Use instead of <i>valueOf()</i>.
	 */
	public static AntennaType getTypeFromString(String value) {
		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) {
		switch(this){
			default:
				return UtilText.returnStringAtRandom("antenna");
		}
	}

	@Override
	public String getNamePlural(GameCharacter gc) {
		switch(this){
			default:
				return UtilText.returnStringAtRandom("antennae");
		}
	}

	@Override
	public String getDescriptor(GameCharacter gc) {
		switch(this){
			case NONE:
				return UtilText.returnStringAtRandom("");
			default:
				return UtilText.returnStringAtRandom("");
		}
	}

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

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

	@Override
	public String getTransformName() {
		switch(this){
			case NONE:
				return "none";
		}
		return "";
	}

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

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


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

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

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

/**
 * @since 0.1.83
 * @version 0.2.10
 * @author Innoxia
 */
public enum AnusType implements BodyPartTypeInterface {

	HUMAN(BodyCoveringType.ANUS, Race.HUMAN),

	ANGEL(BodyCoveringType.ANUS, Race.ANGEL, OrificeModifier.RIBBED, OrificeModifier.TENTACLED, OrificeModifier.MUSCLE_CONTROL),

	DEMON_COMMON(BodyCoveringType.ANUS, Race.DEMON, OrificeModifier.RIBBED, OrificeModifier.TENTACLED, OrificeModifier.MUSCLE_CONTROL),

	COW_MORPH(BodyCoveringType.ANUS, Race.COW_MORPH),

	DOG_MORPH(BodyCoveringType.ANUS, Race.DOG_MORPH),

	FOX_MORPH(BodyCoveringType.ANUS, Race.FOX_MORPH),

	SQUIRREL_MORPH(BodyCoveringType.ANUS, Race.SQUIRREL_MORPH),

	RAT_MORPH(BodyCoveringType.ANUS, Race.RAT_MORPH),

	RABBIT_MORPH(BodyCoveringType.ANUS, Race.RABBIT_MORPH),

	BAT_MORPH(BodyCoveringType.ANUS, Race.BAT_MORPH),

	WOLF_MORPH(BodyCoveringType.ANUS, Race.WOLF_MORPH),

	CAT_MORPH(BodyCoveringType.ANUS, Race.CAT_MORPH),

	HORSE_MORPH(BodyCoveringType.ANUS, Race.HORSE_MORPH, OrificeModifier.PUFFY),

	REINDEER_MORPH(BodyCoveringType.ANUS, Race.REINDEER_MORPH, OrificeModifier.PUFFY),

	ALLIGATOR_MORPH(BodyCoveringType.ANUS, Race.ALLIGATOR_MORPH),

	HARPY(BodyCoveringType.ANUS, Race.HARPY);

	private BodyCoveringType skinType;
	private Race race;
	private List<OrificeModifier> defaultRacialOrificeModifiers;

	private AnusType(BodyCoveringType skinType, Race race, OrificeModifier... defaultRacialOrificeModifiers) {
		this.skinType = skinType;
		this.race = race;
		this.defaultRacialOrificeModifiers = new ArrayList<>();
		Collections.addAll(this.defaultRacialOrificeModifiers, defaultRacialOrificeModifiers);
	}

	/**
	 * Use instead of <i>valueOf()</i>.
	 */
	public static AnusType 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 getName(GameCharacter gc) {
		switch(this){
			default:
				return UtilText.returnStringAtRandom("asshole", "back door", "rear entrance");
		}
	}

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

	@Override
	public String getNamePlural(GameCharacter gc) {
		switch(this){
			default:
				return UtilText.returnStringAtRandom("assholes", "back doors", "rear entrances");
		}
	}

	@Override
	public String getDescriptor(GameCharacter gc) {
		switch(this){
			case ANGEL:
				return UtilText.returnStringAtRandom("angelic", "perfect");
			case DEMON_COMMON:
				return UtilText.returnStringAtRandom("demonic", "irresistible");
			case HORSE_MORPH:
				return UtilText.returnStringAtRandom("horse-like", "equine");
			case COW_MORPH:
				return UtilText.returnStringAtRandom("cow-like", "bovine");
			default:
				return UtilText.returnStringAtRandom("");
		}
	}

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

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

	public List<OrificeModifier> getDefaultRacialOrificeModifiers() {
		return defaultRacialOrificeModifiers;
	}
}


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.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.ItemTag;
import com.lilithsthrone.game.inventory.clothing.BodyPartClothingBlock;
import com.lilithsthrone.utils.Util;

/**
 * Contains static instances of AbstractArmType.
 *
 * @since 0.1.0
 * @version 0.3
 * @author Innoxia
 */
public class ArmType {

	public static AbstractArmType HUMAN = new AbstractArmType(BodyCoveringType.HUMAN,
			Race.HUMAN,
			"arm",
			"arms",
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"hand",
			"hands",
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues("soft", "feminine"),
			"finger",
			"fingers",
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues("soft", "feminine"),
			"Thankfully, the transformation only lasts a matter of moments, leaving [npc.herHim] with normal-looking human arms, complete with human hands.
"
				+ "[npc.Name] now [npc.has] [style.boldHuman(human arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] normal human arms and hands, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)].") {
	};

	public static AbstractArmType ANGEL = new AbstractArmType(BodyCoveringType.HUMAN,
			Race.ANGEL,
			"arm",
			"arms",
			Util.newArrayListOfValues("delicate"),
			Util.newArrayListOfValues("delicate"),
			"hand",
			"hands",
			Util.newArrayListOfValues("delicate", "soft"),
			Util.newArrayListOfValues("delicate", "soft", "feminine"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("delicate", "soft"),
			Util.newArrayListOfValues("delicate", "soft", "feminine"),
			"Within a matter of moments, they've changed into slender, human-like arms, complete with human-like hands."
				+ " Despite their somewhat-normal appearance, they have a subtle, alluring quality to them that reveals their true angelic nature.
"
				+ "[npc.Name] now [npc.has] [style.boldAngel(angelic arms and hands)], which are covered in [npc.armFullDescription]",
			"[npc.She] [npc.has] [npc.armRows] human-like arms and hands, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)].") {
	};

	public static AbstractArmType DEMON_COMMON = new AbstractArmType(BodyCoveringType.DEMON_COMMON,
			Race.DEMON,
			"arm",
			"arms",
			Util.newArrayListOfValues("flawless"),
			Util.newArrayListOfValues("flawless"),
			"hand",
			"hands",
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues("delicate", "soft", "feminine"),
			"finger",
			"fingers",
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues("delicate", "soft", "feminine"),
			"Within a matter of moments, they've changed into slender, human-like arms, complete with human-like hands."
				+ " Despite their somewhat-normal appearance, they have a subtle, alluring quality to them that reveals their true demonic nature.
"
				+ "[npc.Name] now [npc.has] [style.boldDemon(demonic arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] slender human-looking arms and hands, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)].") {
	};

	public static AbstractArmType COW_MORPH = new AbstractArmType(BodyCoveringType.BOVINE_FUR,
			Race.COW_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("bovine"),
			Util.newArrayListOfValues("feminine", "bovine"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("bovine"),
			Util.newArrayListOfValues("feminine", "bovine"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new hair growing over the backs of [npc.her] hands as tough, hoof-like nails push out in place of regular, human-like ones."
				+ " Despite their appearance, [npc.sheIsFull] relieved to discover that [npc.her] hands have lost none of their dexterity."
				+ " As the transformation comes to an end, [npc.she] [npc.verb(see)] that at [npc.her] upper-biceps, [npc.her] hair smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "[npc.NameIsFull] left with anthropomorphic, [style.boldCowMorph(cow-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands, while human in shape, have tough little hoof-like nails.") {
	};

	public static AbstractArmType DOG_MORPH = new AbstractArmType(BodyCoveringType.CANINE_FUR,
			Race.DOG_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("dog-like", "paw-like", "furry", "canine"),
			Util.newArrayListOfValues("soft", "feminine", "dog-like", "paw-like", "furry", "canine"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("padded", "canine"),
			Util.newArrayListOfValues("soft", "feminine", "padded", "canine"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new fur growing over the backs of [npc.her] hands as blunt, dog-like claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in little leathery pads, and at [npc.her] upper-biceps, [npc.her] fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldDogMorph(dog-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, dog-like hands, complete with little blunt claws and leathery pads.") {
	};

	public static AbstractArmType WOLF_MORPH = new AbstractArmType(BodyCoveringType.LYCAN_FUR,
			Race.WOLF_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("wolf-like", "furry", "paw-like"),
			Util.newArrayListOfValues("soft", "feminine", "wolf-like", "furry", "paw-like"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("padded", "wolf-like"),
			Util.newArrayListOfValues("soft", "feminine", "padded", "wolf-like"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new fur growing over the backs of [npc.her] hands as sharp claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in tough leathery pads, and at [npc.her] upper-biceps, [npc.her] fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldWolfMorph(wolf-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, wolf-like hands, complete with sharp claws and tough leathery pads.") {
	};

	public static AbstractArmType FOX_MORPH = new AbstractArmType(BodyCoveringType.FOX_FUR,
			Race.FOX_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("fox-like", "furry", "paw-like"),
			Util.newArrayListOfValues("soft", "feminine", "fox-like", "furry", "paw-like"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("padded", "fox-like"),
			Util.newArrayListOfValues("soft", "feminine", "padded", "fox-like"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new fur growing over the backs of [npc.her] hands as sharp claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in little pads, and at [npc.her] upper-biceps, [npc.her] fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldFoxMorph(fox-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, fox-like hands, complete with sharp claws and tough leathery pads.") {
	};

	public static AbstractArmType CAT_MORPH = new AbstractArmType(BodyCoveringType.FELINE_FUR,
			Race.CAT_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("soft", "delicate", "cat-like", "paw-like", "furry", "feline"),
			Util.newArrayListOfValues("soft", "feminine", "cat-like", "paw-like", "furry", "feline"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("soft", "delicate", "padded", "feline"),
			Util.newArrayListOfValues("soft", "feminine", "padded", "feline"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] fur growing over the backs of [npc.her] hands as sharp, retractable claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in little pink pads, and at [npc.her] upper-biceps, [npc.her] fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldCatMorph(cat-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, cat-like hands, complete with retractable claws and pink pads.") {
	};

	public static AbstractArmType HORSE_MORPH = new AbstractArmType(BodyCoveringType.HORSE_HAIR,
			Race.HORSE_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("hair-coated"),
			Util.newArrayListOfValues("hair-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("equine"),
			Util.newArrayListOfValues("feminine", "equine"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("equine"),
			Util.newArrayListOfValues("feminine", "equine"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new hair growing over the backs of [npc.her] hands as tough, hoof-like nails push out in place of regular, human-like ones."
				+ " Despite their appearance, [npc.sheIs] relieved to discover that [npc.her] hands have lost none of their dexterity."
				+ " As the transformation comes to an end, [npc.she] [npc.verb(see)] that at [npc.her] upper-biceps, [npc.her] hair smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "[npc.NameIsFull] left with anthropomorphic, [style.boldHorseMorph(horse-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands, while human in shape, have tough little hoof-like nails.") {
	};

	public static AbstractArmType REINDEER_MORPH = new AbstractArmType(BodyCoveringType.REINDEER_FUR,
			Race.REINDEER_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("hair-coated"),
			Util.newArrayListOfValues("hair-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("reindeer"),
			Util.newArrayListOfValues("feminine", "reindeer"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("reindeer"),
			Util.newArrayListOfValues("feminine", "reindeer"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new fur growing over the backs of [npc.her] hands as tough, hoof-like nails push out in place of regular, human-like ones."
				+ " Despite their appearance, [npc.sheIs] relieved to discover that [npc.her] hands have lost none of their dexterity."
				+ " As the transformation comes to an end, [npc.she] [npc.verb(see)] that at [npc.her] upper-biceps, [npc.her] fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "[npc.NameIsFull] left with anthropomorphic, [style.boldReindeerMorph(reindeer-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands, while human in shape, have tough little hoof-like nails.") {
	};

	public static AbstractArmType ALLIGATOR_MORPH = new AbstractArmType(BodyCoveringType.ALLIGATOR_SCALES,
			Race.ALLIGATOR_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("scaled", "reptile-like"),
			Util.newArrayListOfValues("scaled", "reptile-like"),
			"hand",
			"hands",
			Util.newArrayListOfValues("scaled"),
			Util.newArrayListOfValues("feminine", "scaled"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("scaled"),
			Util.newArrayListOfValues("feminine", "scaled"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
				+ " [npc.she] [npc.verb(see)] [npc.her] new scales growing over the backs of [npc.her] hands as sharp claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in little scales, and at [npc.her] upper-biceps, [npc.her] scales smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldGatorMorph(alligator-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, alligator-like hands, complete with little claws.") {
	};

	public static AbstractArmType SQUIRREL_MORPH = new AbstractArmType(BodyCoveringType.SQUIRREL_FUR,
			Race.SQUIRREL_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("soft", "squirrel-like", "claw-like", "furry", "rodent"),
			Util.newArrayListOfValues("soft", "feminine", "squirrel-like", "claw-like", "furry", "rodent"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("soft", "clawed", "rodent"),
			Util.newArrayListOfValues("soft", "feminine", "clawed", "rodent"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new fur growing over the backs of [npc.her] hands as sharp little claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in little pink pads, and at [npc.her] upper-biceps, [npc.her] fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldSquirrelMorph(squirrel-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, squirrel-like hands, complete with claws.") {
	};

	public static AbstractArmType RAT_MORPH = new AbstractArmType(BodyCoveringType.RAT_FUR,
			Race.RAT_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("soft", "rat-like", "claw-like", "furry", "rodent"),
			Util.newArrayListOfValues("soft", "feminine", "rat-like", "claw-like", "furry", "rodent"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("soft", "rat-like", "claw-like", "furry", "rodent"),
			Util.newArrayListOfValues("soft", "feminine", "rat-like", "claw-like", "furry", "rodent"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] fur growing over the backs of [npc.her] hands as sharp little claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in little pink pads, and at [npc.her] upper-biceps, [npc.her] new fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldRatMorph(rat-like arms and hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, rat-like hands, complete with claws.") {
	};

	public static AbstractArmType RABBIT_MORPH = new AbstractArmType(BodyCoveringType.RABBIT_FUR,
			Race.RABBIT_MORPH,
			"arm",
			"arms",
			Util.newArrayListOfValues("furry", "fur-coated"),
			Util.newArrayListOfValues("furry", "fur-coated"),
			"hand",
			"hands",
			Util.newArrayListOfValues("rabbit-like", "paw-like", "furry"),
			Util.newArrayListOfValues("soft", "feminine", "rabbit-like", "paw-like", "furry"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("rabbit-like", "paw-like", "furry"),
			Util.newArrayListOfValues("soft", "feminine", "rabbit-like", "paw-like", "furry"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, looking down,"
					+ " [npc.she] [npc.verb(see)] [npc.her] new fur growing over the backs of [npc.her] hands as blunt, rabbit-like claws push out to replace [npc.her] fingernails."
				+ " [npc.Her] palms rapidly transform to be covered in soft little pads, and at [npc.her] upper-biceps, [npc.her] new fur smoothly transitions into the [npc.skin] that's covering the rest of [npc.her] body.
"
				+ "As the transformation comes to an end, [npc.nameIsFull] left with anthropomorphic, [style.boldRabbitMorph(rabbit-like arms and paw-like hands)], which are covered in [npc.armFullDescription].",
			"[npc.She] [npc.has] [npc.armRows] arms, which are [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " [npc.Her] hands are formed into anthropomorphic, rabbit-like hands, complete with blunt little claws.") {
	};

	public static AbstractArmType BAT_MORPH = new AbstractArmType(BodyCoveringType.BAT_SKIN,
			Race.BAT_MORPH,
			"wing",
			"wings",
			Util.newArrayListOfValues("bat-like"),
			Util.newArrayListOfValues("bat-like"),
			"hand",
			"hands",
			Util.newArrayListOfValues("bat-like"),
			Util.newArrayListOfValues("feminine", "bat-like"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("bat-like"),
			Util.newArrayListOfValues("soft", "feminine", "bat-like"),
			"Within a matter of moments, a layer of [npc.armFullDescription] has quickly grown over them, and, staring at [npc.her] hands in shock,"
					+ " [npc.name] [npc.verb(watch)] [npc.her] fingers narrowing down and growing longer and longer as a tough membrane of skin starts to grow between them."
				+ " [npc.She] [npc.verb(cry)] out in alarm as [npc.she] [npc.verb(feel)] [npc.her] bones growing and snapping into a new form, and within moments,"
					+ " [npc.her] hands and arms have completely transformed into huge, bat-like wings."
				+ " Where [npc.her] hands once were, two of [npc.her] fingers have shrunk down into the middle-joint of [npc.her] new appendages,"
					+ " leaving [npc.herHim] with two small forefingers and an opposable thumb, each of which ends in a little claw."
				+ " Where [npc.her] new wings meet [npc.her] body at the shoulder, [npc.her] [npc.armFullDescription] smoothly covers the transition into the [npc.skin] that's covering the rest of [npc.her] torso.
"
				+ "[npc.Name] now [npc.has] huge [style.boldBatMorph(bat-like wings)] in place of arms, which are covered in [npc.armFullDescription].",
			"In place of arms and hands, [npc.she] [npc.has] [npc.armRows] huge bat-like wings, [npc.materialCompositionDescriptor] [npc.armFullDescription(true)]."
				+ " Where [npc.her] hands should be, [npc.she] [npc.has] two forefingers and a thumb, each of which ends in a little blunt claw."
				+ " Although slightly less dexterous than a human hand, [npc.sheIs] still able to use [npc.her] digits to form a hand-like grip.") {
		@Override
		public boolean allowsFlight() {
			return true;
		}
	};

	public static AbstractArmType HARPY = new AbstractArmType(BodyCoveringType.FEATHERS,
			Race.HARPY,
			"wing",
			"wings",
			Util.newArrayListOfValues("feathered", "bird-like"),
			Util.newArrayListOfValues("feathered", "bird-like"),
			"hand",
			"hands",
			Util.newArrayListOfValues("feathered"),
			Util.newArrayListOfValues("feminine", "feathered"),
			"finger",
			"fingers",
			Util.newArrayListOfValues("feathered"),
			Util.newArrayListOfValues("feminine", "feathered"),
			"Within a matter of moments, a thick layer of [npc.armFullDescription] quickly sprouts out all over them, and, looking down, [npc.she] [npc.verb(see)] [npc.her] new feathers growing over the backs of [npc.her] hands as well."
				+ " Just as [npc.she] thinks that the transformation has finished, [npc.she] [npc.verb(cry)] out in shock as [npc.her] bones grow and snap into a new form."
				+ " Thankfully, the transformation is quickly over, leaving [npc.herHim] with a pair of huge, feathered wings in place of arms."
				+ " Where [npc.her] hands once were, two of [npc.her] fingers have shrunk down into the middle-joint of [npc.her] appendages, leaving [npc.herHim] with two feathered forefingers and an opposable thumb,"
					+ " each of which ends in a blunt claw."
				+ " Where [npc.her] new wings meet [npc.her] body at the shoulder, [npc.her] feathers smoothly cover the transition into the [npc.skin] that's covering the rest of [npc.her] torso.
"
				+ "[npc.Name] now [npc.has] huge [style.boldHarpy(harpy wings)] in place of arms, which are covered in [npc.armFullDescription].",
			"In place of arms and hands, [npc.she] [npc.has] [npc.armRows] huge wings, which are [npc.materialCompositionDescriptor] beautiful [npc.armFullDescription(true)]."
				+ " Where [npc.her] hands should be, [npc.she] [npc.has] two feathered forefingers and a thumb, each of which ends in a little blunt claw."
				+ " Although slightly less dexterous than a human hand, [npc.sheIs] still able to use [npc.her] digits to form a hand-like grip.") {
		@Override
		public boolean allowsFlight() {
			return true;
		}

		private BodyPartClothingBlock clothingBlock = new BodyPartClothingBlock(
				Util.newArrayListOfValues(
						InventorySlot.HAND,
						InventorySlot.WRIST),
				Race.HARPY,
				"Due to the fact that [npc.nameHasFull] bird-like wings instead of arms, only specialist clothing can be worn in this slot.",
				Util.newArrayListOfValues(ItemTag.FITS_HARPY_WINGS_EXCLUSIVE, ItemTag.FITS_HARPY_WINGS));
		@Override
		public BodyPartClothingBlock getBodyPartClothingBlock() {
			return clothingBlock;
		}
	};

	private static List<AbstractArmType> allArmTypes;
	private static Map<AbstractArmType, String> armToIdMap = new HashMap<>();
	private static Map<String, AbstractArmType> idToArmMap = new HashMap<>();

	static {
		allArmTypes = new ArrayList<>();

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

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

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

					armToIdMap.put(ct, f.getName());
					idToArmMap.put(f.getName(), ct);

					allArmTypes.add(ct);

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

	public static AbstractArmType getArmTypeFromId(String id) {
		if(id.equals("IMP")) {
			return ArmType.DEMON_COMMON;
		}
		if(id.equals("LYCAN")) {
			return ArmType.WOLF_MORPH;
		}

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

	public static String getIdFromArmType(AbstractArmType armType) {
		return armToIdMap.get(armType);
	}

	public static List<AbstractArmType> getAllArmTypes() {
		return allArmTypes;
	}

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

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

		List<AbstractArmType> types = new ArrayList<>();
		for(AbstractArmType type : ArmType.getAllArmTypes()) {
			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
 * @author Innoxia
 */
public class AssType {

	public static AbstractAssType HUMAN = new AbstractAssType(BodyCoveringType.HUMAN,
			Race.HUMAN,
			AnusType.HUMAN,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldHuman(human)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a human, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType ANGEL = new AbstractAssType(BodyCoveringType.ANGEL,
			Race.ANGEL,
			AnusType.ANGEL,
			null,
			null,
			Util.newArrayListOfValues("angelic"),
			Util.newArrayListOfValues("angelic", "perfect"),
			"[npc.She] now [npc.has] an [style.boldAngel(angelic)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] an angelic, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType DEMON_COMMON = new AbstractAssType(BodyCoveringType.DEMON_COMMON,
			Race.DEMON,
			AnusType.DEMON_COMMON,
			null,
			null,
			Util.newArrayListOfValues("demonic"),
			Util.newArrayListOfValues("demonic", "perfect"),
			"[npc.She] now [npc.has] a [style.boldDemon(demonic)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a demonic, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType DOG_MORPH = new AbstractAssType(BodyCoveringType.CANINE_FUR,
			Race.DOG_MORPH,
			AnusType.DOG_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldDogMorph(canine)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a canine, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType COW_MORPH = new AbstractAssType(BodyCoveringType.BOVINE_FUR,
			Race.COW_MORPH,
			AnusType.COW_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldCowMorph(bovine)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a bovine, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType SQUIRREL_MORPH = new AbstractAssType(BodyCoveringType.SQUIRREL_FUR,
			Race.SQUIRREL_MORPH,
			AnusType.SQUIRREL_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldSquirrelMorph(squirrel-like)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a squirrel-like, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType RAT_MORPH = new AbstractAssType(BodyCoveringType.RAT_FUR,
			Race.RAT_MORPH,
			AnusType.RAT_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldRatMorph(rat-like)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a rat-like, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType RABBIT_MORPH = new AbstractAssType(BodyCoveringType.RABBIT_FUR,
			Race.RABBIT_MORPH,
			AnusType.RABBIT_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldRabbitMorph(rabbit-like)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a rabbit-like, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType BAT_MORPH = new AbstractAssType(BodyCoveringType.BAT_FUR,
			Race.BAT_MORPH,
			AnusType.BAT_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldBatMorph(bat-like)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a bat-like, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType ALLIGATOR_MORPH = new AbstractAssType(BodyCoveringType.ALLIGATOR_SCALES,
			Race.ALLIGATOR_MORPH,
			AnusType.ALLIGATOR_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] an [style.boldGatorMorph(alligator-like)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] an alligator-like, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType WOLF_MORPH = new AbstractAssType(BodyCoveringType.LYCAN_FUR,
			Race.WOLF_MORPH,
			AnusType.WOLF_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldWolfMorph(lupine)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a lupine, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType FOX_MORPH = new AbstractAssType(BodyCoveringType.FOX_FUR,
			Race.FOX_MORPH,
			AnusType.FOX_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldFoxMorph(vulpine)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a vulpine, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType CAT_MORPH = new AbstractAssType(BodyCoveringType.FELINE_FUR,
			Race.CAT_MORPH,
			AnusType.CAT_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldCatMorph(feline)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a feline, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType HORSE_MORPH = new AbstractAssType(BodyCoveringType.HORSE_HAIR,
			Race.HORSE_MORPH,
			AnusType.HORSE_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] an [style.boldHorseMorph(equine)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] an equine, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType REINDEER_MORPH = new AbstractAssType(BodyCoveringType.REINDEER_FUR,
			Race.REINDEER_MORPH,
			AnusType.REINDEER_MORPH,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] a [style.boldReindeerMorph(reindeer-like)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] a reindeer-like, [npc.anusFullDescription(true)]"){
	};

	public static AbstractAssType HARPY = new AbstractAssType(BodyCoveringType.FEATHERS,
			Race.HARPY,
			AnusType.HARPY,
			null,
			null,
			Util.newArrayListOfValues(""),
			Util.newArrayListOfValues(""),
			"[npc.She] now [npc.has] an [style.boldHarpy(avian)], [npc.assholeFullDescription].",
			"[npc.SheHasFull] an avian, [npc.anusFullDescription(true)]"){
	};

	private static List<AbstractAssType> allAssTypes;
	private static Map<AbstractAssType, String> assToIdMap = new HashMap<>();
	private static Map<String, AbstractAssType> idToAssMap = new HashMap<>();

	static {
		allAssTypes = new ArrayList<>();

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

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

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

					assToIdMap.put(ct, f.getName());
					idToAssMap.put(f.getName(), ct);

					allAssTypes.add(ct);

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

	public static AbstractAssType getAssTypeFromId(String id) {
		if(id.equals("IMP")) {
			return AssType.DEMON_COMMON;
		}

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

	public static String getIdFromAssType(AbstractAssType assType) {
		return assToIdMap.get(assType);
	}

	public static List<AbstractAssType> getAllAssTypes() {
		return allAssTypes;
	}

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

		List<AbstractAssType> types = new ArrayList<>();
		for(AbstractAssType type : AssType.getAllAssTypes()) {
			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.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

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

/**
 * @since 0.2.8
 * @version 0.2.8
 * @author Pimgd
 */
public class BodyCoveringTemplate {
	public final String determiner;
	public final String namePlural;
	public final String nameSingular;
	public final List<CoveringModifier> naturalModifiers;
	public final List<CoveringModifier> extraModifiers;
	public final List<Colour> naturalColoursPrimary;
	public final List<Colour> dyeColoursPrimary;
	public final List<Colour> naturalColoursSecondary;
	public final List<Colour> dyeColoursSecondary;
	public final List<CoveringPattern> naturalPatterns;
	public final List<CoveringPattern> dyePatterns;
	public final boolean isDefaultPlural;

	public BodyCoveringTemplate(
			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;

		this.naturalModifiers = getImmutableListFromNullableList(naturalModifiers);
		this.extraModifiers = getImmutableListFromNullableList(extraModifiers);

		this.naturalPatterns = getImmutableListFromNullableList(naturalPatterns, () -> Arrays.asList(CoveringPattern.NONE));
		List<CoveringPattern> dyePatternsList = getListFromNullableList(dyePatterns);
		dyePatternsList.removeAll(this.naturalPatterns);
		this.dyePatterns = Collections.unmodifiableList(dyePatternsList);

		this.naturalColoursPrimary = getImmutableListFromNullableList(naturalColoursPrimary);
		this.dyeColoursPrimary = getImmutableListFromNullableList(dyeColoursPrimary);
		this.naturalColoursSecondary = getImmutableListFromNullableList(naturalColoursSecondary);
		this.dyeColoursSecondary = getImmutableListFromNullableList(dyeColoursSecondary);
	}

	private <T> List<T> getImmutableListFromNullableList(List<T> nullableList) {
		return Collections.unmodifiableList(getListFromNullableList(nullableList));
	}

	private <T> List<T> getImmutableListFromNullableList(List<T> nullableList, Supplier<List<T>> replacement) {
		return Collections.unmodifiableList(Optional.ofNullable(nullableList).orElseGet(replacement));
	}

	private <T> List<T> getListFromNullableList(List<T> nullableList) {
		// Have to wrap in a new ArrayList, as the nullableList might be one of the preset lists from CoveringPattern (such as CoveringPattern.allHairCoveringPatterns).
		// If not wrapped in a new ArrayList, the "dyePatternsList.removeAll(this.naturalPatterns);" line removes elements from the underlying list.
		return new ArrayList<>(Optional.ofNullable(nullableList).orElse(new ArrayList<>()));
	}
}


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

import java.util.Arrays;
import java.util.List;

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.Util;

/**
 * @since 0.2.8
 * @version 0.2.8
 * @author Pimgd
 */
public class BodyCoveringTemplateFactory {

	public static BodyCoveringTemplate createSkin(List<CoveringPattern> coverPatterns, List<Colour> naturalPrimaryColors, List<Colour> naturalSecondaryColours, List<Colour> dyeColours) {
		return new BodyCoveringTemplate("a layer of",
				false,
				"skin",
				"skin",
				Util.newArrayListOfValues(CoveringModifier.SMOOTH),
				null,
				coverPatterns,
				CoveringPattern.allStandardCoveringPatterns,
				naturalPrimaryColors,
				dyeColours,
				naturalSecondaryColours,
				dyeColours);
	}

	public static BodyCoveringTemplate createTopSkin(List<CoveringPattern> coverPatterns, List<Colour> skinColors) {
		return createSkin(coverPatterns, skinColors, skinColors, null);
	}

	public static BodyCoveringTemplate createBottomSkin(List<Colour> skinColors) {
		return createSkin(Util.newArrayListOfValues(CoveringPattern.NONE), skinColors, null, Colour.allSkinColours);
	}

	public static BodyCoveringTemplate createSlime(CoveringPattern basePattern, List<CoveringPattern> coverPatterns) {
		return new BodyCoveringTemplate("a layer of",
				false,
				"slime",
				"slime",
				Util.newArrayListOfValues(CoveringModifier.GOOEY),
				null,
				Util.newArrayListOfValues(basePattern),
				coverPatterns,
				Colour.naturalSlimeColours,
				Colour.dyeSlimeColours,
				Colour.naturalSlimeColours,
				Colour.dyeSlimeColours);
	}

	public static BodyCoveringTemplate createFurSkin(List<CoveringModifier> modifiers, List<CoveringPattern> patterns) {
		return createFur("a layer of", "fur", modifiers, patterns);
	}

	private static BodyCoveringTemplate createFur(String determiner, String name, List<CoveringModifier> modifiers, List<CoveringPattern> patterns) {
		return new BodyCoveringTemplate(determiner,
				false,
				name,
				name,
				modifiers,
				null,
				patterns,
				CoveringPattern.allStandardCoveringPatterns,
				Colour.naturalFurColours,
				Colour.allCoveringColours,
				Colour.naturalFurColours,
				Colour.allCoveringColours);
	}

	private static BodyCoveringTemplate createHair(String determiner, String name, List<CoveringModifier> modifiers, List<CoveringPattern> patterns) {
		return new BodyCoveringTemplate(determiner,
				false,
				name,
				name,
				modifiers,
				null,
				patterns,
				CoveringPattern.allHairCoveringPatterns,
				Colour.naturalHairColours,
				Colour.allCoveringColours,
				Colour.naturalHairColours,
				Colour.allCoveringColours);
	}

	private static BodyCoveringTemplate createHairWithoutPatterns(String determiner, String name, CoveringModifier modifier) {
		return createHair(determiner, name, Util.newArrayListOfValues(modifier), Util.newArrayListOfValues(CoveringPattern.NONE));
	}

	public static BodyCoveringTemplate createHeadHair(CoveringModifier modifier) {
		return createHairWithoutPatterns("a head of", "hair", modifier);
	}

	public static BodyCoveringTemplate createFurHeadHair(CoveringModifier modifier) {
		return createHairWithoutPatterns("a layer of", "hair", modifier);
	}

	public static BodyCoveringTemplate createBodyHair(CoveringModifier modifier) {
		return createHairWithoutPatterns("a layer of", "hair", modifier);
	}

	public static BodyCoveringTemplate createElemental(String name, CoveringModifier modifier, Colour... naturalHairColours) {
		return new BodyCoveringTemplate("",
				false,
				name,
				name,
				Util.newArrayListOfValues(modifier),
				null,
				Util.newArrayListOfValues(CoveringPattern.NONE),
				null,
				Arrays.asList(naturalHairColours),
				null,
				null,
				null);
	}

	public static BodyCoveringTemplate createOrificeSkin(CoveringPattern pattern) {
		return new BodyCoveringTemplate("a layer of",
				false,
				"skin",
				"skin",
				Util.newArrayListOfValues(CoveringModifier.SMOOTH),
				null,
				pattern == null ? null : Util.newArrayListOfValues(pattern),
				null,
				Colour.allSkinColours,
				null,
				Util.newArrayListOfValues(Colour.ORIFICE_INTERIOR),
				Colour.allSkinColours);
	}

	public static BodyCoveringTemplate createPenisSkin() {
		return new BodyCoveringTemplate("a layer of",
				false,
				"skin",
				"skin",
				Util.newArrayListOfValues(CoveringModifier.SMOOTH),
				null,
				Util.newArrayListOfValues(
						CoveringPattern.NONE),
				Util.newArrayListOfValues(
						CoveringPattern.MARKED,
						CoveringPattern.MOTTLED,
						CoveringPattern.SPOTTED,
						CoveringPattern.STRIPED),
				Colour.allSkinColours,
				null,
				Util.newArrayListOfValues(Colour.ORIFICE_INTERIOR),
				Colour.allSkinColours);
	}

	public static BodyCoveringTemplate createEyeIrisesWithCustomColors(List<Colour> naturalIrisColors, List<Colour> dyeIrisColours, boolean heteroIsExtra) {
		List<CoveringPattern> natural = Util.newArrayListOfValues(CoveringPattern.EYE_IRISES, CoveringPattern.EYE_IRISES_HETEROCHROMATIC);
		List<CoveringPattern> extra = null;
		if (heteroIsExtra) {
			natural = Util.newArrayListOfValues(CoveringPattern.EYE_IRISES);
			extra = Util.newArrayListOfValues(CoveringPattern.EYE_IRISES_HETEROCHROMATIC);
		}
		return new BodyCoveringTemplate("a pair of",
				true,
				"eyes",
				"eye",
				Util.newArrayListOfValues(CoveringModifier.EYE),
				null,
				natural,
				extra,
				naturalIrisColors,
				dyeIrisColours,
				naturalIrisColors,
				dyeIrisColours);
	}

	public static BodyCoveringTemplate createEyeIrisesWithCustomColors(List<Colour> naturalIrisColours, List<Colour> dyeIrisColours) {
		return createEyeIrisesWithCustomColors(naturalIrisColours, dyeIrisColours, true);
	}

	public static BodyCoveringTemplate createEyeIrises() {
		return createEyeIrisesWithCustomColors(Colour.naturalIrisColours, Colour.dyeIrisColours, true);
	}

	public static BodyCoveringTemplate createEyeIrisesHeterochromiaNaturallyOccurring() {
		return createEyeIrisesWithCustomColors(Colour.naturalIrisColours, Colour.dyeIrisColours, false);
	}
}