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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.BodyCoveringType;
import com.lilithsthrone.game.character.body.types.BodyPartTypeInterface;

/**
 * @since 0.1.0
 * @version 0.3.1
 * @author Innoxia
 */
public interface BodyPartInterface {

	public abstract BodyPartTypeInterface getType();

	public abstract String getDeterminer(GameCharacter gc);

	public abstract String getName(GameCharacter gc);

	public abstract String getNameSingular(GameCharacter gc);

	public abstract String getNamePlural(GameCharacter gc);

	public abstract String getDescriptor(GameCharacter gc);

	/** @return The name of this body part with its descriptor. */
	public default String getName(GameCharacter gc, boolean withDescriptor) {
		if(withDescriptor) {
			if(getType().getDescriptor(gc)==null) {
				return getType().getName(gc);
			}

			return (getType().getDescriptor(gc).length() > 0 ? getType().getDescriptor(gc) + " " : "") + getType().getName(gc);
		} else {
			return getType().getName(gc);
		}
	}

	/** @return The name of this body part with its descriptor. */
	public default String getNameSingular(GameCharacter gc, boolean withDescriptor) {
		if(withDescriptor) {
			if(getType().getDescriptor(gc)==null)
				return getType().getNameSingular(gc);

			return (getType().getDescriptor(gc).length() > 0 ? getType().getDescriptor(gc) + " " : "") + getType().getNameSingular(gc);
		} else {
			return getType().getNameSingular(gc);
		}
	}

	public default BodyCoveringType getBodyCoveringType(GameCharacter gc) {
		return getType().getBodyCoveringType(gc.getBody());
	}

	public default BodyCoveringType getBodyCoveringType(Body body) {
		return getType().getBodyCoveringType(body);
	}

	/**
	 * @return true if this part is (near enough to) 100% animal-like, with no anthropomorphic qualities at all. This will almost certainly only ever be seen on feral characters or characters who have a non-bipedal body.
	 */
	public boolean isBestial(GameCharacter owner);

}

repo_name: klorpa/Pedo

license: unknown


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

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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.AbstractBreastType;
import com.lilithsthrone.game.character.body.valueEnums.BreastShape;
import com.lilithsthrone.game.character.body.valueEnums.Capacity;
import com.lilithsthrone.game.character.body.valueEnums.CupSize;
import com.lilithsthrone.game.character.body.valueEnums.FluidRegeneration;
import com.lilithsthrone.game.character.body.valueEnums.Lactation;
import com.lilithsthrone.game.character.body.valueEnums.NippleShape;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Units;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.0
 * @version 0.3.2
 * @author Innoxia
 */
public class Breast implements BodyPartInterface {

	public static final int MAXIMUM_BREAST_ROWS = 5;
	public static final int MAXIMUM_NIPPLES_PER_BREAST = 4;

	protected AbstractBreastType type;
	protected BreastShape shape;
	protected int size;
	protected int rows;
	protected int milkStorage;
	protected float milkStored;
	protected int milkRegeneration;
	protected int nippleCountPerBreast;

	protected Nipples nipples;
	protected FluidMilk milk;

	/**
	 * @param size in inches from bust to underbust using the UK system.
	 * @param lactation in mL.
	 */
	public Breast(AbstractBreastType type, BreastShape shape, int size, int milkStorage, int rows, int nippleSize, NippleShape nippleShape, int areolaeSize, int nippleCountPerBreast, float capacity, int elasticity, int plasticity, boolean virgin) {
		this.type = type;
		this.shape = shape;
		this.size = size;
		this.milkStorage = milkStorage;
		milkStored = milkStorage;
		milkRegeneration = FluidRegeneration.ONE_AVERAGE.getMedianRegenerationValuePerDay();
		this.rows = rows;
		this.nippleCountPerBreast = nippleCountPerBreast;

		nipples = new Nipples(type.getNippleType(), nippleSize, nippleShape, areolaeSize, Lactation.getLactationFromInt(milkStorage).getAssociatedWetness().getValue(), capacity, elasticity, plasticity, virgin, false);

		milk = new FluidMilk(type.getFluidType(), false);
	}

	@Override
	public AbstractBreastType getType() {
		return type;
	}

	public BreastShape getShape() {
		return shape;
	}

