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

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

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.lilithsthrone.game.character.CharacterUtils;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.BodyPartTypeInterface;
import com.lilithsthrone.game.character.body.types.FluidType;
import com.lilithsthrone.game.character.body.valueEnums.FluidFlavour;
import com.lilithsthrone.game.character.body.valueEnums.FluidModifier;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.inventory.enchanting.ItemEffect;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.83
 * @version 0.3.1
 * @author Innoxia
 */
public class FluidMilk implements FluidInterface {

	protected FluidType type;
	protected FluidFlavour flavour;
	protected List<FluidModifier> fluidModifiers;
	protected List<ItemEffect> transformativeEffects;

	protected boolean crotchMilk;

	public FluidMilk(FluidType type, boolean crotchMilk) {
		this.type = type;
		this.flavour = type.getFlavour();
		transformativeEffects = new ArrayList<>();

		fluidModifiers = new ArrayList<>();
		fluidModifiers.addAll(type.getFluidModifiers());

		this.crotchMilk = crotchMilk;
	}

	public Element saveAsXML(Element parentElement, Document doc) {
		Element element = doc.createElement("milk");
		parentElement.appendChild(element);

		CharacterUtils.addAttribute(doc, element, "type", this.type.toString());
		CharacterUtils.addAttribute(doc, element, "flavour", this.flavour.toString());

		Element milkModifiers = doc.createElement("milkModifiers");
		element.appendChild(milkModifiers);
		for(FluidModifier fm : this.getFluidModifiers()) {
			CharacterUtils.addAttribute(doc, milkModifiers, fm.toString(), "true");
		}

		return element;
	}

	public static FluidMilk loadFromXML(Element parentElement, Document doc) {
		return loadFromXML(parentElement, doc, null, false);
	}

	/**
	 *
	 * @param parentElement
	 * @param doc
	 * @param baseType If you pass in a baseType, this method will ignore the saved type in parentElement.
	 */
	public static FluidMilk loadFromXML(Element parentElement, Document doc, FluidType baseType, boolean crotchMilk) {

		Element milk = (Element)parentElement.getElementsByTagName("milk").item(0);

		FluidType fluidType = FluidType.MILK_HUMAN;

		if(baseType!=null) {
			fluidType = baseType;

		} else {
			try {
				fluidType = FluidType.getTypeFromString(milk.getAttribute("type"));
			} catch(Exception ex) {
			}
		}

		FluidMilk fluidMilk = new FluidMilk(fluidType, crotchMilk);

		String flavourId = milk.getAttribute("flavour");
		if(flavourId.equalsIgnoreCase("SLIME")) {
			fluidMilk.flavour = FluidFlavour.BUBBLEGUM;
		} else {
			fluidMilk.flavour = FluidFlavour.valueOf(flavourId);
		}

		Element milkModifiersElement = (Element)milk.getElementsByTagName("milkModifiers").item(0);
		fluidMilk.fluidModifiers.clear();
		if(milkModifiersElement!=null) {
			Collection<FluidModifier> milkFluidModifiers = fluidMilk.fluidModifiers;
			Body.handleLoadingOfModifiers(FluidModifier.values(), null, milkModifiersElement, milkFluidModifiers);
		}

		return fluidMilk;
	}

	@Override
	public boolean equals(Object o) {
		if(o instanceof FluidMilk){
			if(((FluidMilk)o).getType().equals(this.getType())
				&& ((FluidMilk)o).getFlavour() == this.getFlavour()
				&& ((FluidMilk)o).getFluidModifiers().equals(this.getFluidModifiers())
				&& ((FluidMilk)o).getTransformativeEffects().equals(this.getTransformativeEffects())){
					return true;
			}
		}
		return false;
	}

	@Override
	public int hashCode() {
		int result = 17;
		result = 31 * result + this.getType().hashCode();
		result = 31 * result + this.getFlavour().hashCode();
		result = 31 * result + this.getFluidModifiers().hashCode();
		result = 31 * result + this.getTransformativeEffects().hashCode();
		return result;
	}

	@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) {
		String modifierDescriptor = "";
		if(!fluidModifiers.isEmpty()) {
			modifierDescriptor = fluidModifiers.get(Util.random.nextInt(fluidModifiers.size())).getName();
		}

		return UtilText.returnStringAtRandom(
				modifierDescriptor,
				flavour.getRandomFlavourDescriptor(),
				(type.getDescriptor(gc).equals("human")?null:type.getDescriptor(gc)));
	}

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

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

	public FluidFlavour getFlavour() {
		return flavour;
	}

	public String setFlavour(GameCharacter owner, FluidFlavour flavour) {
		if(owner==null) {
			this.flavour = flavour;
			return "";
		}

		if(this.flavour == flavour) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		this.flavour = flavour;

		if(this.isCrotchMilk()) {
			return UtilText.parse(owner,
					"
"
						+ "A soothing warmth spreads through [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out a contented little sigh.
"
						+ "[npc.NamePos] [npc.crotchMilk] now tastes of "+flavour.getName()+"."
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "A soothing warmth spreads through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a contented little sigh.
"
						+ "[npc.NamePos] [npc.milk] now tastes of "+flavour.getName()+"."
					+ "
");
		}
	}

	public boolean hasFluidModifier(FluidModifier fluidModifier) {
		return fluidModifiers.contains(fluidModifier);
	}

	public String addFluidModifier(GameCharacter owner, FluidModifier fluidModifier) {
		if(fluidModifiers.contains(fluidModifier)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		fluidModifiers.add(fluidModifier);

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

		switch(fluidModifier) {
			case ADDICTIVE:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A strange, pulsating heat spreads through [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(addictive)]!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A strange, pulsating heat spreads through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(addictive)]!"
							+ "
");
				}
			case ALCOHOLIC:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A strange, soothing warmth spreads up through [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(alcoholic)]!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A strange, soothing warmth spreads up through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(alcoholic)]!"
							+ "
");
				}
			case BUBBLING:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A light, bubbly feeling spreads up through [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(bubbly)]!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A light, bubbly feeling spreads up through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(bubbly)]!"
							+ "
");
				}
			case HALLUCINOGENIC:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A series of strange pulses shoot up through [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(psychoactive)]!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A series of strange pulses shoot up through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(psychoactive)]!"
							+ "
");
				}
			case MINERAL_OIL:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A soothing warmth flows into [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now imbued with [style.boldGrow(mineral oil)], and can melt condoms!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A soothing warmth flows into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now imbued with [style.boldGrow(mineral oil)], and can melt condoms!"
							+ "
");
				}
			case MUSKY:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A slow, creeping warmth rises up into [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(musky)]!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A slow, creeping warmth rises up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(musky)]!"
							+ "
");
				}
			case SLIMY:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A strange, soothing warmth flows up into [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(slimy)]!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A strange, soothing warmth flows up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(slimy)]!"
							+ "
");
				}
			case STICKY:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A thick, sickly warmth flows up into [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(sticky)]!"
							+ "
");
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A thick, sickly warmth flows up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(sticky)]!"
							+ "
");
				}
			case VISCOUS:
				if(this.isCrotchMilk()) {
					return UtilText.parse(owner,
							"
"
								+ "A heavy heat slowly rises up into [npc.namePos] [npc.crotchBoobs], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.crotchMilk] is now [style.boldGrow(viscous)]!"
							+ "
");
				}
			case MINERAL_OIL:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a prolonged heat flow up into your [pc.breasts], causing you to let out [pc.a_moan+].
"
								+ "Your [pc.milk] is now [style.boldGrow(mineral oil)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A prolonged heat flows up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out [npc.a_moan+].
"
								+ "[npc.NamePos] [npc.milk] is now [style.boldGrow(mineral oil)]!"
							+ "
");
				}
		}

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

	public String removeFluidModifier(GameCharacter owner, FluidModifier fluidModifier) {
		if(!fluidModifiers.contains(fluidModifier)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		fluidModifiers.remove(fluidModifier);

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

		switch(fluidModifier) {
			case ADDICTIVE:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soft coolness spreading up through your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer addictive)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A soft coolness spreads up through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer addictive)]!"
							+ "
");
				}
			case ALCOHOLIC:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soft coolness spreading up through your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer alcoholic)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A soft coolness spreads up through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer alcoholic)]!"
							+ "
");
				}
			case BUBBLING:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a calm, settling feeling rising up into your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer bubbly)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A calm, settling feeling spreads up through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer bubbly)]!"
							+ "
");
				}
			case HALLUCINOGENIC:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a series of soothing waves wash up into your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer psychoactive)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A series of soothing waves wash up through [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer psychoactive)]!"
							+ "
");
				}
			case MUSKY:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a gentle coolness rise up into your [pc.breasts], causing you to let out soft sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer musky)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A gentle coolness rises up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a soft sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer musky)]!"
							+ "
");
				}
			case SLIMY:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a calming coolness flow up into your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer slimy)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A calming coolness flows up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer slimy)]!"
							+ "
");
				}
			case STICKY:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soft warmth flow up into your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer sticky)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A soft warmth flows up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer sticky)]!"
							+ "