	public String setShape(GameCharacter owner, BreastShape shape) {
		if (shape == getShape()) {
			if(owner==null) {
				return "";
			}
			return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already [npc.has] "+shape.getDescriptor()+" breasts, so nothing happens...)]
");
		}

		this.shape = shape;

		if(!owner.hasBreasts()) {
			return UtilText.parse(owner,
					"
"
						+ "A strange tingling feeling rises up into [npc.namePos] [npc.breasts+], but as [npc.she] [npc.do]n't have any breasts, nothing seems to happen...
"
						+ "If [npc.she] ever [npc.verb(grow)] any, [npc.name] will now have [style.boldSex("+shape.getDescriptor()+" breasts)]!"
					+ "
");
		}

		return UtilText.parse(owner,
				"
"
					+ "A strange tingling feeling rises up into [npc.namePos] [npc.breasts+], and before [npc.she] [npc.verb(know)] what's happening, they've transformed into a new shape...
"
					+ "[npc.Name] now [npc.has] [style.boldSex("+shape.getDescriptor()+" breasts)]!"
				+ "
");

	}

	public Nipples getNipples() {
		return nipples;
	}

	public FluidMilk getMilk() {
		return milk;
	}

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

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

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

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

	public String getDescriptor(GameCharacter owner) {
		List<String> list = new ArrayList<>();

		if(nippleCountPerBreast == 4) {
			list.add("quad-nippled");
		} else if(nippleCountPerBreast == 3) {
			list.add("tri-nippled");
		} else if(nippleCountPerBreast == 2) {
			list.add("dual-nippled");
		}

		list.add(type.getDescriptor(owner));
		list.add(this.getSize().getDescriptor());
		list.add(this.getShape().getDescriptor());

		return Util.randomItemFrom(list);
	}

	public boolean hasBreasts() {
		return size>=CupSize.getMinimumCupSizeForBreasts().getMeasurement();
	}

	public String setType(GameCharacter owner, AbstractBreastType type) {
		if(!Main.game.isStarted() || owner==null) {
			this.type = type;
			nipples.setType(owner, type.getNippleType());
			milk.setType(type.getFluidType());
			if(owner!=null) {
				owner.resetAreaKnownByCharacters(CoverableArea.BREASTS);
				owner.resetAreaKnownByCharacters(CoverableArea.NIPPLES);
				owner.postTransformationCalculation();
			}
			return "";
		}

		if (type == getType()) {
			return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already [npc.has] the breasts of [npc.a_breastRace], so nothing happens...)]
");
		}

		UtilText.transformationContentSB.setLength(0);

		UtilText.transformationContentSB.append(
				"
"
					+ "The front of [npc.namePos] torso suddenly feels extremely soft and sensitive, and [npc.she] can't help but let out [npc.a_moan+] as [npc.she] [npc.verb(feel)] a transformation start to take place."
					+" [npc.Her] nipples and areolae tingle and harden, causing [npc.herHim] to pant and let out a lewd [npc.moan] as the intense transformation runs its course."
					+ " While there's no change to the [npc.breastFullDescription] which covers [npc.her] [npc.breasts],"
						+ " [npc.she] [npc.verb(feel)] #IFnpc.hasBreasts()#THENtheir#ELSEits#ENDIF interior structure shifting and changing into a new form."
					+ " After just a moment, the transformation ends, leaving [npc.herHim] with [npc.totalNipples] new nipples."
					+ "
");

		// Parse existing content before transformation:
		String s = UtilText.parse(owner, UtilText.transformationContentSB.toString());
		UtilText.transformationContentSB.setLength(0);
		UtilText.transformationContentSB.append(s);
		this.type = type;
		nipples.setType(owner, type.getNippleType());
		milk.setType(type.getFluidType());
		owner.resetAreaKnownByCharacters(CoverableArea.BREASTS);
		owner.resetAreaKnownByCharacters(CoverableArea.NIPPLES);

		UtilText.transformationContentSB.append(type.getTransformationDescription(owner)+"
");

		return UtilText.parse(owner, UtilText.transformationContentSB.toString())
				+ "
"
				+ owner.postTransformationCalculation(false)
				+ "
";
	}

	// Size:

	public CupSize getSize() {
		return CupSize.getCupSizeFromInt(size);
	}

	public int getRawSizeValue() {
		return size;
	}

	/**
	 * Sets the raw size value. Value is bound to >=0 && <=CupSize.MAXIMUM.getMeasurement()
	 *
	 * @param size Value to set size to.
	 * @return description of size change
	 */
	public String setSize(GameCharacter owner, int size) {
		boolean hadBreasts = hasBreasts();

		int oldSize = this.size;
		this.size = Math.max(0, Math.min(size, CupSize.getMaximumCupSize().getMeasurement()));
		int sizeChange = this.size - oldSize;
		if(owner==null) {
			this.size = size;
			return "";
		}

		if (sizeChange == 0) {
			return UtilText.parse(owner, "
[style.colourDisabled(The size of [npc.namePos] [npc.breasts] doesn't change...)]
");
		}

		String sizeDescriptor = getSize().getDescriptor();
		if (sizeChange > 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a tingling heat quickly spreading throughout [npc.her] torso, and [npc.she] can't help but let out [npc.a_moan+] as [npc.her] "
						+ (hadBreasts
								? "[npc.breasts] swell up and [style.boldGrow(grow larger)].
"
								: "chest swells up, and before [npc.she] [npc.verb(know)] what's happening, a pair of breasts have [style.boldGrow(grown)] out of [npc.her] previously-flat torso.
")
						+ "[npc.Name] now [npc.has] [style.boldSex(" + sizeDescriptor + (getSize().getMeasurement()>CupSize.AA.getMeasurement()?", "+getSize().getCupSizeName()+"-cup":"") + " breasts)]!"
					+ "
");
		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a tingling heat quickly spreading throughout [npc.her] torso,"
							+ " and [npc.she] can't help but let out a frustrated [npc.moan] as [npc.her] [npc.breasts] shrink down and [style.boldShrink(get smaller)].
"
						+ (this.size==0
							? "[npc.Name] now [npc.has] [style.boldSex(a completely flat chest)]!"
							: "[npc.Name] now [npc.has] [style.boldSex(" + sizeDescriptor + (getSize().getMeasurement()>CupSize.AA.getMeasurement()?", "+getSize().getCupSizeName()+"-cup":"") + " breasts)]!")
					+ "
");
		}
	}

	// Lactation:

	public Lactation getMilkStorage() {
		return Lactation.getLactationFromInt(milkStorage);
	}

	public int getRawMilkStorageValue() {
		return milkStorage;
	}

	/**
	 * Sets the milkStorage. Value is bound to >=0 && <=Lactation.SEVEN_MONSTROUS_AMOUNT_POURING.getMaximumValue()
	 */
	public String setMilkStorage(GameCharacter owner, int milkStorage) {
		int oldLactation = this.milkStorage;
		this.milkStorage = Math.max(0, Math.min(milkStorage, Lactation.SEVEN_MONSTROUS_AMOUNT_POURING.getMaximumValue()));
		int lactationChange = this.milkStorage - oldLactation;
		if(owner==null) {
			return "";
		}

		if (lactationChange == 0) {
			return UtilText.parse(owner, "
[style.colourDisabled(The amount of [npc.milk] that [npc.nameIsFull] able to produce doesn't change...)]
");
		}

		String lactationDescriptor = getMilkStorage().getDescriptor();
		if (lactationChange > 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange bubbling and churning taking place deep within [npc.her] [npc.breasts], and [npc.a_moan+] drifts out from between [npc.her] [npc.lips] as a few drops of [npc.milk] suddenly leak"
							+ " from [npc.her] [npc.nipples]; clear evidence that that [npc.her] [npc.milk] production has [style.boldGrow(increased)].
"
						+ "[npc.NameIsFull] now able to produce [style.boldSex(" + lactationDescriptor + " [npc.milk])]!"
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange sucking sensation deep within [npc.her] [npc.breasts],"
							+ " and a frustrated sigh drifts out from between [npc.her] [npc.lips] as [npc.she] [npc.verb(realise)] that [npc.sheIs] feeling [npc.her] [npc.milk] production [style.boldShrink(drying up)].
"
						+ "[npc.NameIsFull] now able to produce [style.boldSex(" + lactationDescriptor + " [npc.milk])]."
					+ "
");
		}
	}

	// Stored milk:

	public Lactation getStoredMilk() {
		return Lactation.getLactationFromInt((int) milkStored);
	}

	public float getRawStoredMilkValue() {
		return milkStored;
	}

	/**
	 * Sets the milkStorage. Value is bound to >=0 && <=getRawMilkStorageValue()
	 */
	public String setStoredMilk(GameCharacter owner, float milkStored) {
		float oldStoredMilk = this.milkStored;
		this.milkStored = Math.max(0, (Math.min(milkStored, getRawMilkStorageValue())));
		float lactationChange = oldStoredMilk - this.milkStored;

		if(owner==null) {
			return "";
		}

		if (lactationChange <= 0) {
			return "";
		} else {
			return UtilText.parse(owner,
					"
"
							+ UtilText.returnStringAtRandom(
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.namePos] [npc.milk] squirts out of [npc.her] [npc.nipples+].",
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.milk+] squirts out of [npc.namePos] [npc.nipples+].",
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.namePos] [npc.milk] leaks out of [npc.her] [npc.nipples+].",
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.milk+] leaks out of [npc.namePos] [npc.nipples+].")
					+ ""
					+ (this.milkStored==0
						?"
[npc.Name] now [npc.has] no more [npc.milk] stored in [npc.her] breasts!"
						:"")
					+ "
");
		}
	}

	// Regeneration:

	public FluidRegeneration getLactationRegeneration() {
		return FluidRegeneration.getFluidRegenerationFromInt(milkRegeneration);
	}

	public int getRawLactationRegenerationValue() {
		return milkRegeneration;
	}

	/**
	 * Sets the milkRegeneration. Value is bound to >=0 && <=FluidRegeneration.FOUR_VERY_RAPID.getMaximumRegenerationValuePerDay()
	 */
	public String setLactationRegeneration(GameCharacter owner, int milkRegeneration) {
		int oldRegeneration = this.milkRegeneration;
		this.milkRegeneration = Math.max(0, Math.min(milkRegeneration, FluidRegeneration.FOUR_VERY_RAPID.getMaximumRegenerationValuePerDay()));
		int regenerationChange = this.milkRegeneration - oldRegeneration;

		if(owner==null) {
			return "";
		}

		if (regenerationChange == 0) {
			return UtilText.parse(owner, "
[style.colourDisabled([npc.namePos] rate of [npc.milk] regeneration doesn't change...)]
");
		}

		String regenerationDescriptor = getLactationRegeneration().getName();
		if (regenerationChange > 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] an alarming bubbling and churning taking place deep within [npc.her] [npc.breasts], and [npc.a_moan+] drifts out from between [npc.her] [npc.lips] as a few drops of [npc.milk] suddenly leak"
							+ " from [npc.her] [npc.nipples]; clear evidence that that [npc.her] [npc.milk] regeneration has [style.boldGrow(increased)].
"
						+ "[npc.NamePos] rate of [npc.milk] regeneration is now [style.boldSex(" + regenerationDescriptor + ")]!"
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange sucking sensation deep within [npc.her] [npc.breasts],"
							+ " and a frustrated sigh drifts out from between [npc.her] [npc.lips] as [npc.she] realises that [npc.sheIs] feeling [npc.her] [npc.milk] regeneration [style.boldShrink(decreasing)].
"
						+ "[npc.NamePos] rate of [npc.milk] regeneration is now [style.boldSex(" + regenerationDescriptor + ")]!"
					+ "
");
		}
	}

	// Rows:

	public int getRows() {
		return rows;
	}

	public String setRows(GameCharacter owner, int rows) {
		rows = Math.max(1, Math.min(rows, MAXIMUM_BREAST_ROWS));

		if(owner==null) {
			this.rows = rows;
			return "";
		}

		if(rows == getRows()) {
			return UtilText.parse(owner, "
[style.colourDisabled(The number of breast rows [npc.nameHasFull] doesn't change...)]
");
		}

		String transformation = "";

		int rowsDifference = Math.abs(rows - getRows());

		if (rows < getRows()) {
			transformation = UtilText.parse(owner,
						"
"
							+ "[npc.Name] [npc.verb(glance)] down worriedly as [npc.she] [npc.verb(feel)] a strange tightening sensation in [npc.her] torso, and before [npc.sheHasFull] time to react, [npc.her] "
							+ (rowsDifference==1
								?"lowest pair of [npc.breasts]"
								:"lowest "+Util.intToString(rowsDifference)+" pairs of [npc.breasts]")
							+ " rapidly shrink away and [style.boldShrink(disappear)] into the [npc.skin] of [npc.her] torso.
"
							+ "[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(rows) + " pair"+ (rows > 1 ? "s" : "") + " of " + (hasBreasts() ? "breasts" : "pecs") +")]!"
						+ "
");

		} else if (rows > getRows()) {
			transformation = UtilText.parse(owner,
						"
"
							+ "[npc.Name] [npc.verb(glance)] down in surprise as [npc.she] [npc.verb(feel)] a strange swelling sensation in [npc.her] torso, and before [npc.sheHasFull] time to react, "
							+ (rowsDifference==1
								?"an extra pair of [npc.breasts]"
								:Util.intToString(rowsDifference)+" extra pairs of [npc.breasts]")
							+ " rapidly [style.boldGrow(grow)] out of the [npc.skin] of [npc.her] torso.
"
							+ "[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(rows) + " pair"+ (rows > 1 ? "s" : "") + " of " + (hasBreasts() ? "breasts" : "pecs") +")]!"
						+ "
");
		}

		this.rows = rows;

		return transformation;
	}

	public boolean isFuckable() {
		return nipples.getOrificeNipples().getCapacity() != Capacity.ZERO_IMPENETRABLE && size >= CupSize.C.getMeasurement();
	}

	public int getNippleCountPerBreast() {
		return nippleCountPerBreast;
	}

	/**
	 * Minimum 1, maximum MAXIMUM_NIPPLES_PER_BREAST
	 */
	public String setNippleCountPerBreast(GameCharacter owner, int nippleCountPerBreast) {
		nippleCountPerBreast = Math.max(1, Math.min(nippleCountPerBreast, MAXIMUM_NIPPLES_PER_BREAST));

		if(owner==null) {
			this.nippleCountPerBreast = nippleCountPerBreast;
			return "";
		}

		if (this.nippleCountPerBreast == nippleCountPerBreast) {
			return UtilText.parse(owner, "
[style.colourDisabled(The number of [npc.nipples] [npc.nameHasFull] doesn't change...)]
");
		}

		String transformation = "";

		if (nippleCountPerBreast < getNippleCountPerBreast()) {
			transformation = UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange tingling sensation running just beneath the surface of the [npc.breastSkin] that covers [npc.her] [npc.breasts]."
						+ " A shocked gasp bursts from [npc.her] mouth as the force shoots up into [npc.her] [npc.nipples],"
							+ " and [npc.she] [npc.verb(continue)] [npc.moaning] as some of them [style.boldShrink(shrink)] into the flesh of [npc.her] [npc.breasts].
"
						+ "[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(nippleCountPerBreast) + " "+ (nippleCountPerBreast > 1 ? "[npc.nipples]" : "[npc.nipple]") + " on each of [npc.her] " + (hasBreasts() ? "breasts" : "pecs") +")]!"
					+ "
");

		} else if (nippleCountPerBreast > getNippleCountPerBreast()) {
			transformation = UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange tingling sensation running just beneath the surface of the [npc.breastSkin] that covers [npc.her] [npc.breasts]."
						+ " A shocked gasp bursts from [npc.her] mouth as the force shoots up into [npc.her] [npc.nipples],"
							+ " and [npc.she] [npc.verb(continue)] [npc.moaning] as [npc.she] feels new ones [style.boldGrow(growing)] out of the flesh of [npc.her] [npc.breasts].
"
						+ "[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(nippleCountPerBreast) + " "+ (nippleCountPerBreast > 1 ? "[npc.nipples]" : "[npc.nipple]") + " on each of [npc.her] " + (hasBreasts() ? "breasts" : "pecs") +")]!"
					+ "
");

		}

		this.nippleCountPerBreast = nippleCountPerBreast;

		return transformation;
	}

	@Override
	public boolean isBestial(GameCharacter owner) {
		if(owner==null) {
			return false;
		}
		return owner.getLegConfiguration().getBestialParts().contains(Breast.class) && getType().getRace().isBestialPartsAvailable();
	}

}


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

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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.AbstractBreastType;
import com.lilithsthrone.game.character.body.types.BreastType;
import com.lilithsthrone.game.character.body.valueEnums.BreastShape;
import com.lilithsthrone.game.character.body.valueEnums.Capacity;
import com.lilithsthrone.game.character.body.valueEnums.CupSize;
import com.lilithsthrone.game.character.body.valueEnums.FluidRegeneration;
import com.lilithsthrone.game.character.body.valueEnums.Lactation;
import com.lilithsthrone.game.character.body.valueEnums.LegConfiguration;
import com.lilithsthrone.game.character.body.valueEnums.NippleShape;
import com.lilithsthrone.game.character.race.RacialBody;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Units;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.3.1
 * @version 0.3.1
 * @author Innoxia
 */
public class BreastCrotch implements BodyPartInterface {

	public static final int MAXIMUM_BREAST_ROWS = 5;
	public static final int MAXIMUM_NIPPLES_PER_BREAST = 4;

	protected AbstractBreastType type;
	protected BreastShape shape;
	protected int size;
	protected int rows;
	protected int milkStorage;
	protected float milkStored;
	protected int milkRegeneration;
	protected int nippleCountPerBreast;

	protected Nipples nipples;
	protected FluidMilk milk;

	public BreastCrotch(AbstractBreastType type, BreastShape shape, int size, int milkStorage, int rows, int nippleSize, NippleShape nippleShape, int areolaeSize, int nippleCountPerBreast, float capacity, int elasticity, int plasticity, boolean virgin) {
		this.type = type;
		this.shape = shape;
		this.size = size;
		this.milkStorage = milkStorage;
		milkStored = milkStorage;
		milkRegeneration = FluidRegeneration.ONE_AVERAGE.getMedianRegenerationValuePerDay();
		this.rows = rows;
		this.nippleCountPerBreast = nippleCountPerBreast;

		nipples = new Nipples(type.getNippleType(), nippleSize, nippleShape, areolaeSize, Lactation.getLactationFromInt(milkStorage).getAssociatedWetness().getValue(), capacity, elasticity, plasticity, virgin, true);

		milk = new FluidMilk(type.getFluidType(), true);
	}

	@Override
	public AbstractBreastType getType() {
		return type;
	}

	public boolean isVisibleThroughClothing(GameCharacter owner) {
		if(owner.getLegConfiguration()==LegConfiguration.BIPEDAL) { // Non-bipedal clothing is assumed to conceal the body so that crotch-boobs are not visible.
			return size>=CupSize.A.getMeasurement();
		} else {
			return false;
		}
	}

	public BreastShape getShape() {
		return shape;
	}

	public String setShape(GameCharacter owner, BreastShape shape) {
		if (shape == getShape()) {
			if(owner==null) {
				return "";
			}
			return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already has "+shape.getDescriptor()+" [npc.crotchBoobs], so nothing happens...)]
");
		}

		this.shape = shape;

		if(!owner.hasBreastsCrotch()) {
			return UtilText.parse(owner,
					"
"
						+ "A strange tingling feeling rises up into [npc.namePos] crotch, but as [npc.she] [npc.do]n't have any udders, nothing seems to happen...
"
						+ "If [npc.she] ever [npc.verb(grow)] any, [npc.name] will now have [style.boldSex("+shape.getDescriptor()+" udders)]!"
					+ "
");
		}
		return UtilText.parse(owner,
				"
"
					+ "A strange tingling feeling rises up into [npc.namePos] crotch, and before [npc.she] [npc.verb(know)] what's happening, [npc.her] [npc.crotchBoobs] transform into a new shape...
"
					+ "[npc.Name] now [npc.has] [style.boldSex("+shape.getDescriptor()+" [npc.crotchBoobs])]!"
				+ "
");
	}

	public Nipples getNipples() {
		return nipples;
	}

	public FluidMilk getMilk() {
		return milk;
	}

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

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

	@Override
	public String getNameSingular(GameCharacter gc) {
		if(getShape()==BreastShape.UDDERS) {
			return "udder";
		}
		return type.getCrotchNameSingular(gc);
	}

	@Override
	public String getNamePlural(GameCharacter gc) {
		if(getShape()==BreastShape.UDDERS) {
			return "udders";
		}
		return type.getCrotchNamePlural(gc);
	}

	public String getDescriptor(GameCharacter owner) {
		List<String> list = new ArrayList<>();

		if(nippleCountPerBreast == 4) {
			list.add("quad-nippled");
		} else if(nippleCountPerBreast == 3) {
			list.add("tri-nippled");
		} else if(nippleCountPerBreast == 2) {
			list.add("dual-nippled");
		}

		list.add(type.getDescriptor(owner));
		list.add(this.getSize().getDescriptor());

		list.add(this.getShape().getDescriptor());

		return Util.randomItemFrom(list);
	}

	public boolean hasBreasts() {
		return type!=BreastType.NONE;
	}

	public String setType(GameCharacter owner, AbstractBreastType type) {
		this.shape = Util.randomItemFrom(RacialBody.valueOfRace(type.getRace()).getBreastCrotchShapes());

		if(!Main.game.isStarted() || owner==null) {
			this.type = type;
			nipples.setType(owner, type.getNippleType());
			milk.setType(type.getFluidType());
			if(owner!=null) {
				owner.resetAreaKnownByCharacters(CoverableArea.BREASTS);
				owner.resetAreaKnownByCharacters(CoverableArea.NIPPLES);
				owner.postTransformationCalculation();
			}
			return "";
		}

		if(owner.getLegConfiguration().isBipedalPositionedCrotchBoobs() && Main.getProperties().udders==1 && type!=BreastType.NONE) {
			return UtilText.parse(owner, "
As [npc.nameIsFull] not a taur, [style.colourBad([npc.she] cannot grow crotch-boobs)], and so nothing happens..."
					+ "
[style.colourDisabled(This is due to your 'crotch-boob' content option being set to 'taur only'.)]
");
		}

		if (type == getType()) {
			return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already [npc.has] the [npc.crotchBoobs] of [npc.a_breastRace], so nothing happens...)]
");
		}

		UtilText.transformationContentSB.setLength(0);

		if(type.equals(BreastType.NONE)) { // Removal:
			UtilText.transformationContentSB.append(
					"
"
						+ "The area above [npc.namePos] crotch suddenly feels extremely soft and sensitive, and [npc.she] can't help but let out [npc.a_moan+] as [npc.she] [npc.verb(feel)] a transformation start to take place."
						+" [npc.Her] [npc.crotchBoobs] tingle and soften, and, with [npc.her] nipples and areolae leading the way, they quickly shrink down and disappear back into the [npc.skin] covering [npc.her] lower stomach."
						+ "
");

		} else if(this.getType().equals(BreastType.NONE)) { // New addition:
			UtilText.transformationContentSB.append(
					"
"
						+ "The area above [npc.namePos] crotch suddenly feels extremely soft and sensitive, and [npc.she] can't help but let out [npc.a_moan+] as [npc.she] [npc.verb(feel)] a transformation start to take place."
						+ " [npc.CrotchBoobsRows] hard little nubs start to grow out of the [npc.skin] covering [npc.her] lower stomach,"
							+ " and [npc.she] can't help but let out a surprised gasp as they quickly swell out into [npc.crotchBoobsSize] [npc.crotchBoobs]."
						+ "
");

		} else {
			UtilText.transformationContentSB.append(
					"
"
						+ "The area above [npc.namePos] crotch suddenly feels extremely soft and sensitive, and [npc.she] can't help but let out [npc.a_moan+] as [npc.she] [npc.verb(feel)] a transformation start to take place."
						+" The nipples and areolae on [npc.her] [npc.crotchBoobs] tingle and harden, causing [npc.herHim] to pant and let out a lewd [npc.moan] as the intense transformation runs its course."
						+ " While there's no change to their outwards appearance, [npc.she] [npc.verb(feel)] the interior structure of [npc.her] [npc.crotchBoobs] shifting and changing into a new form."
						+ " After just a moment, the transformation ends, leaving [npc.herHim] with [npc.totalNipples] new nipples."
						+ "
");
		}

		// Parse existing content before transformation:
		String s = UtilText.parse(owner, UtilText.transformationContentSB.toString());
		UtilText.transformationContentSB.setLength(0);
		UtilText.transformationContentSB.append(s);
		this.type = type;
		nipples.setType(owner, type.getNippleType());
		milk.setType(type.getFluidType());
		owner.resetAreaKnownByCharacters(CoverableArea.BREASTS);
		owner.resetAreaKnownByCharacters(CoverableArea.NIPPLES);

		UtilText.transformationContentSB.append(type.getTransformationCrotchDescription(owner)+"
");

		return UtilText.parse(owner, UtilText.transformationContentSB.toString())
				+ "
"
				+ owner.postTransformationCalculation(false)
				+ "
";
	}

	// Size:

	public CupSize getSize() {
		return CupSize.getCupSizeFromInt(size);
	}

	public int getRawSizeValue() {
		return size;
	}

	/**
	 * Sets the raw size value. Value is bound to >=0 && <=CupSize.MAXIMUM.getMeasurement()
	 *
	 * @param size Value to set size to.
	 * @return description of size change
	 */
	public String setSize(GameCharacter owner, int size) {
		int oldSize = this.size;
		this.size = Math.max(0, Math.min(size, CupSize.getMaximumCupSize().getMeasurement()));
		int sizeChange = this.size - oldSize;
		if(owner==null) {
			this.size = size;
			return "";
		}

		if (sizeChange == 0) {
			return UtilText.parse(owner, "
[style.colourDisabled(The size of [npc.namePos] [npc.crotchBoobs] doesn't change...)]
");
		}

		if (sizeChange > 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a tingling heat quickly spreading throughout [npc.her] lower torso, and [npc.she] can't help but let out [npc.a_moan+] as [npc.her] [npc.crotchBoobs] swell up and [style.boldGrow(grow larger)].
"
						+ "[npc.Name] now [npc.has] [style.boldSex([npc.crotchBoobsSize] [npc.crotchBoobs])]!"
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a tingling heat quickly spreading throughout [npc.her] lower torso,"
							+ " and [npc.she] can't help but let out a frustrated [npc.moan] as [npc.her] [npc.crotchBoobs] shrink down and [style.boldShrink(get smaller)].
"
						+ "[npc.Name] now [npc.has] [style.boldSex([npc.crotchBoobsSize] [npc.crotchBoobs])]!"
					+ "
");
		}
	}

	// Lactation:

	public Lactation getMilkStorage() {
		return Lactation.getLactationFromInt(milkStorage);
	}

	public int getRawMilkStorageValue() {
		return milkStorage;
	}

	/**
	 * Sets the milkStorage. Value is bound to >=0 && <=Lactation.SEVEN_MONSTROUS_AMOUNT_POURING.getMaximumValue()
	 */
	public String setMilkStorage(GameCharacter owner, int milkStorage) {
		int oldLactation = this.milkStorage;
		this.milkStorage = Math.max(0, Math.min(milkStorage, Lactation.SEVEN_MONSTROUS_AMOUNT_POURING.getMaximumValue()));
		int lactationChange = this.milkStorage - oldLactation;
		if(owner==null) {
			return "";
		}

		if (lactationChange == 0) {
			return UtilText.parse(owner, "
[style.colourDisabled(The amount of [npc.crotchMilk] that [npc.nameIsFull] able to produce doesn't change...)]
");
		}

		String lactationDescriptor = getMilkStorage().getDescriptor();
		if (lactationChange > 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange bubbling and churning taking place deep within [npc.her] [npc.crotchBoobs], and [npc.a_moan+] drifts out from between [npc.her] [npc.lips] as a few drops of [npc.crotchMilk] suddenly leak"
							+ " from [npc.her] [npc.crotchNipples]; clear evidence that that [npc.her] [npc.crotchMilk] production has [style.boldGrow(increased)].
"
						+ "[npc.NameIsFull] now able to produce [style.boldSex(" + lactationDescriptor + " [npc.crotchMilk])]!"
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange sucking sensation deep within [npc.her] [npc.crotchBoobs],"
							+ " and a frustrated sigh drifts out from between [npc.her] [npc.lips] as [npc.she] [npc.verb(realise)] that [npc.sheIs] feeling [npc.her] [npc.crotchMilk] production [style.boldShrink(drying up)].
"
						+ "[npc.NameIsFull] now able to produce [style.boldSex(" + lactationDescriptor + " [npc.crotchMilk])]."
					+ "
");

		}
	}

	// Stored milk:

	public Lactation getStoredMilk() {
		return Lactation.getLactationFromInt((int) milkStored);
	}

	public float getRawStoredMilkValue() {
		return milkStored;
	}

	/**
	 * Sets the milkStorage. Value is bound to >=0 && <=getRawMilkStorageValue()
	 */
	public String setStoredMilk(GameCharacter owner, float milkStored) {
		float oldStoredMilk = this.milkStored;
		this.milkStored = Math.max(0, (Math.min(milkStored, getRawMilkStorageValue())));
		float lactationChange = oldStoredMilk - this.milkStored;

		if(owner==null) {
			return "";
		}

		if (lactationChange <= 0) {
			return "";
		} else {
			return UtilText.parse(owner,
					"
"
							+ UtilText.returnStringAtRandom(
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.namePos] [npc.crotchMilk] squirts out of [npc.her] [npc.crotchNipples+].",
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.crotchMilk+] squirts out of [npc.namePos] [npc.crotchNipples+].",
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.namePos] [npc.crotchMilk] leaks out of [npc.her] [npc.crotchNipples+].",
									Units.fluid(lactationChange, Units.UnitType.LONG)+" of [npc.crotchMilk+] leaks out of [npc.namePos] [npc.crotchNipples+].")
					+ ""
					+ (this.milkStored==0
						?"
[npc.Name] now [npc.has] no more [npc.crotchMilk] stored in [npc.her] [npc.crotchBoobs]!"
						:"")
					+ "
");
		}
	}

	// Regeneration:

	public FluidRegeneration getLactationRegeneration() {
		return FluidRegeneration.getFluidRegenerationFromInt(milkRegeneration);
	}

	public int getRawLactationRegenerationValue() {
		return milkRegeneration;
	}

	/**
	 * Sets the milkRegeneration. Value is bound to >=0 && <=FluidRegeneration.FOUR_VERY_RAPID.getMaximumRegenerationValuePerDay()
	 */
	public String setLactationRegeneration(GameCharacter owner, int milkRegeneration) {
		int oldRegeneration = this.milkRegeneration;
		this.milkRegeneration = Math.max(0, Math.min(milkRegeneration, FluidRegeneration.FOUR_VERY_RAPID.getMaximumRegenerationValuePerDay()));
		int regenerationChange = this.milkRegeneration - oldRegeneration;

		if(owner==null) {
			return "";
		}

		if (regenerationChange == 0) {
			return UtilText.parse(owner, "
[style.colourDisabled([npc.namePos] rate of [npc.crotchMilk] regeneration doesn't change...)]
");
		}

		String regenerationDescriptor = getLactationRegeneration().getName();
		if (regenerationChange > 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] an alarming bubbling and churning taking place deep within [npc.her] [npc.crotchBoobs], and [npc.a_moan+] drifts out from between [npc.her] [npc.lips] as a few drops of [npc.crotchMilk] suddenly leak"
							+ " from [npc.her] [npc.crotchNipples]; clear evidence that that [npc.her] [npc.crotchMilk] regeneration has [style.boldGrow(increased)].
"
						+ "[npc.NamePos] rate of [npc.crotchMilk] regeneration is now [style.boldSex(" + regenerationDescriptor + ")]!"
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(feel)] a strange sucking sensation deep within [npc.her] [npc.crotchBoobs],"
							+ " and a frustrated sigh drifts out from between [npc.her] [npc.lips] as [npc.she] realises that [npc.sheIs] feeling [npc.her] [npc.crotchMilk] regeneration [style.boldShrink(decreasing)].
"
						+ "[npc.NamePos] rate of [npc.crotchMilk] regeneration is now [style.boldSex(" + regenerationDescriptor + ")]!"
					+ "
");
		}
	}

	// Rows:

	public int getRows() {
		return rows;
	}

	public String setRows(GameCharacter owner, int rows) {
		rows = Math.max(1, Math.min(rows, MAXIMUM_BREAST_ROWS));

		if(owner==null) {
			this.rows = rows;
			return "";
		}

		if(rows == getRows()) {
			return UtilText.parse(owner, "
[style.colourDisabled(The number of breast rows [npc.name] has doesn't change...)]
");
		}

		StringBuilder sb = new StringBuilder();

		int rowsDifference = Math.abs(rows - getRows());

		if (rows < getRows()) {
			sb.append("
"
							+ "[npc.Name] [npc.verb(let)] out a worried gasp as [npc.she] [npc.verb(feel)] a strange tightening sensation just above [npc.her] groin, and before [npc.sheHasFull] time to react, [npc.her] ");

			if(this.getShape()==BreastShape.UDDERS) {
				sb.append((rowsDifference==1
						?"highest pair of [npc.crotchNipples]"
						:"highest "+Util.intToString(rowsDifference)+" pairs of [npc.crotchNipples]"));
				sb.append(" rapidly shrink away and [style.boldShrink(disappear)] into [npc.her] udders.
");
			} else {
				sb.append((rowsDifference==1
						?"highest pair of [npc.crotchBoobs]"
						:"highest "+Util.intToString(rowsDifference)+" pairs of [npc.crotchBoobs]"));
				sb.append(" rapidly shrink away and [style.boldShrink(disappear)] into the [npc.skin] of [npc.her] stomach.
");
			}

		} else if (rows > getRows()) {
			sb.append("
"
						+ "[npc.Name] [npc.verb(let)] out a surprised gasp as [npc.she] [npc.verb(feel)] a strange swelling sensation just above [npc.her] groin, and before [npc.sheHasFull] time to react, ");

			if(this.getShape()==BreastShape.UDDERS) {
				sb.append((rowsDifference==1
						?"an extra pair of [npc.crotchNipples]"
						:Util.intToString(rowsDifference)+" extra pairs of [npc.crotchNipples]"));
				sb.append(" rapidly [style.boldGrow(grow)] out at the top [npc.her] udders.
");
			} else {
				sb.append((rowsDifference==1
						?"an extra pair of [npc.crotchBoobs]"
						:"extra pairs of [npc.crotchBoobs]"));
				sb.append(" rapidly [style.boldGrow(grow)] out of the [npc.skin] of [npc.her] stomach.
");
			}
		}

		if(this.getShape()==BreastShape.UDDERS) {
			sb.append("[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(rows) + " pair"+(rows > 1 ? "s" : "")+" of [npc.crotchNipples])] on [npc.her] udders!"
				+ "
");
		} else {
			sb.append("[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(rows) + " pair"+ (rows > 1 ? "s" : "") + " of [npc.crotchBoobs])]!"
					+ "
");
		}

		this.rows = rows;

		return UtilText.parse(owner,sb.toString());
	}

	public boolean isFuckable() {
		return nipples.getOrificeNipples().getCapacity() != Capacity.ZERO_IMPENETRABLE && size >= CupSize.C.getMeasurement();
	}

	public int getNippleCountPerBreast() {
		return nippleCountPerBreast;
	}

	/**
	 * Minimum 1, maximum MAXIMUM_NIPPLES_PER_BREAST
	 */
	public String setNippleCountPerBreast(GameCharacter owner, int nippleCountPerBreast) {
		nippleCountPerBreast = Math.max(1, Math.min(nippleCountPerBreast, MAXIMUM_NIPPLES_PER_BREAST));

		if(owner==null) {
			this.nippleCountPerBreast = nippleCountPerBreast;
			return "";
		}

		if (this.nippleCountPerBreast == nippleCountPerBreast) {
			return UtilText.parse(owner, "
[style.colourDisabled(The number of [npc.crotchNipples] [npc.nameHasFull] doesn't change...)]
");
		}

		String transformation = "";

		if (nippleCountPerBreast < getNippleCountPerBreast()) {
				transformation = UtilText.parse(owner,
						"
"
							+ "[npc.Name] [npc.verb(feel)] a strange tingling sensation running just beneath the surface of the [npc.breastCrotchSkin] that covers [npc.her] [npc.crotchBoobs]."
							+ " A shocked gasp bursts from [npc.her] mouth as the force shoots up into [npc.her] [npc.crotchNipples],"
								+ " and [npc.she] [npc.verb(continue)] [npc.moaning] as some of them [style.boldShrink(shrink)] into the flesh of [npc.her] [npc.crotchBoobs].
"
							+ "[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(nippleCountPerBreast) + " "+ (nippleCountPerBreast > 1 ? "[npc.crotchNipples]" : "[npc.crotchNipple]") + " on each of [npc.her] [npc.crotchBoobs])]!"
						+ "
");

		} else if (nippleCountPerBreast > getNippleCountPerBreast()) {
				transformation = UtilText.parse(owner,
						"
"
							+ "[npc.Name] [npc.verb(feel)] a strange tingling sensation running just beneath the surface of the [npc.breastCrotchSkin] that covers [npc.her] [npc.crotchBoobs]."
							+ " A shocked gasp bursts from [npc.her] mouth as the force shoots up into [npc.her] [npc.crotchNipples],"
								+ " and [npc.she] [npc.verb(continue)] [npc.moaning] as [npc.she] feels new ones [style.boldGrow(growing)] out of the flesh of [npc.her] [npc.crotchBoobs].
"
							+ "[npc.Name] now [npc.has] [style.boldSex("+ Util.intToString(nippleCountPerBreast) + " "+ (nippleCountPerBreast > 1 ? "[npc.crotchNipples]" : "[npc.crotchNipple]") + " on each of [npc.her] [npc.crotchBoobs])]!"
						+ "
");

		}

		this.nippleCountPerBreast = nippleCountPerBreast;

		return transformation;
	}

	@Override
	public boolean isBestial(GameCharacter owner) {
		if(owner==null) {
			return false;
		}
		return owner.getLegConfiguration().getBestialParts().contains(BreastCrotch.class) && getType().getRace().isBestialPartsAvailable();
	}

}


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

import java.util.HashSet;
import java.util.Set;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.BodyPartTypeInterface;
import com.lilithsthrone.game.character.body.valueEnums.ClitorisSize;
import com.lilithsthrone.game.character.body.valueEnums.PenetrationModifier;
import com.lilithsthrone.game.character.body.valueEnums.PenisGirth;
import com.lilithsthrone.game.dialogue.utils.UtilText;

/**
 * @since 0.2.8
 * @version 0.3.1
 * @author Innoxia
 */
public class Clitoris implements BodyPartInterface {

	protected int clitSize;
	protected int girth;
	protected Set<PenetrationModifier> clitModifiers;

	public Clitoris(int clitSize, int girth) {
		this.clitSize = clitSize;
		this.girth = girth;

		clitModifiers = new HashSet<>();
	}

	@Override
	public BodyPartTypeInterface getType() {
		return null;
	}

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

	@Override
	public String getName(GameCharacter gc) {
		return UtilText.returnStringAtRandom("clit", "clit", "clit", "nub", "button");
	}

	@Override
	public String getNameSingular(GameCharacter gc) {
		return UtilText.returnStringAtRandom("clit", "clit", "clit", "nub", "button");
	}

	@Override
	public String getNamePlural(GameCharacter gc) {
		return UtilText.returnStringAtRandom("clits", "clits", "clits", "nubs", "buttons");
	}

	@Override
	public String getDescriptor(GameCharacter gc) {
		return UtilText.returnStringAtRandom(
				"sensitive",
				"sensitive",
				this.getGirth()!=PenisGirth.TWO_AVERAGE?this.getGirth().getName():"",
				this.getClitorisSize()!=ClitorisSize.ZERO_AVERAGE?this.getClitorisSize().getDescriptor():"little");
	}

	public ClitorisSize getClitorisSize() {
		return ClitorisSize.getClitorisSizeFromInt(clitSize);
	}

	public int getRawClitorisSizeValue() {
		return clitSize;
	}

	public String setClitorisSize(GameCharacter owner, int clitSize) {
		if(!owner.hasVagina()) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		int oldSize = this.clitSize;
		this.clitSize = Math.max(0, Math.min(clitSize, ClitorisSize.SEVEN_STALLION.getMaximumValue()));
		int sizeChange = this.clitSize - oldSize;

		if (sizeChange == 0) {
			if(owner.isPlayer()) {
				return "
[style.colourDisabled(The size of your clit doesn't change...)]
";
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The size of [npc.namePos] clit doesn't change...)]
");
			}
		} else if (sizeChange > 0) {
			if (owner.isPlayer()) {
				return "
"
							+ "You let out [pc.a_moan] as you feel a deep throbbing sensation building up within your [pc.pussy]."
							+ " Your cheeks flush red as the feeling works its way up into your clit, and with a little gasp, you feel it [style.boldGrow(grow larger)].
"
							+ "You now have [style.boldSex([pc.a_clitSize] [pc.clit])]!"
						+ "
";
			} else {
				return UtilText.parse(owner,
						"
"
							+ "[npc.Name] lets out [npc.a_moan] as [npc.she] feels a deep throbbing sensation building up within [npc.her] [npc.pussy]."
							+ " [npc.Her] cheeks flush red as the feeling works its way up [npc.her] clit, and with a little gasp, [npc.she] feels it [style.boldGrow(grow larger)].
"
							+ "[npc.She] now has [style.boldSex([npc.a_clitSize] [npc.clit])]!"
						+ "
");
			}
		} else {
			if (owner.isPlayer()) {
				return "
"
							+ "You let out [pc.a_moan] as you feel a deep throbbing sensation building up within your [pc.pussy]."
							+ " Your cheeks flush red as the feeling works its way up into your clit, and with a little gasp, you feel it [style.boldShrink(shrink)].
"
							+ "You now have [style.boldSex([pc.a_clitSize] [pc.clit])]!"
						+ "
";
			} else {
				return UtilText.parse(owner,
						"
"
								+ "[npc.Name] lets out [npc.a_moan] as [npc.she] feels a deep throbbing sensation building up at the base of [npc.her] cock."
								+ " [npc.Her] cheeks flush red as the feeling works its way up [npc.her] clit, and with a little gasp, [npc.she] feels it [style.boldShrink(shrink)].
"
								+ "[npc.She] now has [style.boldSex([npc.a_clitSize] [npc.clit])]!"
						+ "
");
			}
		}
	}

	// Girth:

	public PenisGirth getGirth() {
		return PenisGirth.getPenisGirthFromInt(girth);
	}

	public int getRawGirthValue() {
		return girth;
	}

	/**
	 * Sets the girth. Value is bound to >=0 && <=PenisGirth.FOUR_FAT.getValue()
	 */
	public String setGirth(GameCharacter owner, int girth) {
		if(owner==null) {
			this.girth = Math.max(0, Math.min(girth, PenisGirth.FOUR_FAT.getValue()));
			return "";
		}

		if(!owner.hasVagina()) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		int girthChange = 0;

		if (girth <= 0) {
			if (this.girth != 0) {
				girthChange = 0 - this.girth;
				this.girth = 0;
			}
		} else if (girth >= PenisGirth.FOUR_FAT.getValue()) {
			if (this.girth != PenisGirth.FOUR_FAT.getValue()) {
				girthChange = PenisGirth.FOUR_FAT.getValue() - this.girth;
				this.girth = PenisGirth.FOUR_FAT.getValue();
			}
		} else {
			if (this.girth != girth) {
				girthChange = girth - this.girth;
				this.girth = girth;
			}
		}

		if(girthChange == 0) {
			return UtilText.parse(owner, "
[style.colourDisabled(The girth of [npc.namePos] [npc.clit] doesn't change...)]
");
		}

		if (girthChange > 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(feel)] a deep throbbing sensation building up within [npc.her] clit."
						+ " [npc.Her] cheeks flush red as the feeling spreads throughout [npc.her] [npc.pussy+], and [npc.she] can't help but let out another [npc.moan+] as [npc.her] clit suddenly [style.boldGrow(grows thicker)].
"
						+ "[npc.She] now [npc.has] [style.boldSex([npc.a_clitGirth] [npc.clit])]!"
					+ "
");
		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(feel)] a deep throbbing sensation building up within [npc.her] clit."
						+ " [npc.Her] cheeks flush red as the feeling works its way up [npc.her] shaft, and [npc.she] can't help but let out another [npc.moan+] as [npc.her] clit suddenly [style.boldShrink(thins down)].
"
						+ "[npc.She] now [npc.has] [style.boldSex([npc.a_clitGirth] [npc.cock])]!"
					+ "
");
		}
	}

	public Set<PenetrationModifier> getClitorisModifiers() {
		return clitModifiers;
	}

	public boolean hasClitorisModifier(PenetrationModifier modifier) {
		return clitModifiers.contains(modifier);
	}

	public String addClitorisModifier(GameCharacter owner, PenetrationModifier modifier) {
		if(hasClitorisModifier(modifier)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		if(!owner.hasVagina()) {
			if(owner.isPlayer()) {
				return "
[style.colourDisabled(You don't have a clitoris, so nothing happens...)]
";
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] doesn't have a clitoris, so nothing happens...)]
");
			}
		}

		clitModifiers.add(modifier);

		switch(modifier) {
			case RIBBED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure building up within your [pc.clit], but before you have a chance to react, a series of [style.boldGrow(hard, fleshy ribs)] grow up all along its length.
"
								+ "[style.boldSex(Your [pc.clit] is now ribbed!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense pressure builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, a series of [style.boldGrow(hard, fleshy ribs)] grow up all along its length.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is now ribbed!)]"
							+ "
";
				}
			case TENTACLED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a pulsating warmth building up within your [pc.clit], but before you have a chance to react, a series of [style.boldGrow(little wriggling tentacles)] grow up all along its length.
"
								+ "[style.boldSex(Your [pc.clit] is now covered with little tentacles, which wriggle with a mind of their own!)]"
							+ "
";
				} else {
					return "
"
								+ "A pulsating warmth builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, a series of [style.boldGrow(little wriggling tentacles)] grow up all along its length.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is now covered with little tentacles, which wriggle with a mind of their own!)]"
							+ "
";
				}
			case BARBED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up within your [pc.clit], but before you have a chance to react, a series of [style.boldGrow(little fleshy barbs)] grow up all along its length.
"
								+ "[style.boldSex(Your [pc.clit] is now lined with fleshy, backwards-facing barbs!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, a series of [style.boldGrow(little fleshy barbs)] grow up all along its length.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is now lined with fleshy, backwards-facing barbs!)]"
							+ "
";
				}
			case FLARED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up in the tip of your [pc.clit], and before you have a chance to react, the [style.boldGrow(head flares out)], much like that of a horse's cock.
"
								+ "[style.boldSex(Your [pc.clit] now has a wide, flared head!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up in the tip of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, the [style.boldGrow(head flares out)], much like that of a horse's cock.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] now has a wide, flared head!)]"
							+ "
";
				}
			case BLUNT:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up in the tip of your [pc.clit], and before you have a chance to react, the [style.boldGrow(head smoothes over)], leaving it looking like the head of a reptile's cock.
"
								+ "[style.boldSex(Your [pc.clit] now has a smooth, blunt head!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up in the tip of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, the [style.boldGrow(head smoothes over)], leaving it looking like the head of a reptile's cock.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] now has a smooth, blunt head!)]"
							+ "
";
				}
			case KNOTTED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up in the base of your [pc.clit], and before you have a chance to react, a [style.boldGrow(fat knot)] quickly grows up there.
"
								+ "[style.boldSex(Your [pc.clit] now has a fat knot at the base!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up in the base of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, a [style.boldGrow(fat knot)] quickly grows up there.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] now has a fat knot at the base!)]"
							+ "
";
				}
			case PREHENSILE:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a strange tingling sensation work its way up the length of your [pc.clit], and you suddenly become aware that it's transformed into being [style.boldGrow(prehensile)],"
									+ " allowing you to twist and move it around just like a primate's tail.
"
								+ "[style.boldSex(Your [pc.clit] is now prehensile!)]"
							+ "
";
				} else {
					return "
"
								+ "A strange tingling sensation work its way up the length of [npc.namePos] [npc.clit], and [npc.she] suddenly becomes aware that it's transformed into being [style.boldGrow(prehensile)],"
									+ " allowing [npc.herHim] to twist and move it around just like a primate's tail.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is now prehensile!)]"
							+ "
";
				}
			case SHEATHED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure building up in the base of your [pc.clit], and before you have a chance to react, it pulls back into a brand new [style.boldGrow(sheath)] that's just grown up there.
"
								+ "[style.boldSex(Your [pc.clit] is now sheathed!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense pressure builds up in the base of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, it pulls back into a brand new [style.boldGrow(sheath)] that's just grown up there.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is now sheathed!)]"
							+ "
";
				}
			case TAPERED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up within your [pc.clit], but before you have a chance to react, the shaft suddenly [style.boldGrow(tapers down)].
"
								+ "[style.boldSex(Your [pc.clit] is now tapered!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, the shaft suddenly [style.boldGrow(tapers down)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is now tapered!)]"
							+ "
";
				}
			case VEINY:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up within your [pc.clit], but before you have a chance to react, a series of [style.boldGrow(prominent veins)] grow up all along its length.
"
								+ "[style.boldSex(Your [pc.clit] is now veiny!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, a series of [style.boldGrow(prominent veins)] grow up all along its length.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is now veiny!)]"
							+ "
";
				}
		}

		// Catch:
		return "
[style.colourDisabled(Nothing happens...)]
";
	}

	public String removeClitorisModifier(GameCharacter owner, PenetrationModifier modifier) {
		if(!hasClitorisModifier(modifier)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		clitModifiers.remove(modifier);

		switch(modifier) {
			case RIBBED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soothing coolness spread up within your [pc.clit], and before you have a chance to react, your hard, fleshy ribs suddenly [style.boldShrink(disappear)].
"
								+ "[style.boldSex(Your [pc.clit] is no longer ribbed!)]"
							+ "
";
				} else {
					return "
"
								+ "An soothing coolness builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, [npc.her] hard, fleshy ribs suddenly [style.boldShrink(disappear)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer ribbed!)]"
							+ "
";
				}
			case TENTACLED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soothing coolness building up within your [pc.clit], and before you have a chance to react, your little wriggling tentacles [style.boldShrink(disappear)].
"
								+ "[style.boldSex(Your [pc.clit] is no longer covered with little tentacles!)]"
							+ "
";
				} else {
					return "
"
								+ "A soothing coolness builds up within [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, [npc.her] little wriggling tentacles [style.boldShrink(disappear)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer covered with little tentacles!)]"
							+ "
";
				}
			case BARBED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soothing coolness building up within your [pc.clit], and before you have a chance to react, your little fleshy barbs [style.boldShrink(disappear)].
"
								+ "[style.boldSex(Your [pc.clit] is no longer barbed!)]"
							+ "
";
				} else {
					return "
"
								+ "A soothing coolness builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, [npc.her] little fleshy barbs [style.boldShrink(disappear)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer barbed!)]"
							+ "
";
				}
			case FLARED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soothing coolness building up in the tip of your [pc.clit], and before you have a chance to react, your flared head [style.boldShrink(shrinks)] down,"
									+ " making your clit look like a regular, human-like one.
"
								+ "[style.boldSex(Your [pc.clit] no longer has a flared head!)]"
							+ "
";
				} else {
					return "
"
								+ "A soothing coolness builds up in the tip of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, [npc.her] flared head [style.boldShrink(shrinks)] down,"
									+ " making [npc.her] clit look like a regular, human-like one.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] no longer has a flared head!)]"
							+ "
";
				}
			case BLUNT:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soothing coolness building up in the tip of your [pc.clit], and before you have a chance to react, your blunt head [style.boldShrink(shrinks)] down,"
									+ " making your clit look like a regular, human-like one.
"
								+ "[style.boldSex(Your [pc.clit] no longer has a blunt head!)]"
							+ "
";
				} else {
					return "
"
								+ "A soothing coolness builds up in the tip of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, [npc.her] blunt head [style.boldShrink(shrinks)] down,"
									+ " making [npc.her] clit look like a regular, human-like one.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] no longer has a blunt head!)]"
							+ "
";
				}
			case KNOTTED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soothing coolness building up in the base of your [pc.clit], and before you have a chance to react, your fat knot [style.boldShrink(shrinks)] and disappears.
"
								+ "[style.boldSex(Your [pc.clit] is no longer knotted!)]"
							+ "
";
				} else {
					return "
"
								+ "A soothing coolness builds up in the base of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, [npc.her] fat knot [style.boldShrink(shrinks)] and disappears.
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer knotted!)]"
							+ "
";
				}
			case PREHENSILE:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a strange tingling sensation work its way up the length of your [pc.clit], and you suddenly become aware that it's [style.boldShrink(no longer prehensile)].
"
								+ "[style.boldSex(Your [pc.clit] is no longer prehensile!)]"
							+ "
";
				} else {
					return "
"
								+ "A strange tingling sensation works its way up the length of [npc.namePos] [npc.clit], and [npc.she] suddenly becomes aware that it's [style.boldShrink(no longer prehensile)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer prehensile!)]"
							+ "
";
				}
			case SHEATHED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soothing coolness building up in the base of your [pc.clit], and before you have a chance to react, your sheath [style.boldShrink(disappears)].
"
								+ "[style.boldSex(Your [pc.clit] is no longer sheathed!)]"
							+ "
";
				} else {
					return "
"
								+ "A soothing coolness builds up in the base of [npc.namePos] [npc.clit], and before [npc.she] has a chance to react, [npc.her] sheath [style.boldShrink(disappears)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer sheathed!)]"
							+ "
";
				}
			case TAPERED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up within your [pc.clit], but before you have a chance to react, the shaft suddenly [style.boldShrink(widens)].
"
								+ "[style.boldSex(Your [pc.clit] is no longer tapered!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, the shaft suddenly [style.boldShrink(widens)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer tapered!)]"
							+ "
";
				}
			case VEINY:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense warmth building up within your [pc.clit], but before you have a chance to react, your prominent veins [style.boldShrink(disappear)].
"
								+ "[style.boldSex(Your [pc.clit] is no longer veiny!)]"
							+ "
";
				} else {
					return "
"
								+ "An intense warmth builds up within [npc.namePos] [npc.clit], but before [npc.she] has a chance to react, [npc.her] prominent veins [style.boldShrink(disappear)].
"
								+ "[style.boldSex([npc.NamePos] [npc.clit] is no longer veiny!)]"
							+ "
";
				}
		}

		// Catch:
		return "
[style.colourDisabled(Nothing happens...)]
";
	}

	@Override
	public boolean isBestial(GameCharacter owner) {
		if(owner==null) {
			return false;
		}
		return owner.getLegConfiguration().getBestialParts().contains(Clitoris.class) && getType().getRace().isBestialPartsAvailable();
	}

}


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

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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.0
 * @version 0.3.1
 * @author Innoxia
 */
public enum CoverableArea {

	// Utility value
	NONE("none",
			new ArrayList<>()),

	HANDS("hands",
			Util.newArrayListOfValues(
					InventorySlot.FINGER,
					InventorySlot.HAND,
					InventorySlot.WRIST)),

	ASS("ass",
			Util.newArrayListOfValues(
					InventorySlot.GROIN,
					InventorySlot.ANUS,
					InventorySlot.LEG,
					InventorySlot.TAIL)),

	ANUS("asshole",
			Util.newArrayListOfValues(
					InventorySlot.GROIN,
					InventorySlot.ANUS)),

	STOMACH("stomach",
			Util.newArrayListOfValues(
					InventorySlot.STOMACH,
					InventorySlot.TORSO_UNDER,
					InventorySlot.TORSO_OVER)),

	BACK("back",
			Util.newArrayListOfValues(
					InventorySlot.TORSO_UNDER,
					InventorySlot.TORSO_OVER,
					InventorySlot.WINGS,
					InventorySlot.TAIL)),

	LEGS("legs",
			Util.newArrayListOfValues(
					InventorySlot.LEG,
					InventorySlot.SOCK)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			switch(owner.getLegConfiguration()) {
				case ARACHNID:
				case BIPEDAL:
				case CEPHALOPOD:
				case TAUR:
					return true;
				case TAIL:
				case TAIL_LONG:
					return false;
			}
			return true;
		}
	},

	FEET("feet",
			Util.newArrayListOfValues(
					InventorySlot.FOOT,
					InventorySlot.ANKLE,
					InventorySlot.SOCK)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return LEGS.isPhysicallyAvailable(owner);
		}
	},

	THIGHS("thighs",
			Util.newArrayListOfValues(
					InventorySlot.LEG,
					InventorySlot.SOCK)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return LEGS.isPhysicallyAvailable(owner);
		}
	},

	VAGINA("pussy",
			Util.newArrayListOfValues(
					InventorySlot.GROIN,
					InventorySlot.LEG)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return owner.hasVagina();
		}
	},

	MOUND("mound",
			Util.newArrayListOfValues(
					InventorySlot.GROIN,
					InventorySlot.LEG)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return !owner.hasVagina() && !owner.hasPenis();
		}
	},

	PENIS("cock",
			Util.newArrayListOfValues(
					InventorySlot.GROIN,
					InventorySlot.LEG)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return owner.hasPenis();
		}
	},

	TESTICLES("balls",
			Util.newArrayListOfValues(
					InventorySlot.GROIN,
					InventorySlot.LEG)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return owner.hasPenis();
		}
	},

	BREASTS("breasts",
			Util.newArrayListOfValues(
					InventorySlot.CHEST,
					InventorySlot.NIPPLE,
					InventorySlot.TORSO_UNDER,
					InventorySlot.TORSO_OVER)),

	NIPPLES("nipples",
			Util.newArrayListOfValues(
					InventorySlot.CHEST,
					InventorySlot.NIPPLE,
					InventorySlot.TORSO_UNDER,
					InventorySlot.TORSO_OVER)),

	BREASTS_CROTCH("crotch-breasts",
			Util.newArrayListOfValues(
					InventorySlot.STOMACH,
					InventorySlot.GROIN,
					InventorySlot.TORSO_UNDER,
					InventorySlot.TORSO_OVER)) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return owner.hasBreastsCrotch();
		}
		public List<InventorySlot> getAssociatedInventorySlots(GameCharacter owner) {
			if(owner.getLegConfiguration().isBipedalPositionedCrotchBoobs()) {
				return super.getAssociatedInventorySlots(owner);
			} else {
				return Util.newArrayListOfValues(
						InventorySlot.STOMACH,
						InventorySlot.GROIN,
						InventorySlot.LEG);
			}
		}
	},

	NIPPLES_CROTCH("crotch-nipples",
			null) {
		public boolean isPhysicallyAvailable(GameCharacter owner) {
			return owner.hasBreastsCrotch();
		}
		public List<InventorySlot> getAssociatedInventorySlots(GameCharacter owner) {
			return BREASTS_CROTCH.getAssociatedInventorySlots(owner);
		}
	},

	HAIR("hair",
			Util.newArrayListOfValues(
					InventorySlot.HAIR,
					InventorySlot.HORNS,
					InventorySlot.HEAD)),

	MOUTH("mouth",
			Util.newArrayListOfValues(
					InventorySlot.HAIR,
					InventorySlot.HORNS,
					InventorySlot.HEAD,
					InventorySlot.EYES,
					InventorySlot.MOUTH,
					InventorySlot.NECK));

	private String name;
	private List<InventorySlot> associatedInventorySlots;

	private CoverableArea(String name, List<InventorySlot> associatedInventorySlots) {
		this.name = name;
		this.associatedInventorySlots = associatedInventorySlots;
	}

	public String getName() {
		return name;
	}

	public List<InventorySlot> getAssociatedInventorySlots(GameCharacter owner) {
		return new ArrayList<>(associatedInventorySlots);
	}

	/**
	 * @return true if the owner has the related orifice/penetration type.
	 */
	public boolean isPhysicallyAvailable(GameCharacter owner) {
		return true;
	}
}


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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.BodyCoveringType;
import com.lilithsthrone.game.character.body.valueEnums.CoveringModifier;
import com.lilithsthrone.game.character.body.valueEnums.CoveringPattern;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.83
 * @version 0.2.1
 * @author Innoxia
 */
public class Covering  {

	protected BodyCoveringType type;
	protected CoveringPattern pattern;
	protected CoveringModifier modifier;

	protected Colour primaryColour, secondaryColour;
	protected boolean primaryGlowing, secondaryGlowing;

	/**
	 * Constructor.

	 * Initialises CoveringPattern pattern to a random value, and boolean glowing to false.
	 * @param type The BodyCoveringType to set this skin to.
	 */
	public Covering(BodyCoveringType type) {
		this(type, type.getNaturalPatterns().get(Util.random.nextInt(type.getNaturalPatterns().size())),
				type.getNaturalColoursPrimary().get(Util.random.nextInt(type.getNaturalColoursPrimary().size())), false,
				(type.getNaturalColoursSecondary().isEmpty()
						?type.getNaturalColoursPrimary().get(Util.random.nextInt(type.getNaturalColoursPrimary().size()))
						:type.getNaturalColoursSecondary().get(Util.random.nextInt(type.getNaturalColoursSecondary().size()))), false);
	}

	/**
	 * Constructor.

	 * Initialises CoveringPattern pattern to the first value, boolean glowing to false, and secondaryColour to same as primaryColour (where available).
	 * @param type
	 * @param primaryColour
	 */
	public Covering(BodyCoveringType type, Colour primaryColour) {
		this(type, type.getNaturalPatterns().get(0),
				primaryColour, false,
				(type.getNaturalColoursSecondary().contains(primaryColour) || type.getNaturalColoursSecondary().isEmpty()
						?primaryColour
						:type.getNaturalColoursSecondary().get(Util.random.nextInt(type.getNaturalColoursSecondary().size()))), false);
	}