");
				}
			case VISCOUS:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a soft coolness slowly rising up into your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer viscous)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A soft coolness rises up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer viscous)]!"
							+ "
");
				}
			case MINERAL_OIL:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a short relief flow up into your [pc.breasts], causing you to let out a gentle sigh.
"
								+ "Your [pc.milk] is [style.boldShrink(no longer mineral oil)]!"
							+ "
";
				} else {
					return UtilText.parse(owner,
							"
"
								+ "A short relief flows up into [npc.namePos] [npc.breasts], causing [npc.herHim] to let out a gentle sigh.
"
								+ "[npc.NamePos] [npc.milk] is [style.boldShrink(no longer mineral oil)]!"
							+ "
");
				}
		}

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

	public List<ItemEffect> getTransformativeEffects() {
		return transformativeEffects;
	}

	public void addTransformativeEffect(ItemEffect ie) {
		transformativeEffects.add(ie);
	}

	/**
	 * DO NOT MODIFY!
	 */
	public List<FluidModifier> getFluidModifiers() {
		return fluidModifiers;
	}

	public void clearFluidModifiers() {
		fluidModifiers.clear();
	}

	public float getValuePerMl() {
		return (0.1f + (this.getFluidModifiers().size()*0.1f)) + (this.getFlavour()!=FluidFlavour.MILK?1.5f:1);
	}

	public boolean isCrotchMilk() {
		return crotchMilk;
	}

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


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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.HairType;
import com.lilithsthrone.game.character.body.valueEnums.HairLength;
import com.lilithsthrone.game.character.body.valueEnums.HairStyle;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Units;

/**
 * @since 0.1.0
 * @version 0.3.5.5
 * @author Innoxia
 */
public class Hair implements BodyPartInterface {

	protected HairType type;
	protected int length;
	protected HairStyle style;

	public Hair(HairType type, int length, HairStyle style) {
		this.type = type;
		this.length = length;
		this.style = style;

	}

	@Override
	public HairType 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);
	}

	public String getDescriptor(GameCharacter owner) {
		return type.getDescriptor(owner);
	}

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

		if (type == getType()) {
			if (owner.isPlayer()) {
				return "
[style.colourDisabled(You already have the [pc.hair] of [pc.a_hairRace], so nothing happens...)]
";
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already has the [npc.hair] of [npc.a_hairRace], so nothing happens...)]
");
			}

		} else {
			UtilText.transformationContentSB.setLength(0);

			if (owner.isPlayer()) {
				UtilText.transformationContentSB.append(
						"
"
							+ "Your scalp tingles and itches, and you rub the top of your head as you feel your [pc.hair] start to transform.");
			} else {
				UtilText.transformationContentSB.append(
						"
"
							+ "[npc.NamePos] scalp tingles and itches, and [npc.she] rubs the top of [npc.her] head as [npc.she] feels [npc.her] [npc.hair] start to transform.");
			}
		}

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

		switch (type) {
			case HUMAN:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with human-like hair.
"
								+ "You now have [pc.hairColour] [style.boldHuman(human hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with human-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldHuman(human hair)]."
							+ "
");
				}
				break;
			case DEMON_COMMON:
				if (!owner.isShortStature()) {
					UtilText.transformationContentSB.append(
							" The transformation only lasts a matter of moments, leaving [npc.herHim] with silky, demonic hair.
"
							+ "[npc.Name] now [npc.has] [npc.hairColour] [style.boldDemon(demonic hair)]."
						+ "
");
				} else {
					UtilText.transformationContentSB.append(
							" The transformation only lasts a matter of moments, leaving [npc.herHim] with silky, impish hair.
"
							+ "[npc.Name] now [npc.has] [npc.hairColour] [style.boldImp(impish hair)]."
						+ "
");
				}
				break;
			case DOG_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldDogMorph(canine hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldDogMorph(canine hair)]."
							+ "
");
				}
				break;
			case FOX_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldFoxMorph(vulpine hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldFoxMorph(vulpine hair)]."
							+ "
");
				}
				break;
			case LYCAN:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldWolfMorph(lupine hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldWolfMorph(lupine hair)]."
							+ "
");
				}
				break;
			case CAT_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldCatMorph(feline hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldCatMorph(feline hair)]."
							+ "
");
				}
				break;
			case CAT_MORPH_SIDEFLUFF:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair and a bunch of fluff on the sides of your face.
"
								+ "You now have [pc.hairColour] [style.boldCatMorph(feline hair)] with side fluff."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair and a bunch of fluff on the sides of [npc.her] face.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldCatMorph(feline hair)] with side fluff."
							+ "
");
				}
				break;
			case COW_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldCowMorph(bovine hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldCowMorph(bovine hair)]."
							+ "
");
				}
				break;
			case ALLIGATOR_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with a mass of scales in place of hair.
"
								+ "You now have [pc.hairColour] [style.boldGatorMorph(scales in place of hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with a mass of scales in place of hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldGatorMorph(scales in place of hair)]."
							+ "
");
				}
				break;
			case HORSE_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with coarse, horse-like hair.
"
								+ "You now have [pc.hairColour] [style.boldHorseMorph(equine hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with coarse, horse-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldHorseMorph(equine hair)]."
							+ "
");
				}
				break;
			case REINDEER_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with coarse, reindeer-like hair.
"
								+ "You now have [pc.hairColour] [style.boldReindeerMorph(rangiferine hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with coarse, reindeer-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldReindeerMorph(rangiferine hair)]."
							+ "
");
				}
				break;
			case SQUIRREL_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldSquirrelMorph(squirrel-like hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldSquirrelMorph(squirrel-like hair)]."
							+ "
");
				}
				break;
			case RAT_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldRatMorph(rat-like hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldRatMorph(rat-like hair)]."
							+ "
");
				}
				break;
			case RABBIT_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldRabbitMorph(rabbit-like hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldRabbitMorph(rabbit-like hair)]."
							+ "
");
				}
				break;
			case BAT_MORPH:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with fur-like hair.
"
								+ "You now have [pc.hairColour] [style.boldBatMorph(bat-like hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with fur-like hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldBatMorph(bat-like hair)]."
							+ "