	/**
	 * Constructor.

	 * Initialises CoveringPattern pattern to the first value, boolean glowing to false, and secondaryColour to same as primaryColour (where available).
	 * @param type
	 * @param primaryColour
	 */
	public Covering(BodyCoveringType type, Colour primaryColour, Colour secondaryColour) {
		this(type, type.getNaturalPatterns().get(0),
				primaryColour, false,
				secondaryColour, false);
	}

	/**
	 * Constructor.
	 * @param type The BodyCoveringType to set this skin to.
	 * @param pattern The CoveringPattern to set this skin to.
	 * @param glowing Whether this skin is glowing or not.
	 */
	public Covering(BodyCoveringType type, CoveringPattern pattern, Colour primaryColour, boolean primaryGlowing, Colour secondaryColour, boolean secondaryGlowing) {
		this(type, pattern, type.getNaturalModifiers().get(0), primaryColour, primaryGlowing, secondaryColour, secondaryGlowing);
	}

	public Covering(BodyCoveringType type, CoveringPattern pattern, CoveringModifier modifier, Colour primaryColour, boolean primaryGlowing, Colour secondaryColour, boolean secondaryGlowing) {
		this.type = type;
		this.pattern = pattern;
		this.modifier = modifier;
		this.primaryColour = primaryColour;
		this.primaryGlowing = primaryGlowing;
		this.secondaryColour = secondaryColour;
		this.secondaryGlowing = secondaryGlowing;
	}