");
				}
				break;
			case HARPY:
				if (owner.isPlayer()) {
					UtilText.transformationContentSB.append(
								" The feeling goes away almost as quickly as it came, leaving you with a plume of feathers in place of hair.
"
								+ "You now have [pc.hairColour] [style.boldHarpy(harpy feathers in place of hair)]."
							+ "
");
				} else {
					UtilText.transformationContentSB.append(
								" The transformation only lasts a matter of moments, leaving [npc.herHim] with a plume of feathers in place of hair.
"
								+ "[npc.Name] now has [npc.hairColour] [style.boldHarpy(harpy feathers in place of hair)]."
							+ "
");
				}
				break;
			case ANGEL://TODO
				break;
		}

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

	public HairLength getLength() {
		return HairLength.getHairLengthFromInt(length);
	}

	public int getRawLengthValue() {
		return length;
	}

	/**
	 * Sets the length value. Value is bound to >=0 && <=HairLength.SEVEN_TO_FLOOR.getMaximumValue()
	 */
	public String setLength(GameCharacter owner, int length) {
		int oldLength = this.length;
		this.length = Math.max(0, Math.min(length, HairLength.SEVEN_TO_FLOOR.getMaximumValue()));
		int sizeChange = this.length - oldLength;

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

		String styleChange = "";
		if(this.length < owner.getHairStyle().getMinimumLengthRequired()) {
			if(owner.isPlayer()) {
				styleChange = "
"
								+ "Your [pc.hair] "+(owner.getHairType().isDefaultPlural()?"are":"is")+" too short for your current hair style!"
							+ "
"
							+ owner.setHairStyle(HairStyle.NONE);
			} else {
				styleChange = "
"
						+ "[npc.Her] [npc.hair] "+(owner.getHairType().isDefaultPlural()?"are":"is")+" too short for [npc.her] current hair style!"
					+ "
"
					+ owner.setHairStyle(HairStyle.NONE);
			}
		}

		if (sizeChange == 0) {
			if(owner.isPlayer()) {
				return "
[style.colourDisabled(The length of your [pc.hair] doesn't change...)]
";
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The length of [npc.namePos] [npc.hair] doesn't change...)]
");
			}
		} else if (sizeChange < 0) {
			if(owner.isPlayer()) {
				return "
Your scalp itches for a moment as you feel your [pc.hair] [style.boldShrink(getting shorter)].
"
						+ "You now have [style.boldTfGeneric([pc.hairLength], "
						+ Units.size(this.length, Units.UnitType.LONG_SINGULAR) +" [pc.hair])]!
"
						+ styleChange;
			} else {
				return UtilText.parse(owner, "
[npc.Name] lets out a little cry and rubs at [npc.her] scalp as [npc.her] [npc.hair] [style.boldShrink(gets shorter)].
"
						+ "[npc.She] now has [style.boldTfGeneric([npc.hairLength], "
						+ Units.size(this.length, Units.UnitType.LONG_SINGULAR) +" [npc.hair])]!
"
						+ styleChange);
			}

		} else {
			if(owner.isPlayer()) {
				return "
Your scalp itches for a moment as you feel your [pc.hair] [style.boldGrow(growing longer)].
"
						+ "You now have [style.boldTfGeneric([pc.hairLength], "
						+ Units.size(this.length, Units.UnitType.LONG_SINGULAR) +" [pc.hair])]!
"
						+ styleChange;
			} else {
				return UtilText.parse(owner, "
[npc.Name] lets out a little cry and rubs at [npc.her] scalp as [npc.her] [npc.hair] [style.boldGrow(grows longer)].
"
						+ "[npc.She] now has [style.boldTfGeneric([npc.hairLength], "
						+ Units.size(this.length, Units.UnitType.LONG_SINGULAR) +" [npc.hair])]!
"
						+ styleChange);
			}
		}
	}

	public HairStyle getStyle() {
		return style;
	}

	public String setStyle(GameCharacter owner, HairStyle style) {
		this.style = style;

		switch(style) {
			case BRAIDED:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into braids.
");
			case CURLY:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now curled.
");
			case LOOSE:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now loose.
");
			case NONE:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now unstyled, and will take on "+(type.isDefaultPlural()?"their":"its")+" natural shape.
");
			case PONYTAIL:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a ponytail.
");
			case STRAIGHT:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now straightened.
");
			case TWIN_TAILS:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into twin-tails.
");
			case WAVY:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now wavy.
");
			case MOHAWK:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a mohawk.
");
			case AFRO:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into an afro.
");
			case SIDECUT:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a sidecut.
");
			case BOB_CUT:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a bob cut.
");
			case PIXIE:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a pixie cut.
");
			case SLICKED_BACK:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now slicked back.
");
			case MESSY:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now unstyled, and particularly messy.
");
			case HIME_CUT:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now straightened, and styled into a hime-cut.
");
			case CHONMAGE:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now straightened, oiled, and styled into a chonmage.
");
			case DREADLOCKS:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into dreadlocks.
");
			case TOPKNOT:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now gathered up into a topknot.
");
			case BIRD_CAGE:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into an elaborate bird cage.
");
			case TWIN_BRAIDS:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into twin braids.
");
			case DRILLS:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into drills.
");
			case LOW_PONYTAIL:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a low ponytail.
");
			case CROWN_BRAID:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a crown braid.
");
			case BUN:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now styled into a bun.
");
			case CHIGNON:
				return UtilText.parse(owner, "
[npc.NamePos] [npc.hair] "+(type.isDefaultPlural()?"are":"is")+" now tied up and styled into a chignon.
");
		}

		// Catch:
		return "";
	}

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

repo_name: klorpa/Pedo

license: unknown


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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.AbstractHornType;
import com.lilithsthrone.game.character.body.types.HornType;
import com.lilithsthrone.game.character.body.valueEnums.HornLength;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Util;

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

	public static final int MAXIMUM_ROWS = 3;
	public static final int MAXIMUM_HORNS_PER_ROW = 4;

	protected AbstractHornType type;
	protected int rows;
	protected int hornsPerRow;
	protected int length;

	public Horn(AbstractHornType type, int length) {
		this.type = type;
		this.length = length;
		rows = 1;
		hornsPerRow = type.getDefaultHornsPerRow();
	}

	@Override
	public AbstractHornType 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, AbstractHornType type) {
		if(!Main.game.isStarted() || owner==null) {
			this.type = type;
			if(owner!=null) {
				owner.postTransformationCalculation();
			}
			return "";
		}

		if (type == getType()) {
			if(type.equals(HornType.NONE)) {
				return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already [npc.verb(lack)] horns, so nothing happens...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already [npc.has] the [npc.horns] of [npc.a_hornRace], so nothing happens...)]
");
			}
		}

		UtilText.transformationContentSB.setLength(0);

		if (type == HornType.HORSE_STRAIGHT) {
			this.setHornsPerRow(owner, 1);
			this.setHornRows(owner, 1);
		}

		if(this.type.equals(HornType.NONE)) {
			UtilText.transformationContentSB.append(UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(let)] out a surprised gasp and [npc.verb(rub)] at [npc.her] forehead as [npc.she] [npc.verb(feel)] it growing hot and sensitive."
						+ " After just a moment, [npc.her] [npc.eyes] widen in shock as something starts pushing out from under the [npc.faceSkin] of [npc.her] forehead."));
		} else {
			UtilText.transformationContentSB.append(UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(let)] out a surprised gasp as [npc.she] [npc.verb(feel)] an odd tingling sensation at the base of [npc.her] "+(owner.getTotalHorns()==1?"[npc.horn]":"[npc.horns]")+"."
						+ " Before [npc.she] [npc.has] any time in which to react, "+(owner.getTotalHorns()==1?"it rapidly crumbles away, and within moments it's":"they rapidly crumble away, and within moments they've")+" completely disappeared. "));
		}

		if(type!=HornType.NONE) {
			UtilText.transformationContentSB.append(UtilText.parse(owner, (owner.getTotalHorns()==1
					?" A hard nub suddenly pushes out from the middle of [npc.her] forehead, and [npc.she] [npc.verb(gasp)] as [npc.she] [npc.verb(feel)] it quickly grow out into a "
					:" Hard nubs suddenly push out from the sides of [npc.her] head, and [npc.she] [npc.verb(gasp)] as [npc.she] [npc.verb(feel)] them quickly grow out into ")));
		}

		this.type = type;

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

		if(this.length==0) {
			length = HornLength.ONE_SMALL.getMinimumValue();
		}

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

	public int getHornRows() {
		return rows;
	}

	public String setHornRows(GameCharacter owner, int rows) {
		rows = Math.max(1, Math.min(rows, MAXIMUM_ROWS));
		if(owner==null) {
			this.rows = rows;
			return "";
		}

		if(owner.getHornRows() == rows) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		boolean removingHorns = owner.getHornRows() > rows;
		this.rows = rows;

		owner.postTransformationCalculation();

		if (owner.getHornType().equals(HornType.NONE)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		if(removingHorns) {
			return UtilText.parse(owner,
					"
"
						+ "A tingling feeling spreads over [npc.namePos] [npc.horns], before moving down and concentrating in [npc.her] forehead."
						+ " [npc.She] can't help but let out a cry as [npc.she] [npc.verb(feel)] some of them [style.boldShrink(crumbling away)] and disappearing back down into [npc.her] [npc.faceSkin].
"
						+ "After a few moments, [npc.sheIs] left with [style.boldTfGeneric([npc.a_horns])]."
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "A tingling feeling spreads over [npc.namePos] [npc.horns], before moving down and concentrating in [npc.her] forehead."
						+ " [npc.She] can't help but let out a cry as [npc.she] [npc.verb(feel)] new [npc.horns] [style.boldGrow(pushing up)] and growing out of [npc.her] [npc.faceSkin].
"
						+ "After a few moments, [npc.sheIs] left with [style.boldTfGeneric([npc.a_horns])]."
					+ "
");
		}
	}

	public int getHornsPerRow() {
		return hornsPerRow;
	}

	public String setHornsPerRow(GameCharacter owner, int hornsPerRow) {
		hornsPerRow = Math.max(1, Math.min(hornsPerRow, MAXIMUM_HORNS_PER_ROW));
		if(owner==null) {
			this.hornsPerRow = hornsPerRow;
			return "";
		}

		if(owner.getHornsPerRow() == hornsPerRow) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		boolean removingHorns = owner.getHornsPerRow() > hornsPerRow;
		this.hornsPerRow = hornsPerRow;

		owner.postTransformationCalculation();

		if (owner.getHornType().equals(HornType.NONE)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		if(removingHorns) {
			return UtilText.parse(owner,
					"
"
						+ "A strange bubbling sensation starts to take root at the base of [npc.namePos] [npc.horns]."
						+ " Before [npc.she] can react, they start to [style.boldShrink(crumble away and reshape)] themselves.
"
						+ "After a few moments, [npc.sheIs] left with [style.boldTfGeneric("+Util.intToString(hornsPerRow)+" "+(hornsPerRow==1?"[npc.horn]":"[npc.horns]")+(rows==1?"":" in each row")+")]!"
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "A strange bubbling sensation starts to rise up in [npc.namePos] forehead."
						+ " Before [npc.she] can react, new [npc.horns] suddenly [style.boldGrow(push up)] and grow out of [npc.her] [npc.faceSkin].
"
						+ "After a few moments, [npc.sheIs] left with [style.boldTfGeneric("+Util.intToString(hornsPerRow)+" "+(hornsPerRow==1?"[npc.horn]":"[npc.horns]")+(rows==1?"":" in each row")+")]!"
					+ "
");
		}
	}

	public HornLength getHornLength() {
		return HornLength.getHornLengthFromInt(length);
	}

	public int getHornLengthValue() {
		return length;
	}

	public String setHornLength(GameCharacter owner, int length) {
		int oldLength = this.length;
		this.length = Math.max(0, Math.min(length, HornLength.FOUR_MASSIVE.getMaximumValue()));
		int sizeChange = this.length - oldLength;

		if(owner.getHornType().equals(HornType.NONE)) {
			return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] [npc.do]n't have any horns, so nothing seems to happen...)]
");
		}

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

		if(sizeChange < 0) {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(let)] out a cry as [npc.she] [npc.verb(feel)] a strange tingling sensation running up through [npc.her] [npc.face] and into [npc.her] [npc.horns],"
							+ " before they suddenly shrink down and [style.boldShrink(get noticeably shorter)].
"
						+ "[npc.Name] now [npc.has] [style.boldTfGeneric([npc.hornSize] [npc.horns])]!"
					+ "
");

		} else {
			return UtilText.parse(owner,
					"
"
						+ "[npc.Name] [npc.verb(let)] out a little cry as [npc.she] [npc.verb(feel)] a warm pulsating sensation running up through [npc.her] [npc.face] and into [npc.her] [npc.horns],"
							+ " before they suddenly grow out and [style.boldGrow(get noticeably longer)].
"
						+ "[npc.Name] now [npc.has] [style.boldTfGeneric([npc.hornSize] [npc.horns])]!"
					+ "
");
		}
	}

	@Override
	public boolean isBestial(GameCharacter owner) {
		if(owner==null) {
			return false;
		}
		return owner.getLegConfiguration().getBestialParts().contains(Horn.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.AbstractLegType;
import com.lilithsthrone.game.character.body.types.FootStructure;
import com.lilithsthrone.game.character.body.valueEnums.LegConfiguration;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Util;

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

	protected AbstractLegType type;
	protected FootStructure footStructure;
	protected LegConfiguration legConfiguration;

	public Leg(AbstractLegType type, LegConfiguration legConfiguration) {
		this.type = type;
		this.legConfiguration = legConfiguration;
		this.footStructure = type.getDefaultFootStructure();
	}

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

	public FootStructure getFootStructure() {
		return footStructure;
	}

	//TODO check
	public String setFootStructure(GameCharacter owner, FootStructure footStructure) {
		if(owner==null) {
			this.footStructure = footStructure;
			return "";
		}

		if(this.getFootStructure()==footStructure) {
			return UtilText.parse(owner,
					"
"
						+ "[style.colourDisabled(Nothing happens, as [npc.namePos] [npc.feet] are already [npc.footStructure]...)]"
					+ "
");
		}

		if(!type.getFootType().getPermittedFootStructures().contains(footStructure)) {
			return UtilText.parse(owner,
					"
"
						+ "[style.colourDisabled(Nothing happens, as [npc.namePos] [npc.feet] cannot transform to be "+footStructure.getName()+"...)]"
					+ "
");
		}

		this.footStructure = footStructure;

		StringBuilder sb = new StringBuilder();

		sb.append("
"
				+ "[npc.Name] [npc.verb(let)] out a shocked gasp as [npc.she] suddenly [npc.verb(feel)] an intense heat shoot down into [npc.her] [npc.legs] and [npc.feet]."
				+ " Staggering and swaying, [npc.she] almost [npc.verb(lose)] [npc.her] balance as the structure of [npc.her] feet transforms and changes.
");

		switch(footStructure) {
			case DIGITIGRADE:
				sb.append("After just a moment, [npc.sheIs] left with [style.boldTfGeneric(digitgrade [npc.feet])], meaning that [npc.she] now [npc.verb(walk)] on [npc.her] toes, with [npc.her] heel being permanently raised.");
				break;
			case PLANTIGRADE:
				sb.append("After just a moment, [npc.sheIs] left with [style.boldTfGeneric(plantigrade [npc.feet])], meaning that [npc.she] now [npc.verb(walk)] with [npc.her] feet flat on the ground.");
				break;
			case UNGULIGRADE:
				sb.append("After just a moment, [npc.sheIs] left with [style.boldTfGeneric(unguligrade [npc.feet])], meaning that [npc.she] now [npc.verb(walk)] on [npc.her] [npc.toes], with the rest of [npc.her] foot being permanently raised.");
				break;
		}

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

	}

	public LegConfiguration getLegConfiguration() {
		return legConfiguration;
	}

	public void setLegConfigurationForced(AbstractLegType type, LegConfiguration legConfiguration) {
		this.type = type;
		this.footStructure = type.getDefaultFootStructure();
		this.legConfiguration = legConfiguration;
	}

	@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) {
		List<String> descriptorList = new ArrayList<>();

		descriptorList.add(type.getDescriptor(gc));
		descriptorList.add(type.getDescriptor(gc));
		descriptorList.add(Util.randomItemFrom(gc.getBodyShape().getLimbDescriptors()));

		return Util.randomItemFrom(descriptorList);
	}

	public String setType(GameCharacter owner, AbstractLegType type) {
		if(!Main.game.isStarted() || owner==null) {
			this.type = type;
			this.footStructure = type.getDefaultFootStructure();
//			if(!type.isLegConfigurationAvailable(this.getLegConfiguration())) {
//				this.legConfiguration = RacialBody.valueOfRace(type.getRace()).getLegConfiguration();
//				this.getType().applyLegConfigurationTransformation(owner, legConfiguration, true);
//			}
			if(owner!=null) {
				owner.postTransformationCalculation();
			}
			return "";
		}

		if(!type.isLegConfigurationAvailable(this.getLegConfiguration())) {
			return "
"
					+ "[style.italicsDisabled(Nothing happens, as [npc.namePos] "+legConfiguration.getName()+" body cannot be transformed into the type '"+type.getTransformName()+"'...)]"
					+ "
";
		}

		if (type == getType()) {
			if (owner.isPlayer()) {
				return "
[style.colourDisabled(You already have the [pc.legs] of [pc.a_legRace], so nothing happens...)]
";
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled([npc.Name] already has the [npc.legs] of [npc.a_legRace], so nothing happens...)]
");
			}

		} else {
			UtilText.transformationContentSB.setLength(0);

			if (owner.isPlayer()) {
				UtilText.transformationContentSB.append(
						"
"
							+ "Your [pc.legs] start to wobble and feel weak, and as you look down to see what's wrong, they start to transform. ");
			} else {
				UtilText.transformationContentSB.append(
						"
"
							+ "[npc.Name] almost loses [npc.her] balance as [npc.her] [npc.legs] start to transform. ");
			}
		}

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

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

		UtilText.transformationContentSB.append(
				"
"
					+ "The transformation has left the structure of [npc.her] [npc.feet] as [style.boldTFGeneric("+this.footStructure.getName()+")]! "+this.footStructure.getDescription()
				+ "
");

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

	@Override
	public boolean isBestial(GameCharacter owner) {
		if(owner==null) {
			return false;
		}
		return owner.getLegConfiguration().getBestialParts().contains(Leg.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.MouthType;
import com.lilithsthrone.game.character.body.valueEnums.LipSize;
import com.lilithsthrone.game.character.body.valueEnums.OrificeModifier;
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;
import com.lilithsthrone.utils.Util;

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

	protected MouthType type;
	protected OrificeMouth orificeMouth;
	protected int lipSize;
	protected boolean piercedLip;

	public Mouth(MouthType type, int lipSize, int wetness, int capacity, int elasticity, int plasticity, boolean virgin) {
		this.type = type;
		this.lipSize = lipSize;
		orificeMouth = new OrificeMouth(wetness, capacity, elasticity, plasticity, virgin, type.getDefaultRacialOrificeModifiers());
	}

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

	public OrificeMouth getOrificeMouth() {
		return orificeMouth;
	}

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

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

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

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

	@Override
	public String getDescriptor(GameCharacter owner) {
		List<String> descriptorList = new ArrayList<>();

		for(OrificeModifier om : orificeMouth.getOrificeModifiers()) {
			descriptorList.add(om.getName());
		}
		descriptorList.add(owner.getCovering(owner.getMouthCovering()).getPrimaryColour().getName());

		descriptorList.add(type.getDescriptor(owner));

		return Util.randomItemFrom(descriptorList);
	}

	public String getLipsNameSingular(GameCharacter gc) {
		return UtilText.returnStringAtRandom("lip");
	}

	public String getLipsNamePlural(GameCharacter gc) {
		return UtilText.returnStringAtRandom("lips");
	}

	public String getLipsDescriptor(GameCharacter gc) {
		List<String> descriptorList = new ArrayList<>();

		if(!Main.game.isInSex() || getLipSize()!=LipSize.ONE_AVERAGE) {
			descriptorList.add(getLipSize().getName());
		}

		if (gc.isFeminine()) {
			descriptorList.add("soft");
			descriptorList.add("delicate");
		}

		return Util.randomItemFrom(descriptorList);
	}

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

	public LipSize getLipSize() {
		return LipSize.getLipSizeFromInt(lipSize);
	}

	public int getLipSizeValue() {
		return lipSize;
	}

	public String setLipSize(GameCharacter owner, int lipSize) {
		int effectiveLipSize = Math.max(0, Math.min(lipSize, LipSize.getLargest()));

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

		if(owner.getLipSizeValue() == effectiveLipSize) {
			if(owner.isPlayer()) {
				return "
[style.colourDisabled(The size of your [pc.lips] doesn't change...)]
";
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The size of [npc.namePos] [npc.lips] doesn't change...)]
");
			}
		}

		String transformation = "";

		if(this.lipSize > effectiveLipSize) {
			if(owner.isPlayer()) {
				transformation = "
A soothing coolness rises up into your [pc.lips], causing you to let out a surprised gasp as you feel them [style.boldShrink(shrinking)].
";
			} else {
				transformation = UtilText.parse(owner, "
[npc.Name] lets out a little cry as [npc.she] feels a soothing coolness rise up into [npc.her] [npc.lips], before they suddenly [style.boldShrink(shrink)].
");
			}

		} else {
			if(owner.isPlayer()) {
				transformation = "
A pulsating warmth rises up into your [pc.lips], causing you to let out a surprised gasp as you feel them [style.boldGrow(growing larger)].
";
			} else {
				transformation = UtilText.parse(owner, "
[npc.Name] lets out a little cry as [npc.she] feels a pulsating warmth rise up into [npc.her] [npc.lips], before they suddenly [style.boldGrow(grow larger)].
");
			}
		}

		this.lipSize = effectiveLipSize;

		if(owner.isPlayer()) {
			return transformation
				+ "You now have [style.boldSex([pc.lipSize] [pc.lips])]!
";
		} else {
			return transformation
					+ UtilText.parse(owner, "[npc.Name] now has [style.boldSex([npc.lipSize] [npc.lips])]!
");
		}
	}

	public boolean isPiercedLip() {
		return piercedLip;
	}

	public String setPiercedLip(GameCharacter owner, boolean piercedLip) {
		if(owner.isPiercedLip() == piercedLip) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		this.piercedLip = piercedLip;

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

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

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

	@Override
	public boolean isBestial(GameCharacter owner) {
		if(owner==null) {
			return false;
		}
		return owner.getLegConfiguration().getBestialParts().contains(Mouth.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.BodyCoveringType;
import com.lilithsthrone.game.character.body.types.BodyPartTypeInterface;
import com.lilithsthrone.game.character.body.types.NippleType;
import com.lilithsthrone.game.character.body.valueEnums.AreolaeShape;
import com.lilithsthrone.game.character.body.valueEnums.AreolaeSize;
import com.lilithsthrone.game.character.body.valueEnums.Capacity;
import com.lilithsthrone.game.character.body.valueEnums.NippleShape;
import com.lilithsthrone.game.character.body.valueEnums.NippleSize;
import com.lilithsthrone.game.character.body.valueEnums.OrificeModifier;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexAreaOrifice;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Util;

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

	protected NippleType type;
	protected OrificeNipples orificeNipples;
	protected NippleShape nippleShape;
	protected AreolaeShape areolaeShape;
	protected int areolaeSize;
	protected int nippleSize;
	protected boolean pierced;
	protected boolean crotchNipples;

	public Nipples(NippleType type, int nippleSize, NippleShape nippleShape, int areolaeSize, int wetness, float capacity, int elasticity, int plasticity, boolean virgin, boolean crotchNipples) {
		this.type = type;
		this.nippleSize = nippleSize;
		this.nippleShape = nippleShape;
		areolaeShape = AreolaeShape.NORMAL;
		this.areolaeSize = areolaeSize;
		orificeNipples = new OrificeNipples(wetness, capacity, elasticity, plasticity, virgin, type.getDefaultRacialOrificeModifiers());
		this.crotchNipples = crotchNipples;
	}

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

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

	@Override
	public String getNameSingular(GameCharacter owner) {
		// I commented this out as I felt that the crotch names (defined in type) were a little unwieldy
//		if(crotchNipples) {
//			return type.getNameCrotchSingular(owner);
//		} else {
			return type.getNameSingular(owner);
//		}
	}

	@Override
	public String getNamePlural(GameCharacter owner) {
		// I commented this out as I felt that the crotch names (defined in type) were a little unwieldy
//		if(crotchNipples) {
//			return type.getNameCrotchPlural(owner);
//		} else {
			return type.getNamePlural(owner);
//		}
	}

	@Override
	public String getDescriptor(GameCharacter owner) {
		List<String> descriptorList = new ArrayList<>();

		for(OrificeModifier om : orificeNipples.getOrificeModifiers()) {
			descriptorList.add(om.getName());
		}

		if(isCrotchNipples()) {
			if(owner.getNippleCrotchCovering()!=null) {
				descriptorList.add(owner.getCovering(owner.getNippleCrotchCovering()).getColourDescriptor(owner, false, false));
			}

			if(owner.isBreastCrotchFuckableNipplePenetration()) {
				switch(owner.getBreastCrotchMilkStorage().getAssociatedWetness()) {
					case ONE_SLIGHTLY_MOIST:
					case TWO_MOIST:
					case THREE_WET:
					case FOUR_SLIMY:
					case FIVE_SLOPPY:
					case SIX_SOPPING_WET:
					case SEVEN_DROOLING:
//						descriptorList.add(owner.getBreastCrotchMilkStorage().getAssociatedWetness().getDescriptor());
						descriptorList.add("milky");
						break;
					default:
						break;
				}
			}

		} else {
			if(owner.getNippleCovering()!=null) {
				descriptorList.add(owner.getCovering(owner.getNippleCovering()).getColourDescriptor(owner, false, false));
			}

			if(owner.isBreastFuckableNipplePenetration()) {
				switch(owner.getBreastMilkStorage().getAssociatedWetness()) {
					case ONE_SLIGHTLY_MOIST:
					case TWO_MOIST:
					case THREE_WET:
					case FOUR_SLIMY:
					case FIVE_SLOPPY:
					case SIX_SOPPING_WET:
					case SEVEN_DROOLING:
//						descriptorList.add(owner.getBreastMilkStorage().getAssociatedWetness().getDescriptor());
						descriptorList.add("milky");
						break;
					default:
						break;
				}
			}
		}

		if(Main.game.isInSex() && Sex.getAllParticipants().contains(owner)) {
			if(Sex.hasLubricationTypeFromAnyone(owner, SexAreaOrifice.NIPPLE)) {
				descriptorList.add("wet");
			}
		}

		descriptorList.add(type.getDescriptor(owner));
		if(orificeNipples.getCapacity()!= Capacity.ZERO_IMPENETRABLE) {
			descriptorList.add(Capacity.getCapacityFromValue(orificeNipples.getStretchedCapacity()).getDescriptor().replaceAll(" ", "-"));
		}

		return Util.randomItemFrom(descriptorList);
	}

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

	public void setType(GameCharacter owner, BodyPartTypeInterface type) {
		this.type = (NippleType) type;
	}

	public NippleSize getNippleSize() {
		return NippleSize.getNippleSizeFromInt(nippleSize);
	}

	public int getNippleSizeValue() {
		return nippleSize;
	}

	public String setNippleSize(GameCharacter owner, int nippleSize) {
		int boundNippleSize = Math.max(0, Math.min(nippleSize, NippleSize.FOUR_MASSIVE.getValue()));
		if(this.nippleSize == boundNippleSize) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(The size of your [pc.nipples] doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The size of [npc.namePos] [npc.nipples] doesn't change...)]
");
			}
		}

		String transformation = "";

		if(this.nippleSize > boundNippleSize) {
			if(owner.isPlayer()) {
				transformation = "
A soothing coolness rises up into your [pc.nipples], causing you to let out a surprised gasp as you feel them [style.boldShrink(shrinking)].
";
			} else {
				transformation = UtilText.parse(owner, "
[npc.Name] lets out a little cry as [npc.she] feels a soothing coolness rise up into [npc.her] [npc.nipples], before they suddenly [style.boldShrink(shrink)].
");
			}

		} else {
			if(owner.isPlayer()) {
				transformation = "
A pulsating warmth rises up into your [pc.nipples], causing you to let out a surprised gasp as you feel them [style.boldGrow(growing larger)].
";
			} else {
				transformation = UtilText.parse(owner, "
[npc.Name] lets out a little cry as [npc.she] feels a pulsating warmth rise up into [npc.her] [npc.nipples], before they suddenly [style.boldGrow(grow larger)].
");
			}
		}

		this.nippleSize = boundNippleSize;

		if(owner.isPlayer()) {
			return UtilText.parse(owner, transformation + "You now have [style.boldSex([pc.nippleSize] [pc.nipples])]!
");
		} else {
			return transformation
					+ UtilText.parse(owner, "[npc.Name] now has [style.boldSex([npc.nippleSize] [npc.nipples])]!
");
		}
	}

	public NippleShape getNippleShape() {
		return nippleShape;
	}

	public String setNippleShape(GameCharacter owner, NippleShape nippleShape) {

		if(this.nippleShape == nippleShape) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(The shape of your [pc.nipples] doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The shape of [npc.namePos] [npc.nipples] doesn't change...)]
");
			}
		}

		String transformation = "";

		switch(nippleShape) {
			case NORMAL:
				if(owner.isPlayer()) {
					transformation = "
"
										+ "Your [pc.nipples] suddenly grow sore and sensitive, and before you have any time to react, they suddenly transform into normal-looking nipples.
"
										+ "Your [pc.nipplesFullDescriptionColour] [pc.nipples] have transformed into [style.boldSex(normal nipples)]!"
									+ "
";
				} else {
					transformation = "
"
										+ "[npc.Name] shifts about uncomfortably as [npc.her] [npc.nipples] start to grow sore and sensitive, before suddenly transforming into normal-looking nipples.
"
										+ "[npc.NamePos] [npc.nipplesFullDescriptionColour] [npc.nipples] have transformed into [style.boldSex(normal nipples)]!"
									+ "
";
				}
				break;
			case LIPS:
				if(owner.isPlayer()) {
					transformation = "
"
										+ "Your [pc.nipples] suddenly grow sore and sensitive, and before you have any time to react, they suddenly swell up and transform into juicy pairs of lips!
"
										+ "Your [pc.nipplesFullDescriptionColour] [pc.nipples] have transformed into [style.boldSex(lip-like lipples)], which you can control just like regular lips!"
									+ "
";
				} else {
					transformation = "
"
										+ "[npc.Name] shifts about uncomfortably as [npc.her] [npc.nipples] start to grow sore and sensitive, before suddenly swelling up and transforming into juicy pairs of lips!
"
										+ "[npc.NamePos] [npc.nipplesFullDescriptionColour] [npc.nipples] have transformed into [style.boldSex(lip-like lipples)], which [npc.she] can control just like regular lips!"
									+ "
";
				}
				break;
			case VAGINA:
				if(owner.isPlayer()) {
					transformation = "
"
										+ "Your [pc.nipples] suddenly grow sore and sensitive, and before you have any time to react, they suddenly shift and transform into vaginas!
"
										+ "Your [pc.nipplesFullDescriptionColour] [pc.nipples] have transformed into [style.boldSex(vagina-like nipple-cunts)]!"
									+ "
";
				} else {
					transformation = "
"
										+ "[npc.Name] shifts about uncomfortably as [npc.her] [npc.nipples] start to grow sore and sensitive, before suddenly shifting and transforming into vaginas!
"
										+ "[npc.NamePos] [npc.nipplesFullDescriptionColour] [npc.nipples] have transformed into [style.boldSex(vagina-like nipple-cunts)]!"
									+ "
";
				}
				break;
		}

		// Parse TF before changing nipple type:
		transformation = UtilText.parse(owner, transformation);

		this.nippleShape = nippleShape;

		return transformation;
	}

	public AreolaeShape getAreolaeShape() {
		return areolaeShape;
	}

	public String setAreolaeShape(GameCharacter owner, AreolaeShape areolaeShape) {

		if(this.areolaeShape == areolaeShape) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(The shape of your areolae doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The shape of [npc.namePos] areolae doesn't change...)]
");
			}
		}

		this.areolaeShape = areolaeShape;

		String transformation = "";
		switch(areolaeShape) {
			case NORMAL:
				if(owner.isPlayer()) {
					transformation = "
Your [pc.nipples] suddenly start to tingle, and you gasp as you feel your areolae shift and transform into regular-looking circles.
"
								+ "Your areolae are now shaped like [style.boldSex(circles)]!";
				} else {
					transformation = "
[npc.NamePos] [npc.nipples] suddenly start to tingle, and [npc.she] gasps as [npc.she] feels [npc.her] areolae shift and transform into regular-looking circles.
"
							+ "[npc.NamePos] areolae are now shaped like [style.boldSex(circles)]!";
				}
				break;
			case HEART:
				if(owner.isPlayer()) {
					transformation = "
Your [pc.nipples] suddenly start to tingle, and you gasp as you feel your areolae shift and transform into the shape of hearts.
"
							+ "Your areolae are now shaped like [style.boldSex(hearts)]!";
				} else {
					transformation = "
[npc.NamePos] [npc.nipples] suddenly start to tingle, and [npc.she] gasps as [npc.she] feels [npc.her] areolae shift and transform into the shape of hearts.
"
							+ "[npc.NamePos] areolae are now shaped like [style.boldSex(hearts)]!";
				}
				break;
			case STAR:
				if(owner.isPlayer()) {
					transformation = "
Your [pc.nipples] suddenly start to tingle, and you gasp as you feel your areolae shift and transform into the shape of stars.
"
							+ "Your areolae are now shaped like [style.boldSex(stars)]!";
				} else {
					transformation = "
[npc.NamePos] [npc.nipples] suddenly start to tingle, and [npc.she] gasps as [npc.she] feels [npc.her] areolae shift and transform into the shape of stars.
"
							+ "[npc.NamePos] areolae are now shaped like [style.boldSex(stars)]!";
				}
				break;
		}

		return UtilText.parse(owner, transformation);
	}

	public OrificeNipples getOrificeNipples() {
		return orificeNipples;
	}

	public AreolaeSize getAreolaeSize() {
		return AreolaeSize.getAreolaeSizeFromInt(areolaeSize);
	}

	public int getAreolaeSizeValue() {
		return areolaeSize;
	}

	public String setAreolaeSize(GameCharacter owner, int areolaeSize) {
		int boundAreolaeSize = Math.max(0, Math.min(areolaeSize, AreolaeSize.FOUR_MASSIVE.getValue()));
		if (this.areolaeSize == boundAreolaeSize) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(The size of your areolae doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The size of [npc.namePos] areolae doesn't change...)]
");
			}
		}

		String transformation = "";

		if (this.areolaeSize > boundAreolaeSize) {
			if(owner.isPlayer()) {
				transformation = "
You feel a strange tingling sensation suddenly build up around your [pc.nipples], and you let out a little cry as you feel your areolae [style.boldShrink(shrinking)].
";
			} else {
				transformation = UtilText.parse(owner, "
[npc.Name] lets out a little cry as [npc.she] feels a strange tingling sensation suddenly build up around [npc.her] [npc.nipples], before [npc.her] areolae suddenly [style.boldShrink(shrink)].
");
			}

		} else {
			if(owner.isPlayer()) {
				transformation = "
You feel a strange tingling sensation suddenly build up around your [pc.nipples], and you let out a little cry as you feel your areolae [style.boldGrow(getting larger)].
";
			} else {
				transformation = UtilText.parse(owner, "
[npc.Name] lets out a little cry as [npc.she] feels a strange tingling sensation suddenly build up around [npc.her] [npc.nipples], before [npc.her] areolae suddenly [style.boldGrow(grow larger)].
");
			}
		}

		this.areolaeSize = boundAreolaeSize;

		if(owner.isPlayer()) {
			return UtilText.parse(owner, transformation
				+ "You now have [style.boldSex([pc.areolaeSize] [pc.nipples])]!
");
		} else {
			return transformation
					+ UtilText.parse(owner, "[npc.Name] now has [style.boldSex([npc.areolaeSize] [npc.nipples])]!
");
		}
	}

	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.nipples] are now [style.boldGrow(pierced)]!
";
			} else {
				return UtilText.parse(owner,
						"
[npc.NamePos] [npc.nipples] are now [style.boldGrow(pierced)]!
");
			}

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

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

	}

	public boolean isCrotchNipples() {
		return crotchNipples;
	}

	@Override
	public BodyCoveringType getBodyCoveringType(GameCharacter gc) {
		if(this.isCrotchNipples()) {
			return BodyCoveringType.NIPPLES_CROTCH;
		}
		return BodyCoveringType.NIPPLES;
	}

	@Override
	public BodyCoveringType getBodyCoveringType(Body body) {
		if(this.isCrotchNipples()) {
			return BodyCoveringType.NIPPLES_CROTCH;
		}
		return BodyCoveringType.NIPPLES;
	}

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


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

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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.types.OrificeInterface;
import com.lilithsthrone.game.character.body.valueEnums.Capacity;
import com.lilithsthrone.game.character.body.valueEnums.OrificeElasticity;
import com.lilithsthrone.game.character.body.valueEnums.OrificeModifier;
import com.lilithsthrone.game.character.body.valueEnums.OrificePlasticity;
import com.lilithsthrone.game.character.body.valueEnums.Wetness;
import com.lilithsthrone.game.dialogue.utils.UtilText;

/**
 * @since 0.1.?
 * @version 0.2.4
 * @author Innoxia
 */
public class OrificeAnus implements OrificeInterface {

	protected int wetness;
	protected int elasticity;
	protected int plasticity;
	protected float capacity;
	protected float stretchedCapacity;
	protected boolean virgin;
	protected Set<OrificeModifier> orificeModifiers;

	public OrificeAnus(int wetness, float capacity, int elasticity, int plasticity, boolean virgin, Collection<OrificeModifier> orificeModifiers) {
		this.wetness = wetness;
		this.capacity = capacity;
		stretchedCapacity = capacity;
		this.elasticity = elasticity;
		this.plasticity = plasticity;
		this.virgin = virgin;

		this.orificeModifiers = new HashSet<>(orificeModifiers);
	}

	@Override
	public Wetness getWetness(GameCharacter owner) {
		return Wetness.valueOf(wetness);
	}

	@Override
	public String setWetness(GameCharacter owner, int wetness) {
		int oldWetness = this.wetness;
		this.wetness = Math.max(0, Math.min(wetness, Wetness.SEVEN_DROOLING.getValue()));
		int wetnessChange = this.wetness - oldWetness;

		if (wetnessChange == 0) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(Your [pc.asshole]'s wetness doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The wetness of [npc.namePos] [npc.asshole] doesn't change...)]
");
			}
		}

		if(this.wetness < Wetness.SEVEN_DROOLING.getValue() && owner.getBodyMaterial().isOrificesAlwaysMaximumWetness()) {
			this.wetness = Wetness.SEVEN_DROOLING.getValue();
			if(owner.isPlayer()) {
				return "
[style.colourSex(Due to being made out of "+owner.getBodyMaterial().getName()+", your [pc.asshole] can't be anything but "+Wetness.SEVEN_DROOLING.getDescriptor()+"...)]
";
			} else {
				return UtilText.parse(owner, "
[style.colourSex(Due to being made out of "+owner.getBodyMaterial().getName()+", [npc.namePos] [npc.asshole] can't be anything but "+Wetness.SEVEN_DROOLING.getDescriptor()+"...)]
");
			}
		}

		String wetnessDescriptor = getWetness(owner).getDescriptor();
		String transformation = "";
		if (wetnessChange > 0) {
			if (owner.isPlayer()) {
				transformation = "
"
							+ "Your [pc.eyes] widen as you feel moisture beading around your asshole, and you let out [pc.a_moan+] as you realise that your rear entrance is lubricating itself and [style.boldGrow(getting wetter)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(wetnessDescriptor) + " " + wetnessDescriptor + " asshole)]!"
						+ "
";
			} else {
				transformation = "
"
							+ "[npc.NamePos] [npc.eyes] widen as [npc.she] feels moisture beading around [npc.her] asshole,"
								+ " and [npc.she] lets out [npc.a_moan+] as [npc.she] realises that [npc.her] rear entrance is lubricating itself and [style.boldGrow(getting wetter)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(wetnessDescriptor) + " " + wetnessDescriptor + " asshole)]!"
						+ "
";
			}

		} else {
			if (owner.isPlayer()) {
				transformation = "
"
							+ "You shift about uncomfortably and let out a frustrated groan as you feel your rear entrance [style.boldShrink(getting drier)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(wetnessDescriptor) + " " + wetnessDescriptor + " asshole)]!"
						+ "
";
			} else {
				transformation = "
"
							+ "[npc.Name] shifts about uncomfortably and lets out a frustrated groan as [npc.she] feels [npc.her] rear entrance [style.boldShrink(getting drier)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(wetnessDescriptor) + " " + wetnessDescriptor + " asshole)]!"
						+ "
";
			}
		}
		return UtilText.parse(owner, transformation);
	}

	@Override
	public Capacity getCapacity() {
		return Capacity.getCapacityFromValue(capacity);
	}

	@Override
	public float getRawCapacityValue() {
		return capacity;
	}

	@Override
	public String setCapacity(GameCharacter owner, float capacity, boolean setStretchedValueToNewValue) {
		float oldCapacity = this.capacity;
		this.capacity = Math.max(0, Math.min(capacity, Capacity.SEVEN_GAPING.getMaximumValue()));
		if(setStretchedValueToNewValue) {
			this.stretchedCapacity = this.capacity;
		}
		float capacityChange = this.capacity - oldCapacity;

		if (capacityChange == 0) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(Your [pc.asshole]'s capacity doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The capacity of [npc.namePos] [npc.asshole] doesn't change...)]
");
			}
		}

		String capacityDescriptor = getCapacity().getDescriptor();
		if (capacityChange > 0) {
			if (owner.isPlayer()) {
				return UtilText.parse(owner, "
"
							+ "You let out a shocked gasp as you feel your asshole dilate and stretch out as its internal [style.boldGrow(capacity increases)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(capacityDescriptor) + " " + capacityDescriptor + " asshole)]!"
						+ "
");
			} else {
				return UtilText.parse(owner,
						"
"
							+ "[npc.Name] lets out a shocked gasp as [npc.she] feels [npc.her] asshole dilate and stretch out as its internal [style.boldGrow(capacity increases)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(capacityDescriptor) + " " + capacityDescriptor + " asshole)]!"
						+ "
");
			}

		} else {
			if (owner.isPlayer()) {
				return UtilText.parse(owner, "
"
							+ "You let out a cry as you feel your asshole uncontrollably tighten and clench as its internal [style.boldShrink(capacity decreases)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(capacityDescriptor) + " " + capacityDescriptor + " asshole)]!"
						+ "
");
			} else {
				return UtilText.parse(owner,
						"
"
							+ "[npc.Name] lets out a cry as [npc.she] feels [npc.her] asshole uncontrollably tighten and clench as its internal [style.boldShrink(capacity decreases)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(capacityDescriptor) + " " + capacityDescriptor + " asshole)]!"
						+ "
");
			}
		}
	}

	@Override
	public float getStretchedCapacity() {
		return stretchedCapacity;
	}

	@Override
	public boolean setStretchedCapacity(float stretchedCapacity) {
		float oldStretchedCapacity = this.stretchedCapacity;
		this.stretchedCapacity = Math.max(0, Math.min(stretchedCapacity, Capacity.SEVEN_GAPING.getMaximumValue()));
		return oldStretchedCapacity != this.stretchedCapacity;
	}

	@Override
	public OrificeElasticity getElasticity() {
		return OrificeElasticity.getElasticityFromInt(elasticity);
	}

	@Override
	public String setElasticity(GameCharacter owner, int elasticity) {
		int oldElasticity = this.elasticity;
		this.elasticity = Math.max(0, Math.min(elasticity, OrificeElasticity.SEVEN_ELASTIC.getValue()));
		int elasticityChange = this.elasticity - oldElasticity;

		if (elasticityChange == 0) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(Your [pc.asshole]'s elasticity doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The elasticity of [npc.namePos] [npc.asshole] doesn't change...)]
");
			}
		}

		String elasticityDescriptor = getElasticity().getDescriptor();
		if (elasticityChange > 0) {
			if (owner.isPlayer()) {
				return UtilText.parse(owner, "
"
							+ "You let out a little gasp as you feel a strange slackening sensation pulsating deep within your ass as your asshole's [style.boldGrow(elasticity increases)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(elasticityDescriptor) + " " + elasticityDescriptor + " asshole)]!"
						+ "
");
			} else {
				return UtilText.parse(owner,
						"
"
							+ "[npc.Name] lets out a little gasp as [npc.she] feels a strange slackening sensation pulsating deep within [npc.her] ass as [npc.her] asshole's [style.boldGrow(elasticity increases)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(elasticityDescriptor) + " " + elasticityDescriptor + " asshole)]!"
						+ "
");
			}

		} else {
			if (owner.isPlayer()) {
				return UtilText.parse(owner, "
"
							+ "You let out a little gasp as you feel a strange clenching sensation pulsating deep within your ass as your asshole's [style.boldShrink(elasticity decreases)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(elasticityDescriptor) + " " + elasticityDescriptor + " asshole)]!"
						+ "
");
			} else {
				return UtilText.parse(owner,
						"
"
							+ "[npc.Name] lets out a little gasp as [npc.she] feels a strange clenching sensation pulsating deep within [npc.her] ass as [npc.her] asshole's [style.boldShrink(elasticity decreases)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(elasticityDescriptor) + " " + elasticityDescriptor + " asshole)]!"
						+ "
");
			}
		}
	}

	@Override
	public OrificePlasticity getPlasticity() {
		return OrificePlasticity.getElasticityFromInt(plasticity);
	}

	@Override
	public String setPlasticity(GameCharacter owner, int plasticity) {
		int oldPlasticity = this.plasticity;
		this.plasticity = Math.max(0, Math.min(plasticity, OrificePlasticity.SEVEN_MOULDABLE.getValue()));
		int plasticityChange = this.plasticity - oldPlasticity;

		if (plasticityChange == 0) {
			if(owner.isPlayer()) {
				return UtilText.parse(owner, "
[style.colourDisabled(Your [pc.asshole]'s plasticity doesn't change...)]
");
			} else {
				return UtilText.parse(owner, "
[style.colourDisabled(The plasticity of [npc.namePos] [npc.asshole] doesn't change...)]
");
			}
		}

		String plasticityDescriptor = getPlasticity().getDescriptor();
		if (plasticityChange > 0) {
			if (owner.isPlayer()) {
				return UtilText.parse(owner, "
"
							+ "You let out a little gasp as you feel a strange moulding sensation pulsating deep within your ass as your asshole's [style.boldGrow(plasticity increases)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(plasticityDescriptor) + " " + plasticityDescriptor + " asshole)]!"
						+ "
");
			} else {
				return UtilText.parse(owner,
						"
"
							+ "[npc.Name] lets out a little gasp as [npc.she] feels a strange moulding sensation pulsating deep within [npc.her] ass as [npc.her] asshole's [style.boldGrow(plasticity increases)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(plasticityDescriptor) + " " + plasticityDescriptor + " asshole)]!"
						+ "
");
			}

		} else {
			if (owner.isPlayer()) {
				return UtilText.parse(owner, "
"
							+ "You let out a little gasp as you feel a strange softening sensation pulsating deep within your ass as your asshole's [style.boldShrink(plasticity decreases)].
"
							+ "The transformation quickly passes, leaving you with [style.boldSex(" + UtilText.generateSingularDeterminer(plasticityDescriptor) + " " + plasticityDescriptor + " asshole)]!"
						+ "
");
			} else {
				return UtilText.parse(owner,
						"
"
							+ "[npc.Name] lets out a little gasp as [npc.she] feels a strange softening sensation pulsating deep within [npc.her] ass as [npc.her] asshole's [style.boldShrink(plasticity decreases)].
"
							+ "The transformation quickly passes, leaving [npc.herHim] with [style.boldSex(" + UtilText.generateSingularDeterminer(plasticityDescriptor) + " " + plasticityDescriptor + " asshole)]!"
						+ "
");
			}
		}
	}

	@Override
	public boolean isVirgin() {
		return virgin;
	}

	@Override
	public void setVirgin(boolean virgin) {
		this.virgin = virgin;
	}

	@Override
	public boolean hasOrificeModifier(OrificeModifier modifier) {
		return orificeModifiers.contains(modifier);
	}

	@Override
	public String addOrificeModifier(GameCharacter owner, OrificeModifier modifier) {
		if(hasOrificeModifier(modifier)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		orificeModifiers.add(modifier);

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

		switch(modifier) {
			case MUSCLE_CONTROL:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure swelling up deep within your [pc.ass], but before you have any chance to react, the feeling suddenly fades away."
								+ " With an experimental clench, you discover that the interior of your [pc.asshole] is now lined with [style.boldGrow(extra muscles)], which you can use to expertly grip and squeeze down on any penetrating object.
"
								+ "[style.boldSex(Your asshole is now lined with an intricate series of muscles!)]"
							+ "
";
				} else {
					return "
"
								+ "[npc.Name] lets out a little cry as an intense pressure swells up deep within [npc.her] [npc.ass], but before [npc.she] has any chance to react, the feeling quickly dissipates."
								+ " With an experimental clench, [npc.she] discovers that the interior of [npc.her] [npc.asshole] is now lined with [style.boldGrow(extra muscles)],"
									+ " which [npc.she] can use to expertly grip and squeeze down on any penetrating object.
"
								+ "[style.boldSex([npc.NamePos] asshole is now lined with an intricate series of muscles!)]"
							+ "
";
				}
			case RIBBED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure swelling up deep within your [pc.ass], but before you have any chance to react, the feeling suddenly fades away."
								+ " Shifting your [pc.ass] around a little, you feel that the inside of your [pc.asshole] is now lined with [style.boldGrow(fleshy, highly-sensitive ribs)], which provide extreme pleasure when stimulated.
"
								+ "[style.boldSex(Your asshole is now lined with fleshy, pleasure-inducing ribs!)]"
							+ "
";
				} else {
					return "
"
							+ "[npc.Name] lets out a little cry as an intense pressure swells up deep within [npc.her] [npc.ass], but before [npc.she] has any chance to react, the feeling quickly dissipates."
							+ " Shifting [npc.her] [npc.ass] around a little, [npc.she] discovers that the inside of [npc.her] [npc.asshole] is now lined with [style.boldGrow(fleshy, highly-sensitive ribs)],"
								+ " which provide extreme pleasure when stimulated.
"
							+ "[style.boldSex([npc.NamePos] asshole is now lined with fleshy, pleasure-inducing ribs!)]"
						+ "
";
				}
			case TENTACLED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure swelling up deep within your [pc.ass], but before you have any chance to react, the feeling suddenly fades away."
								+ " A surprised cry bursts out from your mouth as you feel that the inside of your [pc.asshole] is now filled with [style.boldGrow(a series of little wriggling tentacles)], over which you have limited control.
"
								+ "[style.boldSex(The inside of your asshole is now filled with little tentacles, which wriggle with a mind of their own!)]"
							+ "
";
				} else {
					return "
"
								+ "[npc.Name] lets out a little cry as an intense pressure swells up deep within [npc.her] [npc.ass], but before [npc.she] has any chance to react, the feeling quickly dissipates."
								+ " With an experimental clench, [npc.she] discovers that the inside of [npc.her] [npc.asshole] is now filled with [style.boldGrow(a series of little wriggling tentacles)], over which [npc.she] has limited control.
"
								+ "[style.boldSex(The inside of [npc.namePos] asshole is now filled with little tentacles, which wriggle with a mind of their own!)]"
							+ "
";
				}
			case PUFFY:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a tingling sensation running over your [pc.ass], and you let out a little cry as you feel the rim of your [pc.asshole] [style.boldGrow(puff up)] into a doughnut-like ring.
"
								+ "[style.boldSex(The rim of your asshole is now swollen and puffy!)]"
							+ "
";
				} else {
					return "
"
								+ "[npc.Name] lets out a little cry as [npc.she] feels a tingling sensation running over [npc.her] [npc.ass], before the rim of [npc.her] [npc.asshole] [style.boldGrow(puffs up)] into a doughnut-like ring.
"
								+ "[style.boldSex(The rim of [npc.namePos] asshole is now swollen and puffy!)]"
							+ "
";
				}
		}

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

	@Override
	public String removeOrificeModifier(GameCharacter owner, OrificeModifier modifier) {
		if(!hasOrificeModifier(modifier)) {
			return "
[style.colourDisabled(Nothing happens...)]
";
		}

		orificeModifiers.remove(modifier);

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

		switch(modifier) {
			case MUSCLE_CONTROL:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure swelling up deep within your [pc.ass], but before you have any chance to react, the feeling suddenly fades away."
								+ " With an experimental clench, you discover that the interior of your [pc.asshole] has lost its [style.boldShrink(extra muscles)].
"
								+ "[style.boldSex(Your asshole is no longer lined with an intricate series of muscles!)]"
							+ "
";
				} else {
					return "
"
								+ "[npc.Name] lets out a little cry as an intense pressure swells up deep within [npc.her] [npc.ass], but before [npc.she] has any chance to react, the feeling quickly dissipates."
								+ " With an experimental clench, [npc.she] discovers that the interior of [npc.her] [npc.asshole] has lost its [style.boldShrink(extra muscles)].
"
								+ "[style.boldSex([npc.NamePos] asshole is no longer lined with an intricate series of muscles!)]"
							+ "
";
				}
			case RIBBED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure swelling up deep within your [pc.ass], but before you have any chance to react, the feeling suddenly fades away."
								+ " Shifting your [pc.ass] around a little, you feel that the [style.boldShrink(fleshy, highly-sensitive ribs)] that once lined your [pc.asshole] have vanished.
"
								+ "[style.boldSex(Your asshole is no longer lined with fleshy, pleasure-inducing ribs!)]"
							+ "
";
				} else {
					return "
"
							+ "[npc.Name] lets out a little cry as an intense pressure swells up deep within [npc.her] [npc.ass], but before [npc.she] has any chance to react, the feeling quickly dissipates."
							+ " Shifting [npc.her] [npc.ass] around a little, [npc.she] discovers that the [style.boldShrink(fleshy, highly-sensitive ribs)] that once lined [npc.her] [npc.asshole] have vanished.
"
							+ "[style.boldSex([npc.NamePos] asshole is no longer lined with fleshy, pleasure-inducing ribs!)]"
						+ "
";
				}
			case TENTACLED:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel an intense pressure swelling up deep within your [pc.ass], but before you have any chance to react, the feeling suddenly fades away."
								+ " A surprised cry bursts out from your mouth as you feel that the [style.boldShrink(series of little wriggling tentacles)] within your [pc.asshole] have all disappeared.
"
								+ "[style.boldSex(The inside of your asshole is no longer filled with little tentacles!)]"
							+ "
";
				} else {
					return "
"
								+ "[npc.Name] lets out a little cry as an intense pressure swells up deep within [npc.her] [npc.ass], but before [npc.she] has any chance to react, the feeling quickly dissipates."
								+ " With an experimental clench, [npc.she] discovers that the [style.boldShrink(series of little wriggling tentacles)] within [npc.her] [npc.asshole] have all disappeared.
"
								+ "[style.boldSex(The inside of [npc.namePos] asshole is no longer filled with little tentacles!)]"
							+ "
";
				}
			case PUFFY:
				if(owner.isPlayer()) {
					return "
"
								+ "You feel a tingling sensation running over your [pc.ass], and you let out a little cry as you feel the puffy rim of your [pc.asshole] [style.boldGrow(deflate)] into a more normal-looking shape.
"
								+ "[style.boldSex(The rim of your asshole is no longer swollen and puffy!)]"
							+ "
";
				} else {
					return "
"
								+ "[npc.Name] lets out a little cry as [npc.she] feels a tingling sensation running over [npc.her] [npc.ass],"
									+ " before the puffy rim of [npc.her] [npc.asshole] [style.boldShrink(deflates)] into a more normal-looking shape.
"
								+ "[style.boldSex(The rim of [npc.namePos] asshole is no longer swollen and puffy!)]"
							+ "
";
				}
		}

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

	public Set<OrificeModifier> getOrificeModifiers() {
		return orificeModifiers;
	}

	public void clearOrificeModifiers() {
		orificeModifiers.clear();
	}

}