	public Covering(Covering coveringToClone) {
		this.type = coveringToClone.getType();
		this.pattern = coveringToClone.getPattern();
		this.modifier = coveringToClone.getModifier();
		this.primaryColour = coveringToClone.getPrimaryColour();
		this.primaryGlowing = coveringToClone.isPrimaryGlowing();
		this.secondaryColour = coveringToClone.getSecondaryColour();
		this.secondaryGlowing = coveringToClone.isSecondaryGlowing();
	}

	public String getDeterminer(GameCharacter gc) {
		return type.getDeterminer(gc);
	}

	public String getName(GameCharacter gc) {
		return type.getName(gc);
	}

//	public String getName(GameCharacter gc, boolean withDescriptor) {
//		return (getDescriptor(gc).length() > 0 ? getDescriptor(gc) + " " : "") + getName(gc);
//	}

	public String getNameSingular(GameCharacter gc) {
		return type.getNameSingular(gc);
	}

	public String getNamePlural(GameCharacter gc) {
		return type.getNameSingular(gc);
	}

	public String getDescriptor(GameCharacter gc) {
		return modifier.getName();
	}

	public static String getFormattedColour(Colour colour, String additionalName, boolean glowing, boolean capitalised) {
		return (glowing
					?spanStartGlowing(colour)+getGlowingDescriptor()+" "
					:"")
				+(capitalised?Util.capitaliseSentence(colour.getName()):colour.getName())
				+additionalName
				+"";
	}

	public String getColourDescriptor(GameCharacter gc, boolean coloured, boolean capitalised) {
		String primaryColourName = capitalised?Util.capitaliseSentence(primaryColour.getName()):primaryColour.getName();
		String secondaryColourName = capitalised?Util.capitaliseSentence(secondaryColour.getName()):secondaryColour.getName();
//		if(gc.getRace()==Race.SLIME) {
//			if(this.getType()!=BodyCoveringType.SLIME) {
//				return gc.getCovering(BodyCoveringType.SLIME).getColourDescriptor(gc, coloured, capitalised);
//			}
//		}
		if(coloured) {
			switch(pattern) {
				case NONE:
				case FLUID:
				case FRECKLED_FACE:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised);
				case HIGHLIGHTS:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-highlighted", secondaryGlowing, capitalised);
				case OMBRE:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", fading into "+getFormattedColour(secondaryColour, "", secondaryGlowing, capitalised);
				case MOTTLED:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-mottled", secondaryGlowing, capitalised);
				case FRECKLED:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-freckled", secondaryGlowing, capitalised);
				case SPOTTED:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-spotted", secondaryGlowing, capitalised);
				case MARKED:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-marked", secondaryGlowing, capitalised);
				case STRIPED:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-striped", secondaryGlowing, capitalised);
				case ORIFICE_ANUS:
					return getFormattedColour(primaryColour, "-rimmed", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-interiored", secondaryGlowing, capitalised);
				case ORIFICE_NIPPLE:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-interiored", secondaryGlowing, capitalised);
				case ORIFICE_VAGINA:
					return getFormattedColour(primaryColour, "-lipped", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-interiored", secondaryGlowing, capitalised);
				case ORIFICE_MOUTH:
					return getFormattedColour(primaryColour, "-lipped", primaryGlowing, capitalised)+", "+getFormattedColour(secondaryColour, "-interiored", secondaryGlowing, capitalised);
				case EYE_IRISES:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised);
				case EYE_IRISES_HETEROCHROMATIC:
					return "heterochromatic " + getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+" and "+getFormattedColour(secondaryColour, "", secondaryGlowing, capitalised);
				case EYE_PUPILS:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised);
				case EYE_PUPILS_HETEROCHROMATIC:
					return "heterochromatic " + getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+" and "+getFormattedColour(secondaryColour, "", secondaryGlowing, capitalised);
				case EYE_SCLERA:
					return getFormattedColour(primaryColour, "", primaryGlowing, capitalised);
				case EYE_SCLERA_HETEROCHROMATIC:
					return "heterochromatic " + getFormattedColour(primaryColour, "", primaryGlowing, capitalised)+" and "+getFormattedColour(secondaryColour, "", secondaryGlowing, capitalised);
			}
			return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColourName+"";

		} else {
			switch(pattern) {
				case NONE:
				case FLUID:
				case FRECKLED_FACE:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName;
				case HIGHLIGHTS:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+", "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName+"-highlighted";
				case OMBRE:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+", fading into "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName+"";
				case MOTTLED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+", "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName+"-mottled";
				case FRECKLED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+", "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName+"-freckled";
				case SPOTTED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+", "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName+"-spotted";
				case MARKED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+", "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName+"-marked";
				case STRIPED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+", "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName+"-striped";
				case ORIFICE_ANUS:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+"-rimmed";
				case ORIFICE_NIPPLE:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName;
				case ORIFICE_VAGINA:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+"-lipped";
				case ORIFICE_MOUTH:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+" lips";
				case EYE_IRISES:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName;
				case EYE_IRISES_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+" and "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName;
				case EYE_PUPILS:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName;
				case EYE_PUPILS_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+" and "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName;
				case EYE_SCLERA:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName;
				case EYE_SCLERA_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName+" and "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColourName;
			}
			return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColourName;
		}
	}

	public String getPrimaryColourDescriptor(boolean coloured) {
		if(coloured) {
			return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+"";

		} else {
			return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName();
		}
	}

	public String getSecondaryColourDescriptor(boolean coloured) {
		if(coloured) {
			return (secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+"";

		} else {
			return (secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName();
		}
	}

	/**
	 * @return A full description of this covering. e.g. "glowing black, wolf-like fur, with luminescent purple stripes" or "black, shaggy fur"
	 */
	public String getFullDescription(GameCharacter gc, boolean coloured) {
		//text-shadow: 0px 0px 4px #FF0000;
		String descriptor = modifier.getName();
		String name = type.getName(gc);

//		if(gc.getRace()==Race.SLIME) {
//			if(this.getType()!=BodyCoveringType.SLIME) {
//				return gc.getCovering(BodyCoveringType.SLIME).getFullDescription(gc, coloured);
//			}
//		}
		if(coloured) {
			switch(pattern) {
				case NONE:
				case FLUID:
				case FRECKLED_FACE:
					if(primaryColour==Colour.COVERING_NONE) {
						return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+"";
					}
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name;
				case HIGHLIGHTS:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" highlights";
				case OMBRE:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", which gradually "+(type.isDefaultPlural()?"fade":"fades")+" into "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+"";
				case MOTTLED:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" mottling";
				case FRECKLED:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" freckles";
				case SPOTTED:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" spots";
				case MARKED:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" markings";
				case STRIPED:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+""
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" stripes";
				case ORIFICE_ANUS:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+"-rimmed anus, with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" internal walls";
				case ORIFICE_NIPPLE:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" nipples, with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" internal walls";
				case ORIFICE_VAGINA:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+"-lipped pussy, with "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" internal walls";
				case ORIFICE_MOUTH:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" lips, with a "
							+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" throat";
				case EYE_IRISES:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" irises";
				case EYE_IRISES_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" and "
						+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" irises";
				case EYE_PUPILS:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" pupils";
				case EYE_PUPILS_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" and "
						+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" pupils";
				case EYE_SCLERA:
					return (primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" sclerae";
				case EYE_SCLERA_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?spanStartGlowing(primaryColour)+getGlowingDescriptor()+" ":"")+primaryColour.getName()+" and "
						+(secondaryGlowing?spanStartGlowing(secondaryColour)+getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" sclerae";

			}

		} else {
			switch(pattern) {
				case NONE:
				case FLUID:
				case FRECKLED_FACE:
					if(primaryColour==Colour.COVERING_NONE) {
						return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName();
					}
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name;
				case HIGHLIGHTS:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" highlights";
				case OMBRE:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", which gradually "+(type.isDefaultPlural()?"fade":"fades")+" into "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName();
				case MOTTLED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" mottling";
				case FRECKLED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" freckles";
				case SPOTTED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" spots";
				case MARKED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" markings";
				case STRIPED:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()
							+(descriptor!=null && !descriptor.isEmpty()?", "+descriptor:"")+" "+name+", with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" stripes";
				case ORIFICE_ANUS:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+"-rimmed anus, with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" internal walls";
				case ORIFICE_NIPPLE:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" nipples, with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" internal walls";
				case ORIFICE_VAGINA:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+"-lipped pussy, with "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" internal walls";
				case ORIFICE_MOUTH:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" lips, with a "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" throat";
				case EYE_IRISES:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" irises";
				case EYE_IRISES_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" and "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" irises";
				case EYE_PUPILS:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" pupils";
				case EYE_PUPILS_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" and "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" sclerae";
				case EYE_SCLERA:
					return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" pupils";
				case EYE_SCLERA_HETEROCHROMATIC:
					return "heterochromatic "+(primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName()+" and "+(secondaryGlowing?getGlowingDescriptor()+" ":"")+secondaryColour.getName()+" sclerae";
			}
		}
		return (primaryGlowing?getGlowingDescriptor()+" ":"")+primaryColour.getName();
	}

	private static String spanStartGlowing(Colour colour) {
		return "";
	}

	private static String getGlowingDescriptor() {
		return UtilText.returnStringAtRandom("glowing", "luminescent", "luminous", "fluorescent");
	}

	@Override
	public boolean equals(Object o) {
//		if(super.equals(o)){
			if(o instanceof Covering){
				if(((Covering)o).getType() == type
						&& ((Covering)o).getPattern() == pattern
						&& ((Covering)o).getModifier() == modifier
						&& ((Covering)o).getPrimaryColour() == primaryColour
						&& ((Covering)o).isPrimaryGlowing() == primaryGlowing
						&& ((Covering)o).getSecondaryColour() == secondaryColour
						&& ((Covering)o).isSecondaryGlowing() == secondaryGlowing){
					return true;
				}
			}
//		}
		return false;
	}

	@Override
	public int hashCode() {
		int result = 17;
		result = 31 * result + type.hashCode();
		result = 31 * result + pattern.hashCode();
		result = 31 * result + modifier.hashCode();
		result = 31 * result + primaryColour.hashCode();
		result = 31 * result + (primaryGlowing ? 1 : 0);
		result = 31 * result + secondaryColour.hashCode();
		result = 31 * result + (secondaryGlowing ? 1 : 0);
		return result;
	}

	public BodyCoveringType getType() {
		return type;
	}

	public void setType(BodyCoveringType type) {
		this.type = type;
	}

	public CoveringPattern getPattern() {
		return pattern;
	}

	public void setPattern(CoveringPattern pattern) {
		this.pattern = pattern;
	}

	public CoveringModifier getModifier() {
		return modifier;
	}

	public void setModifier(CoveringModifier modifier) {
		this.modifier = modifier;
	}

	public Colour getPrimaryColour() {
		return primaryColour;
	}

	public void setPrimaryColour(Colour primaryColour) {
		this.primaryColour = primaryColour;
	}

	public Colour getSecondaryColour() {
		return secondaryColour;
	}

	public void setSecondaryColour(Colour secondaryColour) {
		this.secondaryColour = secondaryColour;
	}

	public boolean isPrimaryGlowing() {
		return primaryGlowing;
	}

	public void setPrimaryGlowing(boolean primaryGlowing) {
		this.primaryGlowing = primaryGlowing;
	}

	public boolean isSecondaryGlowing() {
		return secondaryGlowing;
	}

	public void setSecondaryGlowing(boolean secondaryGlowing) {
		this.secondaryGlowing = secondaryGlowing;
	}
}


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

import com.lilithsthrone.game.character.body.types.PenisType;
import com.lilithsthrone.game.character.body.valueEnums.PenisGirth;
import com.lilithsthrone.game.character.body.valueEnums.TesticleSize;

/**
 * @since 0.2.5
 * @version 0.2.5
 * @author Innoxia
 */
public class Dildo {

	public static Penis dildoTiny = new Penis(
			PenisType.DILDO,
			8,
			false,
			PenisGirth.ONE_THIN.getValue(),
			TesticleSize.ONE_TINY.getValue(),
			0,
			2);

	public static Penis dildoAverage = new Penis(
			PenisType.DILDO,
			15,
			false,
			PenisGirth.TWO_AVERAGE.getValue(),
			TesticleSize.TWO_AVERAGE.getValue(),
			0,
			2);

	public static Penis dildoLarge = new Penis(
			PenisType.DILDO,
			25,
			false,
			PenisGirth.TWO_AVERAGE.getValue(),
			TesticleSize.TWO_AVERAGE.getValue(),
			0,
			2);

	public static Penis dildoHuge = new Penis(
			PenisType.DILDO,
			35,
			false,
			PenisGirth.THREE_THICK.getValue(),
			TesticleSize.THREE_LARGE.getValue(),
			0,
			2);

	public static Penis dildoEnormous = new Penis(
			PenisType.DILDO,
			45,
			false,
			PenisGirth.THREE_THICK.getValue(),
			TesticleSize.THREE_LARGE.getValue(),
			0,
			2);

	public static Penis dildoGigantic = new Penis(
			PenisType.DILDO,
			55,
			false,
			PenisGirth.FOUR_FAT.getValue(),
			TesticleSize.FOUR_HUGE.getValue(),
			0,
			2);

	public static Penis dildoStallion = new Penis(
			PenisType.DILDO,
			80,
			false,
			PenisGirth.FOUR_FAT.getValue(),
			TesticleSize.FOUR_HUGE.getValue(),
			0,
			2);
}


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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.AbstractEarType;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
import com.lilithsthrone.main.Main;

/**
 * @since 0.1.0
 * @version 0.3.2
 * @author Innoxia
 */
public class Ear implements BodyPartInterface {

	protected AbstractEarType type;
	protected boolean pierced;

	public Ear(AbstractEarType type) {
		this.type = type;
		pierced = false;
	}

	@Override
	public AbstractEarType getType() {
		return type;
	}

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

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

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

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

	@Override
	public String getDescriptor(GameCharacter gc) {
		return type.getDescriptor(gc);
	}

	public String setType(GameCharacter owner, AbstractEarType type) {
		if(!Main.game.isStarted() || owner==null) {
			this.type = type;
			if(owner!=null) {
				owner.postTransformationCalculation();
			}
			return "";
		}

		if (type.equals(getType())) {
			return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already [npc.has] the [npc.ears] of [npc.a_earRace], so nothing happens...)]
");
		}

		UtilText.transformationContentSB.setLength(0);

		UtilText.transformationContentSB.append(
				"
"
					+ "[npc.NamePos] [npc.ears] start to involuntarily twitch and itch, and, feeling them start to transform, [npc.she] [npc.verb(let)] out a gasp as [npc.she] [npc.verb(reach)] up to rub at them. ");

		// Parse existing content before transformation:
		String s = UtilText.parse(owner, UtilText.transformationContentSB.toString());
		UtilText.transformationContentSB.setLength(0);
		UtilText.transformationContentSB.append(s);
		this.type = type;

		UtilText.transformationContentSB.append(type.getTransformationDescription(owner));

		UtilText.transformationContentSB.append("
");

		return UtilText.parse(owner, UtilText.transformationContentSB.toString())
				+ "
"
					+ owner.postTransformationCalculation()
				+ "
";
	}

	public boolean isPierced() {
		return pierced;
	}

	public String setPierced(GameCharacter owner, boolean pierced) {
		if(this.pierced == pierced) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		this.pierced = pierced;

		if(pierced) {
			if(owner.isPlayer()) {
				return "
Your [pc.ears] are now [style.boldGrow(pierced)]!
";
			} else {
				return UtilText.parse(owner,
						"
[npc.NamePos] [npc.ears] are now [style.boldGrow(pierced)]!
");
			}

		} else {
			AbstractClothing c = owner.getClothingInSlot(InventorySlot.PIERCING_EAR);
			String piercingUnequip = "";
			if(c!=null) {
				owner.forceUnequipClothingIntoVoid(owner, c);
				piercingUnequip = owner.addClothing(c, false);
			}

			if(owner.isPlayer()) {
				return "
"
							+ "Your [pc.ears] are [style.boldShrink(no longer pierced)]!"
						+ "
"
						+piercingUnequip;
			} else {
				return UtilText.parse(owner,
						"
"
								+ "[npc.NamePos] [npc.ears] are [style.boldShrink(no longer pierced)]!"
						+ "
"
						+piercingUnequip);
			}
		}

	}

	@Override
	public boolean isBestial(GameCharacter owner) {
		if(owner==null) {
			return false;
		}
		return owner.getLegConfiguration().getBestialParts().contains(Ear.class) && getType().getRace().isBestialPartsAvailable();
	}

}