code:
package com.lilithsthrone.game.sex.sexActions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.lilithsthrone.controller.MainController;
import com.lilithsthrone.game.PropertyValue;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.character.body.CoverableArea;
import com.lilithsthrone.game.character.body.valueEnums.CumProduction;
import com.lilithsthrone.game.character.body.valueEnums.FluidModifier;
import com.lilithsthrone.game.character.fetishes.Fetish;
import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.game.dialogue.responses.ResponseEffectsOnly;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
import com.lilithsthrone.game.inventory.enchanting.ItemEffect;
import com.lilithsthrone.game.inventory.enchanting.TFModifier;
import com.lilithsthrone.game.sex.ArousalIncrease;
import com.lilithsthrone.game.sex.CondomFailure;
import com.lilithsthrone.game.sex.LubricationType;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexAreaInterface;
import com.lilithsthrone.game.sex.SexAreaOrifice;
import com.lilithsthrone.game.sex.SexAreaPenetration;
import com.lilithsthrone.game.sex.SexControl;
import com.lilithsthrone.game.sex.SexPace;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.SexType;
import com.lilithsthrone.game.sex.positions.slots.SexSlotGeneric;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.GenericActions;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.0
 * @version 0.3.4.5
 * @author Innoxia
 */
public interface SexActionInterface {

	/**
	 * This is a method to support old sex actions. Do not set the return value of this to anything other than null.
	 * @return
	 */
	public default SexActionLimitation getLimitation() {//TODO remove this
		return null;
	}

	public abstract SexActionType getActionType();

	public default boolean isPositionSwap() {
		return false;
	}

	public default boolean isSadisticAction() {
		return false;
	}

	public abstract String getActionTitle();

	public abstract String getActionDescription();

	public abstract String getDescription();

	public abstract String getFluidFlavourDescription(GameCharacter performing, GameCharacter receiving);

	public CorruptionLevel getCorruptionNeeded();

	/**
	 * Determines if an additional character can join in on the ongoing action.
	 * Only used as a check in SexActionType.START_ADDITIONAL_ONGOING actions.
	 */
	public default Map<String, Boolean> getAdditionalOngoingAvailableMap() {
		if(getActionType()==SexActionType.START_ADDITIONAL_ONGOING) {
			throw new NullPointerException("getAdditionalOngoingAvailableMap() should not be returning null for a SexAction of type 'SexActionType.START_ADDITIONAL_ONGOING'. (Occurring in SexAction '"+getActionTitle()+"')");
		}
		return null;
	}

	public default boolean isAdditionalOngoingAvailable() {
		if(getAdditionalOngoingAvailableMap()!=null) {
			for(Boolean b : getAdditionalOngoingAvailableMap().values()) {
				if(!b) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * The keys are areas that belong to the performing character. The values are areas that belong to the targeted character.
	 * @return
	 */
	public Map<SexAreaInterface, SexAreaInterface> getSexAreaInteractions();

	public default List<SexAreaOrifice> getPerformingCharacterOrifices() {
		List<SexAreaOrifice> list = new ArrayList<>();
		for(SexAreaInterface sArea : getSexAreaInteractions().keySet()) {
			if(sArea!=null && sArea.isOrifice()) {
				list.add((SexAreaOrifice)sArea);
			}
		}
		return list;
	}

	public default List<SexAreaPenetration> getPerformingCharacterPenetrations() {
		List<SexAreaPenetration> list = new ArrayList<>();
		for(SexAreaInterface sArea : getSexAreaInteractions().keySet()) {
			if(sArea!=null && sArea.isPenetration()) {
				list.add((SexAreaPenetration)sArea);
			}
		}
		return list;
	}

	public default List<SexAreaInterface> getPerformingCharacterAreas() {
		List<SexAreaInterface> list = new ArrayList<>();
		for(SexAreaInterface sArea : getSexAreaInteractions().keySet()) {
			if(sArea!=null) {
				list.add(sArea);
			}
		}
		return list;
	}

	public default List<SexAreaOrifice> getTargetedCharacterOrifices() {
		List<SexAreaOrifice> list = new ArrayList<>();
		for(SexAreaInterface sArea : getSexAreaInteractions().values()) {
			if(sArea!=null && sArea.isOrifice()) {
				list.add((SexAreaOrifice)sArea);
			}
		}
		return list;
	}

	public default List<SexAreaPenetration> getTargetedCharacterPenetrations() {
		List<SexAreaPenetration> list = new ArrayList<>();
		for(SexAreaInterface sArea : getSexAreaInteractions().values()) {
			if(sArea!=null && sArea.isPenetration()) {
				list.add((SexAreaPenetration)sArea);
			}
		}
		return list;
	}

	public default List<SexAreaInterface> getTargetedCharacterAreas() {
		List<SexAreaInterface> list = new ArrayList<>();
		for(SexAreaInterface sArea : getSexAreaInteractions().values()) {
			if(sArea!=null) {
				list.add(sArea);
			}
		}
		return list;
	}

	/**
	 * @return true if any character can lose virginity from this action.
	 */
	public default boolean isTakesVirginity(boolean includeForeplayOrifices) {

		boolean penetrationTakesVirginity = false;
		boolean orificeHasVirginity = false;
		for(SexAreaPenetration sArea : this.getPerformingCharacterPenetrations()) {
			if(sArea.isTakesVirginity()) {
				penetrationTakesVirginity = true;
			}
		}
		for(SexAreaOrifice sArea : this.getTargetedCharacterOrifices()) {
			if(sArea.isInternalOrifice() && (includeForeplayOrifices || sArea!=SexAreaOrifice.MOUTH)) {
				orificeHasVirginity = true;
			}
		}
		if(penetrationTakesVirginity && orificeHasVirginity) {
			return true;
		}

		for(SexAreaPenetration sArea : this.getTargetedCharacterPenetrations()) {
			if(sArea.isTakesVirginity()) {
				penetrationTakesVirginity = true;
			}
		}
		for(SexAreaOrifice sArea : this.getPerformingCharacterOrifices()) {
			if(sArea.isInternalOrifice() && (includeForeplayOrifices || sArea!=SexAreaOrifice.MOUTH)) {
				orificeHasVirginity = true;
			}
		}
		return penetrationTakesVirginity && orificeHasVirginity;
	}

	public abstract SexParticipantType getParticipantType();

	/**
	 * @return A list of fetishes that affect the character in this sex action.
	 */
	public List<Fetish> getFetishes(GameCharacter characterPerformingAction);

	/**
	 * @return A list of fetishes that affect the target of 'characterPerformingAction' in this sex action.
	 */
	public List<Fetish> getFetishesForTargetedPartner(GameCharacter characterPerformingAction);

	// Sex-specific:

	public ArousalIncrease getArousalGainSelf();

	public ArousalIncrease getArousalGainTarget();

	/**
	 * @return A String to be appended and displayed immediately before the sex action's description.
	 */
	public default String preDescriptionBaseEffects() {
		StringBuilder stopSB = new StringBuilder();

		if(getActionType()==SexActionType.START_ONGOING) {
			for(Entry<SexAreaInterface, SexAreaInterface> entry : getSexAreaInteractions().entrySet()) {
				try {
					if(!entry.getKey().isFree(Sex.getCharacterPerformingAction())) {
						Map<GameCharacter, Set<SexAreaInterface>> map = Sex.getOngoingActionsMap(Sex.getCharacterPerformingAction()).get(entry.getKey());
						Entry<GameCharacter, Set<SexAreaInterface>> firstEntry = map.entrySet().iterator().next();
						stopSB.append(Sex.stopOngoingAction(
								Sex.getCharacterPerformingAction(),
								entry.getKey(),
								firstEntry.getKey(),
								firstEntry.getValue().iterator().next(),
								false));
					}
				} catch(Exception ex) {
					// No first entry in iterator found
				}
				try {
					if(!entry.getValue().isFree(Sex.getCharacterTargetedForSexAction(this))) {
						Map<GameCharacter, Set<SexAreaInterface>> map = Sex.getOngoingActionsMap(Sex.getCharacterTargetedForSexAction(this)).get(entry.getValue());
						Entry<GameCharacter, Set<SexAreaInterface>> firstEntry = map.entrySet().iterator().next();
						stopSB.append(Sex.stopOngoingAction(
								firstEntry.getKey(),
								firstEntry.getValue().iterator().next(),
								Sex.getCharacterTargetedForSexAction(this),
								entry.getValue(),
								false));
					}
				} catch(Exception ex) {
					// No first entry in iterator found
				}
			}
		}
		return stopSB.toString();
	}

	public default String baseEffects() {
		if(getActionType()==SexActionType.START_ONGOING || getActionType()==SexActionType.START_ADDITIONAL_ONGOING) {
			for(Entry<SexAreaInterface, SexAreaInterface> entry : getSexAreaInteractions().entrySet()) {
				Sex.applyOngoingAction(
						Sex.getCharacterPerformingAction(),
						entry.getKey(),
						Sex.getCharacterTargetedForSexAction(this),
						entry.getValue(),
						getActionType()!=SexActionType.START_ADDITIONAL_ONGOING);
			}

			if(SexActionInterface.this.getPerformingCharacterAreas().stream().anyMatch((area) -> area.equals(SexAreaOrifice.MOUTH))
					|| SexActionInterface.this.getPerformingCharacterAreas().stream().anyMatch((area) -> area.equals(SexAreaPenetration.TONGUE))) {
				Sex.getCharacterPerformingAction().setAreaKnownByCharacter(CoverableArea.MOUTH, Sex.getCharacterTargetedForSexAction(SexActionInterface.this), true);

			} else if(SexActionInterface.this.getTargetedCharacterAreas().stream().anyMatch((area) -> area.equals(SexAreaOrifice.MOUTH))
					|| SexActionInterface.this.getTargetedCharacterAreas().stream().anyMatch((area) -> area.equals(SexAreaPenetration.TONGUE))) {
				Sex.getCharacterTargetedForSexAction(SexActionInterface.this).setAreaKnownByCharacter(CoverableArea.MOUTH, Sex.getCharacterPerformingAction(), true);
			}
		}

		if(getActionType()==SexActionType.POSITIONING) {
			// For reference, ongoing penetrations are reset in Sex.setSexManager()

			if(!Sex.getCharacterPerformingAction().isPlayer()) { // Ban further positioning actions (this is reset when moving from foreplay to main sex, or when orgasming):
				Sex.addCharacterBannedFromPositioning(Sex.getCharacterPerformingAction());
			}
		}

		if(getActionType()==SexActionType.STOP_ONGOING) {
			for(Entry<SexAreaInterface, SexAreaInterface> entry : getSexAreaInteractions().entrySet()) {
				if(entry.getKey()!=null) {
					if(entry.getValue()!=null) {
						Sex.stopOngoingAction(
								Sex.getCharacterPerformingAction(),
								entry.getKey(),
								Sex.getCharacterTargetedForSexAction(this),
								entry.getValue());

					} else {
						for(SexAreaInterface sArea : Sex.getContactingSexAreas(Sex.getCharacterPerformingAction(), entry.getKey(), Sex.getCharacterTargetedForSexAction(this))) {
							Sex.stopOngoingAction(
									Sex.getCharacterPerformingAction(),
									entry.getKey(),
									Sex.getCharacterTargetedForSexAction(this),
									sArea);
						}
					}

				} else {
					if(entry.getValue()!=null) {
						for(SexAreaInterface sArea : Sex.getContactingSexAreas(Sex.getCharacterTargetedForSexAction(this), entry.getValue(), Sex.getCharacterPerformingAction())) {
								Sex.stopOngoingAction(
										Sex.getCharacterPerformingAction(),
										entry.getKey(),
										Sex.getCharacterTargetedForSexAction(this),
										sArea);
						}
					}
				}
			}
		}

		applyEffects();

		StringBuilder sb = new StringBuilder();
		if(this.isSadisticAction()) {
			if(!Sex.getCharacterTargetedForSexAction(this).getFetishDesire(Fetish.FETISH_MASOCHIST).isPositive()) {
			sb.append("
"
						+ "[style.colourBad([npc2.Name] [npc2.verb(find)] this sadistic action to be a huge turn-off!)]"
						+ Sex.getCharacterTargetedForSexAction(this).incrementLust(-15, false)
					+"
");
			}
		}

		sb.append(applyEffectsString());

		return sb.toString();
	}

	public default void applyEffects(){
	}

	public default String applyEffectsString(){
		return "";
	}

	/**
	 * These effects are applied after everything else, so it is a safe place to put ongoing action stops or the like.
	 */
	public default void applyEndEffects(){
	}

	public default boolean isBaseRequirementsMet() {
		return true;
	}

	public default boolean isBasicCoreRequirementsMet() {
		if(!Main.getProperties().hasValue(PropertyValue.sadisticSexContent) && this.isSadisticAction()) {
			return false;
		}

		boolean analAllowed = true;
		try { // Wrap in try/catch block as some sex actions may make calls to ongoing actions that aren't ongoing yet
			analAllowed = Main.game.isAnalContentEnabled()
					|| Collections.disjoint(Util.mergeLists(this.getFetishes(Sex.getCharacterPerformingAction()), this.getFetishesForTargetedPartner(Sex.getCharacterPerformingAction())),
						Util.newArrayListOfValues(Fetish.FETISH_ANAL_GIVING, Fetish.FETISH_ANAL_RECEIVING));
		} catch(Exception ex) {
		}

		boolean footAllowed = true;
		try { // Wrap in try/catch block as some sex actions may make calls to ongoing actions that aren't ongoing yet
			footAllowed = Main.game.isFootContentEnabled()
					|| Collections.disjoint(Util.mergeLists(this.getFetishes(Sex.getCharacterPerformingAction()), this.getFetishesForTargetedPartner(Sex.getCharacterPerformingAction())),
						Util.newArrayListOfValues(Fetish.FETISH_FOOT_GIVING, Fetish.FETISH_FOOT_RECEIVING));
		} catch(Exception ex) {
		}

		boolean crotchBoobsAllowed = true;
		try { // Wrap in try/catch block as some sex actions may make calls to ongoing actions that aren't ongoing yet
			crotchBoobsAllowed = Main.getProperties().udders>0
					|| (!this.getTargetedCharacterAreas().contains(SexAreaOrifice.BREAST_CROTCH)
							&& !this.getTargetedCharacterAreas().contains(SexAreaOrifice.NIPPLE_CROTCH)
							&& !this.getPerformingCharacterAreas().contains(SexAreaOrifice.BREAST_CROTCH)
							&& !this.getPerformingCharacterAreas().contains(SexAreaOrifice.NIPPLE_CROTCH));
		} catch(Exception ex) {
		}

		return analAllowed
				&& footAllowed
				&& crotchBoobsAllowed
				&& (this.getSexPace()==null
					|| (this.getSexPace().isDom() == Sex.getSexPace(Sex.getCharacterPerformingAction()).isDom()))
				&& (this.getActionType()!=SexActionType.STOP_ONGOING // Can only stop non-self ongoing penetrations if full control
					|| this.getParticipantType()==SexParticipantType.SELF
					|| Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL)
				&& (Sex.getSexPositionSlot(Sex.getCharacterPerformingAction())!=SexSlotGeneric.MISC_WATCHING // Cannot switch positions as spectator
					|| this.getActionType()!=SexActionType.POSITIONING);
	}

	/**
	 * Used for determining how likely an NPC is to use this action.
	 */
	public default SexActionPriority getPriority() {
		return SexActionPriority.NORMAL;
	}

	/**
	 * Defines if this SexAction switches the pace of sex for the performing character.
	 * @return null if no switch.
	 */
	public SexPace getSexPace();

	public default boolean endsSex() {
		return false;
	}

	/**
	 * @return true If performer is able to free up orifices in order to perform this action.
	 */
	public default boolean isAbleToAccessParts(GameCharacter performer) {
		if(!this.isPhysicallyPossible()) {
			return false;
		}
		boolean canAccessSelfParts = true;
		for(SexAreaOrifice orifice : this.getPerformingCharacterOrifices()) {
			if(!orifice.isFree(performer)) {
				if(Sex.getSexControl(performer)!=SexControl.FULL) { // Doms and full control subs can always free up their own parts.
					for(GameCharacter character : Sex.getCharactersHavingOngoingActionWith(performer, orifice)) {
						if(!character.equals(performer) && Sex.isDom(character)) { // It's a non-self action with a dom:
							canAccessSelfParts = false;
						}
					}
				}
			}
		}
		for(SexAreaPenetration penetration : this.getPerformingCharacterPenetrations()) {
			if(!penetration.isFree(performer)) {
				if(Sex.getSexControl(performer)!=SexControl.FULL) { // Doms and full control subs can always free up their own parts.
					for(GameCharacter character : Sex.getCharactersHavingOngoingActionWith(performer, penetration)) {
						if(!character.equals(performer) && Sex.isDom(character)) { // It's a non-self action with a dom:
							canAccessSelfParts = false;
						}
					}
				}
			}
		}
		boolean canAccessOthersParts = true;
		GameCharacter target = Sex.getCharacterTargetedForSexAction(this);
		for(SexAreaOrifice orifice : this.getTargetedCharacterOrifices()) {
			if(!orifice.isFree(target)) {
				for(GameCharacter character : Sex.getCharactersHavingOngoingActionWith(target, orifice)) {
					if(!character.equals(performer) && !character.equals(target)) { // It's someone else they're interacting with:
						canAccessOthersParts = false;
					}
				}
			}
			if(!target.isAbleToAccessCoverableArea(orifice.getRelatedCoverableArea(), true)) {
				canAccessOthersParts = false;
			}
		}
		for(SexAreaPenetration penetration : this.getTargetedCharacterPenetrations()) {
			if(!penetration.isFree(target)) {
				for(GameCharacter character : Sex.getCharactersHavingOngoingActionWith(target, penetration)) {
					if(!character.equals(performer) && !character.equals(target)) { // It's someone else they're interacting with:
						canAccessOthersParts = false;
					}
				}
			}
			if(!target.isAbleToAccessCoverableArea(penetration.getRelatedCoverableArea(), true)) {
				canAccessOthersParts = false;
			}
		}

		return canAccessSelfParts && canAccessOthersParts;
	}

	public default boolean isAddedToAvailableSexActions() {
		return toResponse() != null;
	}

	public default boolean isSwitchOngoingActionAvailable() {
		if(Sex.getCharacterPerformingAction().isPlayer()
				&& Sex.getSexControl(Sex.getCharacterPerformingAction()).getValue()>=SexControl.ONGOING_PLUS_LIMITED_PENETRATIONS.getValue()) {
			List<GameCharacter> ongoingTargetedAreaCharacters = Sex.getCharactersHavingOngoingActionWith(Sex.getCharacterTargetedForSexAction(this), this.getTargetedCharacterAreas().get(0));
			List<GameCharacter> ongoingPerformingAreaCharacters = Sex.getCharactersHavingOngoingActionWith(Sex.getCharacterPerformingAction(), this.getPerformingCharacterAreas().get(0));

			// If targeted area is having multiple ongoing actions, or non-self actions that do not involve the player do not allow switch:
			if(ongoingTargetedAreaCharacters.size()>1 || ongoingPerformingAreaCharacters.size()>1) {
				return false;
			}
			if(!ongoingTargetedAreaCharacters.isEmpty()
					&& !ongoingTargetedAreaCharacters.contains(Sex.getCharacterTargetedForSexAction(this))
					&& !ongoingTargetedAreaCharacters.contains(Main.game.getPlayer())) {
				return false;
			}

			try {
				return !Sex.getOngoingActionsMap(Sex.getCharacterPerformingAction()).get(this.getPerformingCharacterAreas().get(0)).get(Sex.getCharacterTargetedForSexAction(this)).contains(this.getTargetedCharacterAreas().get(0));
			} catch(Exception ex) {
				return true;
			}
		} else {
			return false;
		}
	}

	public default Response toResponse() {
		if(isBasicCoreRequirementsMet()
				&& isBaseRequirementsMet()
				&& isPhysicallyPossible()
				&& !isBannedFromSexManager()
				&& !Sex.getPosition().isActionBlocked(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), this)) {

			if(this.getParticipantType()==SexParticipantType.SELF
					&& !this.getActionType().isOrgasmOption()
					&& Sex.getSexControl(Sex.getCharacterPerformingAction()).getValue()<SexControl.SELF.getValue()) {
				return null;
			}

			if(this.getActionType()==SexActionType.POSITIONING
					&& !this.isPositionSwap()
					&& !Sex.isPositionChangingAllowed(Sex.getCharacterPerformingAction())) {
				return null;
			}

			for(Entry<SexAreaInterface, SexAreaInterface> entry : getSexAreaInteractions().entrySet()) {
				if(isForbiddenArea(entry.getKey()) || isForbiddenArea(entry.getValue())) {
					return null;
				}
			}

			// Return null if the player doesn't know about the partners penis/vagina
			if(Sex.getCharacterPerformingAction().isPlayer()) {
				for(SexAreaOrifice sArea : this.getTargetedCharacterOrifices()) {
					switch(sArea){
						case NIPPLE:
							if(!Sex.getCharacterTargetedForSexAction(this).isAreaKnownByCharacter(CoverableArea.NIPPLES, Main.game.getPlayer())) {
								return null;
							}
							break;
						case URETHRA_PENIS:
							if(!Sex.getCharacterTargetedForSexAction(this).isAreaKnownByCharacter(CoverableArea.PENIS, Main.game.getPlayer())) {
								return null;
							}
							break;
						case URETHRA_VAGINA:
							if(!Sex.getCharacterTargetedForSexAction(this).isAreaKnownByCharacter(CoverableArea.VAGINA, Main.game.getPlayer())) {
								return null;
							}
							break;
						case VAGINA:
							if(!Sex.getCharacterTargetedForSexAction(this).isAreaKnownByCharacter(CoverableArea.VAGINA, Main.game.getPlayer())) {
								return null;
							}
							break;
						case NIPPLE_CROTCH:
							if(!Sex.getCharacterTargetedForSexAction(this).isAreaKnownByCharacter(CoverableArea.NIPPLES_CROTCH, Main.game.getPlayer())) {
								return null;
							}
							break;
						case BREAST_CROTCH:
						case ANUS:
						case ASS:
						case BREAST:
						case MOUTH:
						case THIGHS:
							break;
					}
				}
				for(SexAreaPenetration sArea : this.getTargetedCharacterPenetrations()) {
					switch(sArea){
						case CLIT:
							if(!Sex.getCharacterTargetedForSexAction(this).isAreaKnownByCharacter(CoverableArea.VAGINA, Main.game.getPlayer())) {
								return null;
							}
							break;
						case PENIS:
							if(!Sex.getCharacterTargetedForSexAction(this).isAreaKnownByCharacter(CoverableArea.PENIS, Main.game.getPlayer())) {
								return null;
							}
							break;
						case FINGER:
						case FOOT:
						case TAIL:
						case TENTACLE:
						case TONGUE:
							break;
					}
				}
			}

			// You can't prepare for orgasms if your partner won't orgasm on the next turn:
			if(Sex.getCharacterPerformingAction().isPlayer()
					&& getActionType() == SexActionType.PREPARE_FOR_PARTNER_ORGASM) {
				if(!Sex.isReadyToOrgasm(Sex.getTargetedPartner(Main.game.getPlayer()))) {
					return null;
				} else {
					return convertToResponse();
				}
			}
			// Your partner can't prepare for orgasms if you won't orgasm on the next turn:
			if(!Sex.getCharacterPerformingAction().isPlayer()
					&& getActionType() == SexActionType.PREPARE_FOR_PARTNER_ORGASM) {
				if(!Sex.isReadyToOrgasm(Main.game.getPlayer())) {
					return null;
				} else {
					return convertToResponse();
				}
			}

			// You can't resist in scenes that don't allow it or if non-con is disabled:
			if(getSexPace()==SexPace.SUB_RESISTING) {
				if((Sex.isConsensual() && !Sex.getCharacterPerformingAction().hasFetish(Fetish.FETISH_NON_CON_SUB)) || !Main.game.isNonConEnabled()) {
					return null;
				}
			}

			// If this is a positioning action:
			if(getActionType()==SexActionType.POSITIONING) {
//				// If there is size-difference and more than 1 participant, block non-switching with size-difference NPCS:
//				if(Sex.isSizeDifference() && Sex.getTotalParticipantCount(false)>2) {
//					if(Sex.getCharacterTargetedForSexAction(this).isSizeDifferenceShorterThan(Sex.getCharacterPerformingAction())
//							|| Sex.getCharacterTargetedForSexAction(this).isSizeDifferenceTallerThan(Sex.getCharacterPerformingAction())) {
//						return convertToNullResponse();
//					}
//				}
				return convertToResponse();

			// If this is a 'stop penetration' action, check to see if all the requirements are met:
			} else if(getActionType()==SexActionType.STOP_ONGOING) {
				if(!this.getSexAreaInteractions().isEmpty()) {
					// If there is no ongoing action involving the targeted character, return null:
					boolean ongoingFound = false;
					//TODO check
					outerloop:
					for(SexAreaInterface sArea : this.getSexAreaInteractions().keySet()) {
						for(SexAreaInterface sAreaTarget : this.getSexAreaInteractions().values()) {
							if(Sex.getContactingSexAreas(Sex.getCharacterPerformingAction(), sArea, Sex.getCharacterTargetedForSexAction(this)).contains(sAreaTarget)) {
								ongoingFound = true;
								break outerloop;
							}
						}
					}
					if(!ongoingFound) {
						return null;
					}

					// The sub stopping penetration actions (not including self-penetration actions) is only available if the sub has equal control:
					if(this.getParticipantType()!=SexParticipantType.SELF) {
						if(Sex.getSexControl(Sex.getCharacterPerformingAction())!=SexControl.FULL) {
							return null;
						}
					}

				} else {
					return null;
				}

				return convertToResponse();

			// If this is a 'start penetration' action, check to see if all the requirements are met:
			} else if(getActionType()==SexActionType.START_ONGOING || getActionType()==SexActionType.START_ADDITIONAL_ONGOING) {

				// Penetration actions (not including self-penetration actions) are only available in consensual sex or if the penetrator is the dom:
				if(!this.getSexAreaInteractions().isEmpty()) {
					if(this.getParticipantType() != SexParticipantType.SELF) { // This is a penetrative action between both partners:

						boolean canStartPenetration = Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
								|| Sex.isDom(Sex.getCharacterPerformingAction()) == Sex.isDom(Sex.getTargetedPartner(Sex.getCharacterPerformingAction()));

						if(!canStartPenetration
								&& Sex.getSexPace(Sex.getTargetedPartner(Sex.getCharacterPerformingAction()))!=SexPace.DOM_ROUGH
								&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.ONGOING_PLUS_LIMITED_PENETRATIONS) {
							if(this.getTargetedCharacterOrifices().isEmpty()) {
								canStartPenetration = true; // Can start submissive penetrations (getting penetrated, not doing the penetrating) when character is a sub with restricted control.
							} else {
								boolean virginityTakingPenetration = false;
								for(SexAreaPenetration pen :this.getPerformingCharacterPenetrations()) {
									if(pen.isTakesVirginity()) {
										virginityTakingPenetration = true;
									}
								}
								canStartPenetration = !virginityTakingPenetration;
							}
						}

						if(!canStartPenetration || getSexPace()==SexPace.SUB_RESISTING) {
							return null;
						}
					}
				}

				// If this ongoing action is already ongoing, don't show the 'start' action:
				if(getActionType()==SexActionType.START_ONGOING) {
					if(Sex.getOngoingCharactersUsingAreas(Sex.getCharacterPerformingAction(), this.getPerformingCharacterAreas().get(0), this.getTargetedCharacterAreas().get(0)).contains(Sex.getCharacterTargetedForSexAction(this))) {
						return null;
					}
				}

				// Make sure OrificeTypes are available:
				for(SexAreaOrifice sArea : this.getPerformingCharacterOrifices()) {
					// THis is already checked in isPhysicallyPossible:
//					switch(sArea){
//						case NIPPLE:
//							if(!Sex.getCharacterPerformingAction().isBreastFuckableNipplePenetration()) {
//								return null;
//							}
//							break;
//						case NIPPLE_CROTCH:
//							if(!Sex.getCharacterPerformingAction().isBreastCrotchFuckableNipplePenetration()) {
//								return null;
//							}
//							break;
//						default:
//							break;
//					}

					// Check for access:
					if(!Sex.getCharacterPerformingAction().isOrificeTypeExposed(sArea)) {
						return convertToNullResponse();
					}

					// Check to see if it's already in use:
					if(getActionType()==SexActionType.START_ONGOING) {
						if(!isSwitchOngoingActionAvailable() && !sArea.isFree(Sex.getCharacterPerformingAction())) {
							return convertToNullResponse();
						}

					} else { // Check to see if an additional ongoing action is allowed:
						if(!isAdditionalOngoingAvailable()) {
							return convertToNullResponse();
						}
					}
				}
				for(SexAreaOrifice sArea : this.getTargetedCharacterOrifices()) {
					// THis is already checked in isPhysicallyPossible:
//					switch(sArea){
//						case NIPPLE:
//							if(!Sex.getCharacterTargetedForSexAction(this).isBreastFuckableNipplePenetration()) {
//								return null;
//							}
//							break;
//						case NIPPLE_CROTCH:
//							if(!Sex.getCharacterTargetedForSexAction(this).isBreastCrotchFuckableNipplePenetration()) {
//								return null;
//							}
//							break;
//						default:
//							break;
//					}

					// Check for access:
					if(!Sex.getCharacterTargetedForSexAction(this).isOrificeTypeExposed(sArea)) {
						return convertToNullResponse();
					}

					// Check to see if it's already in use:
					if(getActionType()==SexActionType.START_ONGOING) {
						if(!isSwitchOngoingActionAvailable() && !sArea.isFree(Sex.getCharacterTargetedForSexAction(this))) {
							return convertToNullResponse();
						}

					} else { // Check to see if an additional ongoing action is allowed:
						if(!isAdditionalOngoingAvailable()) {
							return convertToNullResponse();
						}
					}
				}

				// Make sure PenetrationTypes is available:
				for(SexAreaPenetration sArea : this.getPerformingCharacterPenetrations()) {
					// Check for access:
					if(!Sex.getCharacterPerformingAction().isPenetrationTypeExposed(sArea)) {
						return convertToNullResponse();
					}

					// Check to see if it's already in use:
					if(getActionType()==SexActionType.START_ONGOING) {
						if(!isSwitchOngoingActionAvailable() && !sArea.isFree(Sex.getCharacterPerformingAction())) {
							return convertToNullResponse();
						}

					} else { // Check to see if an additional ongoing action is allowed:
						if(!isAdditionalOngoingAvailable()) {
							return convertToNullResponse();
						}
					}
				}
				for(SexAreaPenetration sArea : this.getTargetedCharacterPenetrations()) {
					// Check for access:
					if(!Sex.getCharacterTargetedForSexAction(this).isPenetrationTypeExposed(sArea)) {
						return convertToNullResponse();
					}

					// Check to see if it's already in use:
					if(getActionType()==SexActionType.START_ONGOING) {
						if(!isSwitchOngoingActionAvailable() && !sArea.isFree(Sex.getCharacterTargetedForSexAction(this))) {
							return convertToNullResponse();
						}

					} else { // Check to see if an additional ongoing action is allowed:
						if(!isAdditionalOngoingAvailable()) {
							return convertToNullResponse();
						}
					}
				}

				return convertToResponse();

			} else if(getActionType()==SexActionType.REQUIRES_NO_PENETRATION_AND_EXPOSED) {

				// Check penetrations:
				for(SexAreaPenetration sArea : this.getPerformingCharacterPenetrations()) {
					if(!Sex.getCharacterPerformingAction().isPenetrationTypeExposed(sArea)) {
						return convertToNullResponse();
					}
					if(!sArea.isFree(Sex.getCharacterPerformingAction())) {
						return convertToNullResponse();
					}
				}
				for(SexAreaPenetration sArea : this.getTargetedCharacterPenetrations()) {
					if(!Sex.getCharacterTargetedForSexAction(this).isPenetrationTypeExposed(sArea)) {
						return convertToNullResponse();
					}
					if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(this))) {
						return convertToNullResponse();
					}
				}

				// Check orifices:
				for(SexAreaOrifice sArea : this.getPerformingCharacterOrifices()) {
					if(!Sex.getCharacterPerformingAction().isOrificeTypeExposed(sArea)) {
						return convertToNullResponse();
					}
					if(!sArea.isFree(Sex.getCharacterPerformingAction())) {
						return convertToNullResponse();
					}
				}
				for(SexAreaOrifice sArea : this.getTargetedCharacterOrifices()) {
					if(!Sex.getCharacterTargetedForSexAction(this).isOrificeTypeExposed(sArea)) {
						return convertToNullResponse();
					}
					if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(this))) {
						return convertToNullResponse();
					}
				}

				return convertToResponse();

			} else if(getActionType()==SexActionType.REQUIRES_EXPOSED) {

				// Check penetrations:
				for(SexAreaPenetration sArea : this.getPerformingCharacterPenetrations()) {
					if(!Sex.getCharacterPerformingAction().isPenetrationTypeExposed(sArea)) {
						return convertToNullResponse();
					}
				}
				for(SexAreaPenetration sArea : this.getTargetedCharacterPenetrations()) {
					if(!Sex.getCharacterTargetedForSexAction(this).isPenetrationTypeExposed(sArea)) {
						return convertToNullResponse();
					}
				}

				// Check orifices:
				for(SexAreaOrifice sArea : this.getPerformingCharacterOrifices()) {
					if(!Sex.getCharacterPerformingAction().isOrificeTypeExposed(sArea)) {
						return convertToNullResponse();
					}
				}
				for(SexAreaOrifice sArea : this.getTargetedCharacterOrifices()) {
					if(!Sex.getCharacterTargetedForSexAction(this).isOrificeTypeExposed(sArea)) {
						return convertToNullResponse();
					}
				}

				return convertToResponse();

			} else if(getActionType()==SexActionType.REQUIRES_NO_PENETRATION) {

				// Check penetrations:
				for(SexAreaPenetration sArea : this.getPerformingCharacterPenetrations()) {
					if(!sArea.isFree(Sex.getCharacterPerformingAction()) || Sex.getCharacterPerformingAction().isCoverableAreaBlockedFromGroping(sArea.getRelatedCoverableArea())) {
						return convertToNullResponse();
					}
				}
				for(SexAreaPenetration sArea : this.getTargetedCharacterPenetrations()) {
					if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(this)) || Sex.getCharacterTargetedForSexAction(this).isCoverableAreaBlockedFromGroping(sArea.getRelatedCoverableArea())) {
						return convertToNullResponse();
					}
				}

				// Check orifices:
				for(SexAreaOrifice sArea : this.getPerformingCharacterOrifices()) {
					if(!sArea.isFree(Sex.getCharacterPerformingAction()) || Sex.getCharacterPerformingAction().isCoverableAreaBlockedFromGroping(sArea.getRelatedCoverableArea())) {
						return convertToNullResponse();
					}
				}
				for(SexAreaOrifice sArea : this.getTargetedCharacterOrifices()) {
					if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(this)) || Sex.getCharacterTargetedForSexAction(this).isCoverableAreaBlockedFromGroping(sArea.getRelatedCoverableArea())) {
						return convertToNullResponse();
					}
				}

				return convertToResponse();

			} else if(getActionType()==SexActionType.SPEECH) {
				// Check penetrations:
				if(Sex.isOngoingActionsBlockingSpeech(Sex.getCharacterPerformingAction())) {
					return convertToNullResponse();
				}

				return convertToResponse();

			} else {
				if(!this.getSexAreaInteractions().isEmpty()) {
					boolean ongoingFound = false;
					// TODO check
					for(SexAreaInterface sArea : this.getSexAreaInteractions().keySet()) {
						for(SexAreaInterface sAreaTarget : this.getSexAreaInteractions().values()) {
							if(Sex.getContactingSexAreas(Sex.getCharacterPerformingAction(), sArea, Sex.getCharacterTargetedForSexAction(this)).contains(sAreaTarget)) {
								ongoingFound = true;
								return convertToResponse();
							}
						}
					}
					if(!ongoingFound) {
						return null;
					}
				}
				return convertToResponse();
			}

		} else {
			return null;
		}
	}

	default boolean isForbiddenArea(SexAreaInterface sArea) {
		if(sArea!=null && sArea.isOrifice()) {
			switch((SexAreaOrifice) sArea){
				case NIPPLE:
					if(!Main.getProperties().hasValue(PropertyValue.nipplePenContent) && this.getActionType()==SexActionType.START_ONGOING) {
						return true;
					}
					break;
				case NIPPLE_CROTCH:
					if(!Main.getProperties().hasValue(PropertyValue.nipplePenContent) && this.getActionType()==SexActionType.START_ONGOING) {
						return true;
					}
					break;
				case URETHRA_PENIS:
					if(!Main.getProperties().hasValue(PropertyValue.urethralContent) && this.getActionType()==SexActionType.START_ONGOING) {
						return true;
					}
					break;
				case URETHRA_VAGINA:
					if(!Main.getProperties().hasValue(PropertyValue.urethralContent) && this.getActionType()==SexActionType.START_ONGOING) {
						return true;
					}
					break;
				default:
					break;
			}
		}
		return false;
	}

	public default SexActionCategory getCategory() {
		if(this.getSexAreaInteractions().isEmpty()) {
			if(getActionType() == SexActionType.POSITIONING
					|| getActionType() == SexActionType.POSITIONING_MENU) {
				return SexActionCategory.POSITIONING;
			} else {
				return SexActionCategory.MISCELLANEOUS;
			}

		} else {
			if(this.getParticipantType()==SexParticipantType.SELF) {
				return SexActionCategory.SELF;
			} else {
				return SexActionCategory.SEX;
			}
		}
	}

	default String getArousalHitWarning() {
		if(this.isSadisticAction()) {
			if(!Sex.getCharacterTargetedForSexAction(this).getFetishDesire(Fetish.FETISH_MASOCHIST).isPositive()) {
				return "
[style.colourBad(As [npc2.name] doesn't have a positive desire towards the '"+Fetish.FETISH_MASOCHIST.getName(Sex.getCharacterTargetedForSexAction(this))+"' fetish, [npc2.she] will find this action to be a huge turn-off!)]";
			} else {
				return "
[style.colourSex(As [npc2.name] has a positive desire towards the '"+Fetish.FETISH_MASOCHIST.getName(Sex.getCharacterTargetedForSexAction(this))+"' fetish, [npc2.she] finds such sadistic actions to be a turn-on!)]";
			}
		}
		return "";
	}

	public default Response convertToResponse() {
		if(getCategory()!=SexActionCategory.CHARACTER_SWITCH && getActionType()!=SexActionType.POSITIONING_MENU) {

//			if(getActionDescription()==null) {
//				System.out.println(this.getClass().getName());
//			}

			return new Response(
					this.endsSex()
						?getActionTitle()
						:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionTitle()),
					this.endsSex()
						?getActionDescription()+getArousalHitWarning()
						:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionDescription()+getArousalHitWarning()),
					Sex.SEX_DIALOGUE,
					getFetishes(Main.game.getPlayer()),
					getCorruptionNeeded(),
					null, null, null,
					this.getActionType(),
					Sex.getCharacterPerformingAction(),
					this.getSexAreaInteractions().keySet(),
					Sex.getCharacterTargetedForSexAction(this),
					this.getSexAreaInteractions().values()){

				@Override
				public void effects() {
					if(SexActionInterface.this.getSexPace()!=null) {
						Sex.setSexPace(Sex.getCharacterPerformingAction(), (SexActionInterface.this.getSexPace()));
					}
					Sex.setSexStarted(true);
					Sex.endSexTurn(SexActionInterface.this);
				}
				@Override
				public boolean isSexPenetrationHighlight() {
					return getActionType()==SexActionType.START_ONGOING || getActionType()==SexActionType.START_ADDITIONAL_ONGOING || getActionType()==SexActionType.STOP_ONGOING;
				}
				@Override
				public boolean isSexPositioningHighlight() {
					return !SexActionInterface.this.isPositionSwap() && (SexActionInterface.this.getActionType()==SexActionType.POSITIONING || SexActionInterface.this.equals(GenericActions.PLAYER_STOP_SEX));
				}
				@Override
				public Colour getHighlightColour() {
					if(SexActionInterface.this.getActionType()==SexActionType.POSITIONING_MENU) {
						return Colour.BASE_LILAC;
					}
					if(SexActionInterface.this.isPositionSwap() || getCategory()==SexActionCategory.CHARACTER_SWITCH) {
						return Colour.BASE_PURPLE_LIGHT;
					}
					if(isSadisticAction()) {
						return Colour.BASE_CRIMSON;
					}
					if(isSexPenetrationHighlight()) {
						if(SexActionInterface.this.getPerformingCharacterAreas().stream().anyMatch((area) -> area.isPenetration())) {
							return Colour.GENERIC_SEX_AS_DOM;
						} else {
							return Colour.GENERIC_SEX;
						}
					}
					return super.getHighlightColour();
				}
				@Override
				public SexPace getSexPace() {
					return SexActionInterface.this.getSexPace();
				}
				@Override
				public SexActionType getSexActionType() {
					return getActionType();
				}
				@Override
				public boolean isSexActionSwitch() {
					if(getActionType()==SexActionType.START_ONGOING) {
						for(SexAreaInterface sArea : SexActionInterface.this.getPerformingCharacterAreas()) {
							if(!sArea.isFree(Sex.getCharacterPerformingAction())) {
								return isSwitchOngoingActionAvailable();
							}
						}
						for(SexAreaInterface sArea : SexActionInterface.this.getTargetedCharacterAreas()) {
							if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(SexActionInterface.this))) {
								return isSwitchOngoingActionAvailable();
							}
						}
					}
					return false;
				}
				@Override
				public Map<String, Boolean> getAdditionalOngoingAvailableMap() {
					return SexActionInterface.this.getAdditionalOngoingAvailableMap();
				}
				@Override
				public boolean isAbleToBypass() {
					if(!Main.game.isBypassSexActions()) {
						return false;
					}
					return super.isAbleToBypass();
				}
			};

		} else {
			return new ResponseEffectsOnly(
					this.endsSex()
						?getActionTitle()
						:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionTitle()),
					this.endsSex()
						?getActionDescription()+getArousalHitWarning()
						:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionDescription()+getArousalHitWarning())){
				@Override
				public void effects() {
					SexActionInterface.this.applyEffects();
					MainController.updateUI();
					Main.game.updateResponses();
				}
				@Override
				public boolean isSexPositioningHighlight() {
					return !SexActionInterface.this.isPositionSwap() && SexActionInterface.this.getActionType()==SexActionType.POSITIONING;
				}
				@Override
				public Colour getHighlightColour() {
					if(SexActionInterface.this.getActionType()==SexActionType.POSITIONING_MENU) {
						return Colour.BASE_LILAC;
					}
					if(SexActionInterface.this.isPositionSwap() || getCategory()==SexActionCategory.CHARACTER_SWITCH) {
						return Colour.BASE_PURPLE_LIGHT;
					}
					if(isSadisticAction()) {
						return Colour.BASE_CRIMSON;
					}
					return super.getHighlightColour();
				}
				@Override
				public SexPace getSexPace() {
					return SexActionInterface.this.getSexPace();
				}
				@Override
				public SexActionType getSexActionType() {
					return getActionType();
				}
				@Override
				public boolean isSexActionSwitch() {
					if(getActionType()==SexActionType.START_ONGOING) {
						for(SexAreaInterface sArea : SexActionInterface.this.getPerformingCharacterAreas()) {
							if(!sArea.isFree(Sex.getCharacterPerformingAction())) {
								return isSwitchOngoingActionAvailable();
							}
						}
						for(SexAreaInterface sArea : SexActionInterface.this.getTargetedCharacterAreas()) {
							if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(SexActionInterface.this))) {
								return isSwitchOngoingActionAvailable();
							}
						}
					}
					return false;
				}
				@Override
				public Map<String, Boolean> getAdditionalOngoingAvailableMap() {
					return SexActionInterface.this.getAdditionalOngoingAvailableMap();
				}
			};
		}
	}

	public default Response convertToNullResponse() {
		if(!Sex.getCharacterPerformingAction().isPlayer()) {
			return null;
		}

		if(getActionType()==SexActionType.POSITIONING) {
			return new Response(
					this.endsSex()
						?getActionTitle()
						:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionTitle()),
					this.endsSex()
						?getActionDescription()+getArousalHitWarning()
						:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionDescription()+getArousalHitWarning()),
					null,
					getFetishes(Main.game.getPlayer()),
					getCorruptionNeeded(),
					null, null, null,
					this.getActionType(),
					Sex.getCharacterPerformingAction(),
					this.getSexAreaInteractions().keySet(),
					Sex.getCharacterTargetedForSexAction(this),
					this.getSexAreaInteractions().values()){

				@Override
				public boolean isSexPenetrationHighlight() {
					return getActionType()==SexActionType.START_ONGOING || getActionType()==SexActionType.START_ADDITIONAL_ONGOING || getActionType()==SexActionType.STOP_ONGOING;
				}
				@Override
				public boolean isSexPositioningHighlight() {
					return !SexActionInterface.this.isPositionSwap() && (getActionType()==SexActionType.POSITIONING || SexActionInterface.this.equals(GenericActions.PLAYER_STOP_SEX));
				}
				@Override
				public SexPace getSexPace() {
					return SexActionInterface.this.getSexPace();
				}
				@Override
				public SexActionType getSexActionType() {
					return getActionType();
				}
				@Override
				public boolean isSexActionSwitch() {
					if(getActionType()==SexActionType.START_ONGOING) {
						for(SexAreaInterface sArea : SexActionInterface.this.getPerformingCharacterAreas()) {
							if(!sArea.isFree(Sex.getCharacterPerformingAction())) {
								return isSwitchOngoingActionAvailable();
							}
						}
						for(SexAreaInterface sArea : SexActionInterface.this.getTargetedCharacterAreas()) {
							if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(SexActionInterface.this))) {
								return isSwitchOngoingActionAvailable();
							}
						}
					}
					return false;
				}
				@Override
				public Map<String, Boolean> getAdditionalOngoingAvailableMap() {
					return SexActionInterface.this.getAdditionalOngoingAvailableMap();
				}
				@Override
				public boolean hasRequirements() {
					return true;
				}
				@Override
				public boolean isAvailable(){
					return false;
				}
				@Override
				public boolean isAbleToBypass(){
					return false;
				}
				@Override
				public String getTooltipRequiredList(){
					StringBuilder SB = new StringBuilder();

					if(fetishesRequired!=null) {
						for(Fetish f : fetishesRequired){
							if(Main.game.getPlayer().hasFetish(f)) {
								SB.append("
"
										+"Associated Fetish"
										+ " (owned): "
										+ Util.capitaliseSentence(f.getName(Main.game.getPlayer())));

							} else {
								SB.append("
"
										+"Associated Fetish"
										+ " (not owned): "
										+ Util.capitaliseSentence(f.getName(Main.game.getPlayer())));
							}
						}
					}

					if(corruptionBypass!=null) {
						if(isCorruptionWithinRange()) {
							SB.append("
"
									+"Associated Corruption"
									+ " (within range): "
									+ Util.capitaliseSentence(corruptionBypass.getName()));
						} else {
							SB.append("
"
									+"Associated Corruption"
									+ " (out of range): "
									+ Util.capitaliseSentence(corruptionBypass.getName()));
						}
					}

					if(Sex.isSizeDifference() && Sex.getTotalParticipantCount(false)>2) {
						if(Sex.getCharacterTargetedForSexAction(SexActionInterface.this).isSizeDifferenceShorterThan(Sex.getCharacterPerformingAction())
								|| Sex.getCharacterTargetedForSexAction(SexActionInterface.this).isSizeDifferenceTallerThan(Sex.getCharacterPerformingAction())) {
							SB.append("
"
									+"Size-difference is blocking swap!");
						}
					}

//					SB.append("
"
//							+"Requires no penetration");

					return SB.toString();
				}
				@Override
				public int lineHeight(){
					return super.lineHeight()+1;
				}
			};
		}

		return new Response(
				this.endsSex()
					?getActionTitle()
					:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionTitle()),
				this.endsSex()
					?getActionDescription()+getArousalHitWarning()
					:UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this), getActionDescription()+getArousalHitWarning()),
				null,
				getFetishes(Main.game.getPlayer()),
				getCorruptionNeeded(),
				null, null, null,
				this.getActionType(),
				Sex.getCharacterPerformingAction(),
				this.getSexAreaInteractions().keySet(),
				Sex.getCharacterTargetedForSexAction(this),
				this.getSexAreaInteractions().values()){

			@Override
			public boolean isSexPenetrationHighlight() {
				return getActionType()==SexActionType.START_ONGOING || getActionType()==SexActionType.START_ADDITIONAL_ONGOING || getActionType()==SexActionType.STOP_ONGOING;
			}
			@Override
			public boolean isSexPositioningHighlight() {
				return !SexActionInterface.this.isPositionSwap() && (getActionType()==SexActionType.POSITIONING || SexActionInterface.this.equals(GenericActions.PLAYER_STOP_SEX));
			}
			@Override
			public SexPace getSexPace() {
				return SexActionInterface.this.getSexPace();
			}
			@Override
			public SexActionType getSexActionType() {
				return getActionType();
			}
			@Override
			public boolean isSexActionSwitch() {
				if(getActionType()==SexActionType.START_ONGOING) {
					for(SexAreaInterface sArea : SexActionInterface.this.getPerformingCharacterAreas()) {
						if(!sArea.isFree(Sex.getCharacterPerformingAction())) {
							return isSwitchOngoingActionAvailable();
						}
					}
					for(SexAreaInterface sArea : SexActionInterface.this.getTargetedCharacterAreas()) {
						if(!sArea.isFree(Sex.getCharacterTargetedForSexAction(SexActionInterface.this))) {
							return isSwitchOngoingActionAvailable();
						}
					}
				}
				return false;
			}
			@Override
			public Map<String, Boolean> getAdditionalOngoingAvailableMap() {
				return SexActionInterface.this.getAdditionalOngoingAvailableMap();
			}
		};
	}

	public default boolean isBannedFromSexManager() {
		for(SexAreaInterface sArea : this.getSexAreaInteractions().keySet()) {
			if (Sex.getSexManager().getAreasBannedMap().get(Sex.getCharacterPerformingAction()) != null
					&& Sex.getSexManager().getAreasBannedMap().get(Sex.getCharacterPerformingAction()).contains(sArea)) {
				if(this.getParticipantType()==SexParticipantType.NORMAL || Sex.getSexManager().isAreasBannedMapAppliedToSelfActions(Sex.getCharacterPerformingAction())) {
					return true;
				}
			}
		}
		for(SexAreaInterface sArea : this.getSexAreaInteractions().values()) {
			if (Sex.getSexManager().getAreasBannedMap().get(Sex.getCharacterTargetedForSexAction(this)) != null
					&& Sex.getSexManager().getAreasBannedMap().get(Sex.getCharacterTargetedForSexAction(this)).contains(sArea)) {
				if(this.getParticipantType()==SexParticipantType.NORMAL || Sex.getSexManager().isAreasBannedMapAppliedToSelfActions(Sex.getCharacterTargetedForSexAction(this))) {
					return true;
				}
			}
		}

		return false;
	}

	static List<SexAreaInterface> prohibitedNonBipedFingerSelfAreas = Util.newArrayListOfValues(
			SexAreaPenetration.CLIT,
			SexAreaPenetration.PENIS,
			SexAreaOrifice.ANUS,
			SexAreaOrifice.ASS,
			SexAreaOrifice.URETHRA_PENIS,
			SexAreaOrifice.URETHRA_VAGINA,
			SexAreaOrifice.VAGINA);

	public default boolean isPhysicallyPossible() {
		if(this.getParticipantType()==SexParticipantType.SELF) {
			if(Sex.getCharacterPerformingAction().isTaur()) {
				if(this.getSexAreaInteractions().keySet().contains(SexAreaPenetration.FINGER)) {
					if(!Collections.disjoint(prohibitedNonBipedFingerSelfAreas, this.getSexAreaInteractions().values())) {
						return false;
					}
				}
				if(this.getSexAreaInteractions().keySet().contains(SexAreaPenetration.TAIL)
						|| this.getSexAreaInteractions().keySet().contains(SexAreaPenetration.CLIT)
						|| this.getSexAreaInteractions().keySet().contains(SexAreaPenetration.PENIS)) {
					if(!Collections.disjoint(SexActionPresets.upperHalf, this.getSexAreaInteractions().values())) {
						return false;
					}
				}
			}
			if(!Sex.getCharacterPerformingAction().getLegConfiguration().isBipedalPositionedCrotchBoobs()) {
				if(this.getSexAreaInteractions().keySet().contains(SexAreaPenetration.FINGER)) {
					if(this.getSexAreaInteractions().values().contains(SexAreaOrifice.BREAST_CROTCH)
							|| this.getSexAreaInteractions().values().contains(SexAreaOrifice.NIPPLE_CROTCH)) {
						return false;
					}
				}
			}
		}

		for(SexAreaInterface targetedArea : this.getSexAreaInteractions().keySet()) {
			for(SexAreaInterface interactingWithArea : this.getSexAreaInteractions().values()) {
				if(!performPhysicallyBlockedCheck(targetedArea, Sex.getCharacterPerformingAction(), interactingWithArea)) {
					return false;
				}
			}
		}
		for(SexAreaInterface performingArea : this.getSexAreaInteractions().values()) {
			for(SexAreaInterface interactingWithArea : this.getSexAreaInteractions().keySet()) {
				if(!performPhysicallyBlockedCheck(performingArea, Sex.getCharacterTargetedForSexAction(this), interactingWithArea)) {
					return false;
				}
			}
		}

		return true;
	}

	default boolean performPhysicallyBlockedCheck(SexAreaInterface performingArea, GameCharacter performingCharacter, SexAreaInterface interactingWithArea) {
		// Things that make *any* actions related to the penetration ***physically impossible***:
		if(performingArea != null && performingArea.isPenetration()) {
			switch((SexAreaPenetration) performingArea) {
				case FINGER:
					break;
				case PENIS:
					if(!performingCharacter.hasPenis())
						return false;
					break;
				case TAIL:
					if(!performingCharacter.getTailType().isSuitableForPenetration()) {
						return false;
					}
					break;
				case TENTACLE:
					break;
				case TONGUE:
					break;
				case CLIT:
					if(!performingCharacter.hasVagina()) {
						return false;
					}
					break;
				case FOOT:
					break;
			}
		}
		// Things that make *any* actions related to the orifice ***physically impossible***:
		if(performingArea != null && performingArea.isOrifice()) {
			switch((SexAreaOrifice) performingArea){
				case ANUS:
				case ASS:
				case MOUTH:
					break;
				case NIPPLE:
					if(interactingWithArea!=SexAreaPenetration.TONGUE && this.getActionType()==SexActionType.START_ONGOING && !performingCharacter.isBreastFuckableNipplePenetration()) {
						return false;
					}
					break;
				case BREAST:
					break;
				case NIPPLE_CROTCH:
					if(!performingCharacter.hasBreastsCrotch() || (this.getActionType()==SexActionType.START_ONGOING && !performingCharacter.isBreastCrotchFuckableNipplePenetration())) {
						return false;
					}
					break;
				case BREAST_CROTCH:
					if(!performingCharacter.hasBreastsCrotch()) {
						return false;
					}
					break;
				case URETHRA_PENIS:
					if(!performingCharacter.hasPenis() || !performingCharacter.isUrethraFuckable()) {
						return false;
					}
					break;
				case VAGINA:
					if(!performingCharacter.hasVagina()) {
						return false;
					}
					break;
				case URETHRA_VAGINA:
					if(!performingCharacter.hasVagina() || !performingCharacter.isVaginaUrethraFuckable()) {
						return false;
					}
					break;
				case THIGHS: //TODO mermaid/centaur legs
					break;
			}
		}
		return true;
	}

	public default List<SexAreaInterface> getAreasCummedIn(GameCharacter cumProvider, GameCharacter cumTarget) { return null; }

	public default List<CoverableArea> getAreasCummedOn(GameCharacter cumProvider, GameCharacter cumTarget) { return null; }

	// This is in the SexAction Interface, as it might be necessary in some special actions to override this to prevent condom breaks.
	public default CondomFailure getCondomFailure(GameCharacter condomWearer, GameCharacter cumTarget) {
		AbstractClothing condom = condomWearer.getClothingInSlot(InventorySlot.PENIS);
		if(condom==null || !condom.isCondom(InventorySlot.PENIS)) {
			return CondomFailure.NONE;
		}

		int cumQuantity = condomWearer.getPenisRawOrgasmCumQuantity();

		for(ItemEffect effect : condom.getEffects()) {
			if(effect.getPrimaryModifier()==TFModifier.CLOTHING_CONDOM) {
				switch(effect.getPotency()) {
					case MINOR_BOOST:
						if(cumQuantity>CumProduction.FIVE_HUGE.getMaximumValue()) {
							return CondomFailure.CUM_OVERLOAD;
						}
						break;
					case BOOST:
						if(cumQuantity>CumProduction.SIX_EXTREME.getMaximumValue()) {
							return CondomFailure.CUM_OVERLOAD;
						}
						break;
					case MAJOR_BOOST:
						break;
					case MAJOR_DRAIN:
					case DRAIN:
					case MINOR_DRAIN:
						return CondomFailure.SABOTAGED;
				}
			}
		}

		if(cumQuantity>0 && condomWearer.getCumModifiers().contains(FluidModifier.MINERAL_OIL)) {
			return CondomFailure.MINERAL_OIL_SELF_CUM;
		}

		//TODO saliva

		if(Sex.getOrificesBeingPenetratedBy(condomWearer, SexAreaPenetration.PENIS, cumTarget).contains(SexAreaOrifice.URETHRA_PENIS)
				&& Sex.getWetAreas(cumTarget).get(SexAreaOrifice.URETHRA_PENIS).get(cumTarget).contains(LubricationType.CUM)
				&& cumTarget.getCumModifiers().contains(FluidModifier.MINERAL_OIL)) {
			return CondomFailure.MINERAL_OIL_CUM;
		}

		if(Sex.getOrificesBeingPenetratedBy(condomWearer, SexAreaPenetration.PENIS, cumTarget).contains(SexAreaOrifice.VAGINA)
				&& cumTarget.getGirlcum().getFluidModifiers().contains(FluidModifier.MINERAL_OIL)) {
			return CondomFailure.MINERAL_OIL_GIRLCUM;
		}

		if(Sex.getOrificesBeingPenetratedBy(condomWearer, SexAreaPenetration.PENIS, cumTarget).contains(SexAreaOrifice.NIPPLE)
				&& cumTarget.getBreastRawStoredMilkValue()>0
				&& cumTarget.getMilk().getFluidModifiers().contains(FluidModifier.MINERAL_OIL)) {
			return CondomFailure.MINERAL_OIL_MILK;
		}

		if(Sex.getOrificesBeingPenetratedBy(condomWearer, SexAreaPenetration.PENIS, cumTarget).contains(SexAreaOrifice.NIPPLE_CROTCH)
				&& cumTarget.getBreastCrotchRawStoredMilkValue()>0
				&& cumTarget.getMilkCrotch().getFluidModifiers().contains(FluidModifier.MINERAL_OIL)) {
			return CondomFailure.MINERAL_OIL_MILK;
		}

		return CondomFailure.NONE;
	}

	public default List<Fetish> getFetishesFromPenetrationAndOrificeTypes(
			GameCharacter characterPerformingAction,
			SexAreaInterface performingArea,
			GameCharacter characterTarget,
			SexAreaInterface targetedArea,
			boolean characterPerformingActionFetishes) {

		SexType type = new SexType(this.getParticipantType(), performingArea, targetedArea);

		// Self areas:
		List<Fetish> associatedFetishes = new ArrayList<>(type.getRelatedFetishes(characterPerformingAction, characterTarget, this.getActionType().isPenetratingOption(), this.getActionType()==SexActionType.ORGASM));

		// Add opposite fetishes for partner:
		List<Fetish> associatedFetishesPartner = new ArrayList<>(type.getOppositeFetishes(characterPerformingAction, characterTarget, this.getActionType().isPenetratingOption(), this.getActionType()==SexActionType.ORGASM));

		if(characterPerformingActionFetishes) {
			return associatedFetishes;
		} else {
			return associatedFetishesPartner;
		}
	}

	/**
	 * @return A SexType object which is representative of this SexAction. Will only represent the first area found in both performing and targeted areas, due to SexType limitations.
	 */
	public default SexType getAsSexType() {
		return new SexType(this.getParticipantType(), getPerformingCharacterAreas().isEmpty()?null:getPerformingCharacterAreas().get(0), getTargetedCharacterAreas().isEmpty()?null:getTargetedCharacterAreas().get(0));
	}
}


code:
package com.lilithsthrone.game.sex.sexActions;

/**
 * @since 0.2.7
 * @version 0.2.7
 * @author Innoxia
 */
public enum SexActionLimitation {
	PLAYER_ONLY,
	NPC_ONLY;
}

repo_name: klorpa/Pedo

license: unknown


code:
package com.lilithsthrone.game.sex.sexActions;

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

	private boolean endsSex;
	private String description;

	public SexActionOrgasmOverride(boolean endsSex, String description) {
		super();
		this.endsSex = endsSex;
		this.description = description;
	}

	public abstract void applyEffects();

	public void applyEndEffects() {
	}

	public boolean isEndsSex() {
		return endsSex;
	}

	public String getDescription() {
		return description;
	}
}


code:
package com.lilithsthrone.game.sex.sexActions;

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

import com.lilithsthrone.game.sex.SexAreaInterface;
import com.lilithsthrone.game.sex.SexAreaOrifice;
import com.lilithsthrone.game.sex.SexAreaPenetration;
import com.lilithsthrone.game.sex.sexActions.baseActions.ClitClit;
import com.lilithsthrone.game.sex.sexActions.baseActions.ClitMouth;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerAnus;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerBreasts;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerBreastsCrotch;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerClit;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerMouth;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerNipple;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerPenis;
import com.lilithsthrone.game.sex.sexActions.baseActions.FingerVagina;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisAnus;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisAss;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisBreasts;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisBreastsCrotch;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisFeet;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisFoot;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisMouth;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisNipple;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisNippleCrotch;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisThighs;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisUrethraPenis;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisUrethraVagina;
import com.lilithsthrone.game.sex.sexActions.baseActions.PenisVagina;
import com.lilithsthrone.game.sex.sexActions.baseActions.TailAnus;
import com.lilithsthrone.game.sex.sexActions.baseActions.TailVagina;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueAnus;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueBreasts;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueBreastsCrotch;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueMound;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueMouth;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueNipple;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueNippleCrotch;
import com.lilithsthrone.game.sex.sexActions.baseActions.TongueVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.GenericActions;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.GenericOrgasms;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.GenericPositioning;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.GenericTalk;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.PartnerTalk;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.PlayerTalk;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.PositioningMenu;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.SadisticActions;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfFingerAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfFingerBreasts;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfFingerMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfFingerNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfFingerVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfNoPen;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfPenisAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfPenisMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfPenisNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfPenisVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTailAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTailMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTailNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTailVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTongueAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTongueMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTongueNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPartner.PartnerSelfTongueVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfFingerAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfFingerBreasts;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfFingerMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfFingerNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfFingerVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfNoPen;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfPenisAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfPenisMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfPenisNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfPenisVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTailAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTailMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTailNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTailVagina;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTongueAnus;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTongueMouth;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTongueNipple;
import com.lilithsthrone.game.sex.sexActions.baseActionsSelfPlayer.PlayerSelfTongueVagina;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;

/**
 * @since 0.1.97
 * @version 0.3.3
 * @author Innoxia
 */
public class SexActionPresets {

	public static List<SexAreaInterface> groinAreas = Util.newArrayListOfValues(
			SexAreaPenetration.PENIS,
			SexAreaPenetration.CLIT,
			SexAreaOrifice.THIGHS,
			SexAreaOrifice.URETHRA_PENIS,
			SexAreaOrifice.URETHRA_VAGINA,
			SexAreaOrifice.VAGINA,
			SexAreaOrifice.NIPPLE_CROTCH,
			SexAreaOrifice.BREAST_CROTCH);

	public static List<SexAreaInterface> crotchBoobAreas = Util.newArrayListOfValues(
			SexAreaOrifice.NIPPLE_CROTCH,
			SexAreaOrifice.BREAST_CROTCH);

	public static List<SexAreaInterface> thighAreas = Util.newArrayListOfValues(
			SexAreaOrifice.THIGHS);

	public static List<SexAreaInterface> feetAreas = Util.newArrayListOfValues(
			SexAreaPenetration.FOOT);

	public static List<SexAreaInterface> vaginaAreas = Util.newArrayListOfValues(
			SexAreaPenetration.CLIT,
			SexAreaOrifice.URETHRA_VAGINA,
			SexAreaOrifice.VAGINA);

	public static List<SexAreaInterface> penisAreas = Util.newArrayListOfValues(
			SexAreaPenetration.PENIS,
			SexAreaOrifice.URETHRA_PENIS);

	public static List<SexAreaInterface> assAreas = Util.newArrayListOfValues(
			SexAreaOrifice.ANUS,
			SexAreaOrifice.ASS);

	public static List<SexAreaInterface> mouthAreas = Util.newArrayListOfValues(
			SexAreaPenetration.TONGUE,
			SexAreaOrifice.MOUTH);

	public static List<SexAreaInterface> breastAreas = Util.newArrayListOfValues(
			SexAreaOrifice.NIPPLE,
			SexAreaOrifice.BREAST);

	public static List<SexAreaInterface> appendageAreas = Util.newArrayListOfValues(
			SexAreaPenetration.FINGER,
			SexAreaPenetration.TAIL,
			SexAreaPenetration.TENTACLE);

	public static List<SexAreaInterface> handAreas = Util.newArrayListOfValues(
			SexAreaPenetration.FINGER);

	public static List<SexAreaInterface> tailAndTentacle = Util.newArrayListOfValues(
			SexAreaPenetration.TAIL,
			SexAreaPenetration.TENTACLE);

	public static List<SexAreaInterface> lowerHalf = Util.newArrayListOfValues( // Populated in static block
			SexAreaPenetration.TAIL,
			SexAreaPenetration.TENTACLE);

	public static List<SexAreaInterface> upperHalf = Util.newArrayListOfValues(
			SexAreaPenetration.FINGER,
			SexAreaOrifice.NIPPLE,
			SexAreaOrifice.BREAST,
			SexAreaPenetration.TONGUE,
			SexAreaOrifice.MOUTH);

	public static List<SexAreaInterface> allowedInterPenetrationAreas = Util.newArrayListOfValues(
			SexAreaPenetration.FOOT,
			SexAreaPenetration.FINGER,
			SexAreaPenetration.TAIL,
			SexAreaPenetration.TENTACLE,
			SexAreaOrifice.MOUTH,
			SexAreaOrifice.VAGINA);

	private static List<SexAreaInterface> allAreas;

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> appendagesToAllAreas = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> appendagesToLowerHalf = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> appendagesToAss = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> appendagesToUpperHalf = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> appendagesToBreasts = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> appendagesToCrotchBoobs = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> allAreasToAppendages = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> allAreasToTailAndTentacle = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> allAreasToLowerHalf = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> lowerHalfToAppendages = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> lowerHalfToFinger = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> upperHalfToFinger = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> upperHalfToAppendages = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> crotchBoobsToAppendages = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToAppendages = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToTailAndTentacle = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> tailToAllAreas;
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> tentacleToAllAreas;

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> fingerToUpperTorso;
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> fingerToLowerHalf;

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> tailToUpperTorso;
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> tailToLowerHalf;

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> tentacleToUpperTorso;
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> tentacleToLowerHalf;

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> appendagesToGroin = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> groinToGroin = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> groinToVagina = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> groinToPenis = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> groinToAss = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> groinToMouth = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> groinToFeet = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> groinToCrotchBoobs = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> penisToVagina = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> penisToBreasts = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> penisToAss = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> penisToMouth = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> penisToThighs = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> penisToFeet = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> fingerToMouth = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> fingerToPenis = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> fingerToVagina = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> vaginaToVagina = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> vaginaToPenis = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> assToPenis = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> anusToPenis = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> penisToPenis = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> vaginaToMouth = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> assToMouth = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> assToGroin = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> kissing = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> breastsToPenis = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToBreasts = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToLowerHalf = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToAss = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToVagina = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToPenis = new HashMap<>();
	public static HashMap<SexAreaInterface, List<SexAreaInterface>> mouthToCrotchBoobs = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> breastsToMouth = new HashMap<>();

	public static HashMap<SexAreaInterface, List<SexAreaInterface>> handHolding = new HashMap<>();

	static {

		allAreas = Util.mergeLists(appendageAreas, groinAreas, assAreas, mouthAreas, breastAreas);

		tailToAllAreas = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.TAIL, allAreas));
		tentacleToAllAreas = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.TENTACLE, allAreas));

		fingerToUpperTorso = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, Util.mergeLists(mouthAreas, breastAreas)));
		fingerToLowerHalf = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, Util.mergeLists(groinAreas, lowerHalf, assAreas)));

		tailToUpperTorso = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.TAIL, Util.mergeLists(mouthAreas, breastAreas)));
		tailToLowerHalf = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.TAIL, Util.mergeLists(groinAreas, lowerHalf, assAreas)));

		tentacleToUpperTorso = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.TENTACLE, Util.mergeLists(mouthAreas, breastAreas)));
		tentacleToLowerHalf = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.TENTACLE, Util.mergeLists(groinAreas, lowerHalf, assAreas)));

		handHolding = Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, Util.mergeLists(mouthAreas, Util.newArrayListOfValues(SexAreaPenetration.FINGER))));

		lowerHalf.addAll(groinAreas);
		lowerHalf.addAll(thighAreas);
		lowerHalf.addAll(feetAreas);
		lowerHalf.addAll(vaginaAreas);
		lowerHalf.addAll(penisAreas);
		lowerHalf.addAll(assAreas);

		fingerToMouth.put(SexAreaPenetration.FINGER, mouthAreas);
		fingerToPenis.put(SexAreaPenetration.FINGER, penisAreas);
		fingerToVagina.put(SexAreaPenetration.FINGER, vaginaAreas);
		anusToPenis.put(SexAreaOrifice.ANUS, penisAreas);

		for(SexAreaInterface area : appendageAreas) {
			appendagesToGroin.put(area, groinAreas);

			appendagesToAllAreas.put(area, allAreas);

			appendagesToBreasts.put(area, breastAreas);
			appendagesToCrotchBoobs.put(area, crotchBoobAreas);

			appendagesToLowerHalf.put(area, Util.mergeLists(groinAreas, lowerHalf, assAreas));

			appendagesToAss.put(area, assAreas);

			appendagesToUpperHalf.put(area, Util.mergeLists(mouthAreas, breastAreas));
		}

		for(SexAreaInterface area : allAreas) {
			allAreasToAppendages.put(area, appendageAreas);
			allAreasToTailAndTentacle.put(area, tailAndTentacle);
			allAreasToLowerHalf.put(area, lowerHalf);
		}

		for(SexAreaInterface area : lowerHalf) {
			lowerHalfToAppendages.put(area, appendageAreas);
			lowerHalfToFinger.put(area, handAreas);
		}

		for(SexAreaInterface area : upperHalf) {
			upperHalfToFinger.put(area, handAreas);
			upperHalfToAppendages.put(area, appendageAreas);
		}

		for(SexAreaInterface area : crotchBoobAreas) {
			crotchBoobsToAppendages.put(area, appendageAreas);
		}

		for(SexAreaInterface area : groinAreas) {
			groinToGroin.put(area, groinAreas);
			groinToAss.put(area, assAreas);
			groinToPenis.put(area, penisAreas);
			groinToVagina.put(area, vaginaAreas);
			groinToMouth.put(area, mouthAreas);
			groinToFeet.put(area, feetAreas);
			groinToCrotchBoobs.put(area, crotchBoobAreas);
		}

		for(SexAreaInterface area : mouthAreas) {
			kissing.put(area, mouthAreas);
			mouthToAss.put(area, assAreas);
			mouthToVagina.put(area, vaginaAreas);
			mouthToPenis.put(area, penisAreas);
			mouthToCrotchBoobs.put(area, crotchBoobAreas);

			mouthToBreasts.put(area, breastAreas);

			mouthToLowerHalf.put(area, Util.mergeLists(groinAreas, lowerHalf, assAreas));

			mouthToAppendages.put(area, appendageAreas);

			mouthToTailAndTentacle.put(area, tailAndTentacle);
		}

		for(SexAreaInterface area : breastAreas) {
			breastsToMouth.put(area, mouthAreas);

			breastsToPenis.put(area, penisAreas);
		}

		for(SexAreaInterface area : penisAreas) {
			penisToAss.put(area, assAreas);
			penisToVagina.put(area, vaginaAreas);
			penisToBreasts.put(area, breastAreas);
			penisToMouth.put(area, mouthAreas);
			penisToPenis.put(area, penisAreas);
			penisToThighs.put(area, thighAreas);
			penisToFeet.put(area, feetAreas);
		}
		for(SexAreaInterface area : assAreas) {
			assToPenis.put(area, penisAreas);
			assToMouth.put(area, mouthAreas);
			assToGroin.put(area, groinAreas);
		}
		for(SexAreaInterface area : vaginaAreas) {
			vaginaToPenis.put(area, penisAreas);
			vaginaToMouth.put(area, mouthAreas);
			vaginaToVagina.put(area, vaginaAreas);
		}
	}

	public static List<Class<?>> positioningActionsNew = new ArrayList<>();
	public static List<Class<?>> miscActions = new ArrayList<>();
	public static List<Class<?>> sexActions = new ArrayList<>();
	public static List<Class<?>> selfActions = new ArrayList<>();

	public static List<Class<?>> allCommonActions = new ArrayList<>();

	static {
//		positioningActions.add(GenericPositioning.class);
		positioningActionsNew.add(PositioningMenu.class);
		positioningActionsNew.add(GenericPositioning.class);

		miscActions.add(GenericActions.class);
		miscActions.add(GenericOrgasms.class);
		miscActions.add(PlayerTalk.class);
		miscActions.add(PartnerTalk.class);
		miscActions.add(GenericTalk.class);

		// Sadistic actions:
		sexActions.add(SadisticActions.class);

		// Finger actions:
		sexActions.add(FingerAnus.class);
		sexActions.add(FingerBreasts.class);
		sexActions.add(FingerBreastsCrotch.class);
		sexActions.add(FingerMouth.class);
		sexActions.add(FingerNipple.class);
		sexActions.add(FingerVagina.class);
		sexActions.add(FingerClit.class);
		sexActions.add(FingerPenis.class);

		// Oral actions:
		sexActions.add(TongueMouth.class);
		sexActions.add(TongueAnus.class);
		sexActions.add(TongueVagina.class);
		sexActions.add(ClitMouth.class);
		sexActions.add(TongueMound.class);
		sexActions.add(TongueBreasts.class);
		sexActions.add(TongueBreastsCrotch.class);
		sexActions.add(TongueNipple.class);
		sexActions.add(TongueNippleCrotch.class);
		sexActions.add(PenisMouth.class);

		// Tail actions:
		sexActions.add(TailAnus.class);
		sexActions.add(TailVagina.class);

		// Penis actions:
		sexActions.add(PenisAss.class);
		sexActions.add(PenisAnus.class);
		sexActions.add(PenisNipple.class);
		sexActions.add(PenisNippleCrotch.class);
		sexActions.add(PenisVagina.class);
		sexActions.add(PenisBreasts.class);
		sexActions.add(PenisBreastsCrotch.class);
		sexActions.add(PenisThighs.class);
		sexActions.add(PenisFoot.class);
		sexActions.add(PenisFeet.class);
		sexActions.add(PenisUrethraVagina.class);
		sexActions.add(PenisUrethraPenis.class);

		// Vagina actions:
		sexActions.add(ClitClit.class);

//		selfActions.add(PlayerMasturbation.class);
//		selfActions.add(PartnerMasturbation.class);

		selfActions.add(PartnerSelfNoPen.class);
		selfActions.add(PartnerSelfFingerAnus.class);
		selfActions.add(PartnerSelfFingerMouth.class);
		selfActions.add(PartnerSelfFingerBreasts.class);
		selfActions.add(PartnerSelfFingerNipple.class);
		selfActions.add(PartnerSelfFingerVagina.class);
		selfActions.add(PartnerSelfPenisAnus.class);
		selfActions.add(PartnerSelfPenisMouth.class);
		selfActions.add(PartnerSelfPenisNipple.class);
		selfActions.add(PartnerSelfPenisVagina.class);
		selfActions.add(PartnerSelfTailAnus.class);
		selfActions.add(PartnerSelfTailMouth.class);
		selfActions.add(PartnerSelfTailNipple.class);
		selfActions.add(PartnerSelfTailVagina.class);
		selfActions.add(PartnerSelfTongueAnus.class);
		selfActions.add(PartnerSelfTongueMouth.class);
		selfActions.add(PartnerSelfTongueNipple.class);
		selfActions.add(PartnerSelfTongueVagina.class);

		selfActions.add(PlayerSelfNoPen.class);
		selfActions.add(PlayerSelfFingerAnus.class);
		selfActions.add(PlayerSelfFingerMouth.class);
		selfActions.add(PlayerSelfFingerBreasts.class);
		selfActions.add(PlayerSelfFingerNipple.class);
		selfActions.add(PlayerSelfFingerVagina.class);
		selfActions.add(PlayerSelfPenisAnus.class);
		selfActions.add(PlayerSelfPenisMouth.class);
		selfActions.add(PlayerSelfPenisNipple.class);
		selfActions.add(PlayerSelfPenisVagina.class);
		selfActions.add(PlayerSelfTailAnus.class);
		selfActions.add(PlayerSelfTailMouth.class);
		selfActions.add(PlayerSelfTailNipple.class);
		selfActions.add(PlayerSelfTailVagina.class);
		selfActions.add(PlayerSelfTongueAnus.class);
		selfActions.add(PlayerSelfTongueMouth.class);
		selfActions.add(PlayerSelfTongueNipple.class);
		selfActions.add(PlayerSelfTongueVagina.class);

		allCommonActions = Util.mergeLists(miscActions, sexActions, selfActions);

	}
}


code:
package com.lilithsthrone.game.sex.sexActions;

/**
 * @since 0.1.69
 * @version 0.3.5.1
 * @author Innoxia
 */
public enum SexActionPriority {
	LOW(0),

	NORMAL(1),

	HIGH(2),

	UNIQUE_MAX(3);

	private int value;

	private SexActionPriority(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}
}


code:
package com.lilithsthrone.game.sex.sexActions;

/**
 * @since 0.1.65
 * @version 0.3.4
 * @author Innoxia
 */
public enum SexActionType {

	/**Standard ongoing action. Requires all SexAreaInterfaces to be occupied with each other.*/
	ONGOING,

	/**An action in which a character is speaking.*/
	SPEECH,

	/**Standard non-penetrative action. Requires all SexAreaInterfaces to be exposed.*/
	REQUIRES_EXPOSED,

	/**Standard non-penetrative action. Requires all SexAreaInterfaces to be free (not involved in an ongoing action).*/
	REQUIRES_NO_PENETRATION,

	/**Non-penetrative action which also requires all SexAreaInterfaces to be exposed.*/
	REQUIRES_NO_PENETRATION_AND_EXPOSED,

	/**Action which starts an ongoing penetration or action (such as performing a blowjob, or fingering someone).*/
	START_ONGOING,

	/**Action which brings another character in to an ongoing penetration or action (such as getting a second kneeling NPC to join the first in giving you a blowjob, or fingering an NPC who is being fucked by a third party).*/
	START_ADDITIONAL_ONGOING,

	/**Action which stops the ongoing process of all SexAreaInterfaces.*/
	STOP_ONGOING,

	/**Positioning action.*/
	POSITIONING,

	/**OPening the positioning menu action.*/
	POSITIONING_MENU,

	/**The action taken to prepare for partner's orgasm.*/
	PREPARE_FOR_PARTNER_ORGASM,

	/**Orgasm.*/
	ORGASM,

	/**Orgasm that it denied (so it doesn't reset arousal, increment orgasm count, nor apply any orgasm-related effects).*/
	ORGASM_DENIAL,

	/**Special miscellaneous actions such as stopping sex.*/
	SPECIAL;

	/**
	 * @return True if this action <b>could</b> be a penetrating one.
	 *  If the ongoing action involves no penetration, such as penis being rubbed over pussy without being inserted (there are none at the time of this method being created in v0.3.0.6), then this will not be accurate.
	 */
	public boolean isPenetratingOption() {
		return this == START_ONGOING || this == ONGOING;
	}

	public boolean isOrgasmOption() {
		return this == ORGASM || this == ORGASM_DENIAL;
	}
}


code:
package com.lilithsthrone.game.sex.sexActions;

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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.sex.ArousalIncrease;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.positions.slots.SexSlotGeneric;
import com.lilithsthrone.game.sex.sexActions.baseActionsMisc.PositioningMenu;
import com.lilithsthrone.main.Main;

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

	// GENERIC:

	public static final SexAction PLAYER_NONE = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.ZERO_NONE,
			ArousalIncrease.ZERO_NONE,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.PLAYER_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Do nothing";
		}

		@Override
		public String getActionDescription() {
			return "Don't make a move.";
		}

		@Override
		public String getDescription() {
			if(Sex.isMasturbation()) {
				return "You remain still, not making a move...";
			}

			if(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction())==SexSlotGeneric.MISC_WATCHING) {
				List<GameCharacter> characters = new ArrayList<>(Sex.getAllParticipants());
				characters.remove(Sex.getCharacterPerformingAction());
				if(characters.size()>=2) {
					return UtilText.parse(characters,
							UtilText.returnStringAtRandom(
							"You remain in position, watching [npc.name] and [npc2.name] have sex before you.",
							"Staying quite still, you watch [npc.name] and [npc2.name] having fun in front of you.",
							"You carry on watching [npc.name] and [npc2.name], while doing nothing yourself."));
				}
			}

			switch(Sex.getSexPace(Main.game.getPlayer())) {
				case DOM_GENTLE:
					return UtilText.returnStringAtRandom(
							"You remain in position, gently pressing yourself against [npc.name], but not making any sort of move on [npc.herHim].",
							"Staying quite still, you press yourself up against [npc.name], waiting for [npc.herHim] to make the next move.",
							"You gently press yourself against [npc.name], content to let [npc.herHim] make the next move.");
				case DOM_NORMAL:
					return UtilText.returnStringAtRandom(
							"You remain in position, pressing yourself against [npc.name], but not making any sort of move on [npc.herHim].",
							"Staying quite still, you press yourself up against [npc.name], waiting for [npc.herHim] to make the next move.",
							"You press yourself against [npc.name], content to let [npc.herHim] make the next move.");
				case DOM_ROUGH:
					return UtilText.returnStringAtRandom(
							"You remain in position, grinding yourself against [npc.name], but not making any sort of move on [npc.herHim].",
							"Staying quite still, you grind yourself up against [npc.name], waiting for [npc.herHim] to make the next move.",
							"You grind yourself against [npc.name], content to let [npc.herHim] make the next move.");
				case SUB_EAGER:
					return UtilText.returnStringAtRandom(
							"You remain in position, pressing yourself against [npc.name], but not making any sort of move on [npc.herHim].",
							"Staying quite still, you press yourself up against [npc.name], waiting for [npc.herHim] to make the next move.",
							"You press yourself against [npc.name], content to let [npc.herHim] make the next move.");
				case SUB_NORMAL:
					return UtilText.returnStringAtRandom(
							"You remain in position, pressing yourself against [npc.name], but not making any sort of move on [npc.herHim].",
							"Staying quite still, you press yourself up against [npc.name], waiting for [npc.herHim] to make the next move.",
							"You press yourself against [npc.name], content to let [npc.herHim] make the next move.");
				case SUB_RESISTING:
					return UtilText.returnStringAtRandom(
							"You continue struggling against [npc.name], refusing to make any sort of move on [npc.herHim].",
							"Struggling and [pc.sobbing], you try to wriggle out of [npc.namePos] grasp, dreading what [npc.her] next move might be.",
							"You try to push [npc.name] away from you, [pc.sobbing] and struggling in distress as you refuse to submit.");
			}

			return "You remain in position, content to simply wait and see what [npc.name] does next.";
		}
	};

	public static final SexAction PLAYER_CALM_DOWN = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.NEGATIVE,
			ArousalIncrease.ZERO_NONE,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.PLAYER_ONLY;
		}
		@Override
		public String getActionTitle() {
			return "Calm down";
		}

		@Override
		public String getActionDescription() {
			return "Focus on calming yourself down, which lowers your arousal.";
		}

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

		@Override
		public String getDescription() {
			switch(Sex.getSexPace(Main.game.getPlayer())) {
				case DOM_GENTLE:
					return UtilText.returnStringAtRandom(
							Sex.isMasturbation()?"":"You take a moment to focus on something other than [npc.name], calming yourself down in the process.",
							"Closing your [pc.eyes], you take a deep breath, calming yourself down and lowering your arousal.",
							"Taking a deep breath, you focus on calming yourself down a little.");
				case DOM_NORMAL:
					return UtilText.returnStringAtRandom(
							Sex.isMasturbation()?"":"You take a moment to focus on something other than [npc.name], calming yourself down in the process.",
							"Closing your [pc.eyes], you take a deep breath, calming yourself down and lowering your arousal.",
							"Taking a deep breath, you focus on calming yourself down a little.");
				case DOM_ROUGH:
					return UtilText.returnStringAtRandom(
							Sex.isMasturbation()?"":"You take a moment to focus on something other than [npc.name], calming yourself down in the process.",
							"Closing your [pc.eyes], you take a deep breath, calming yourself down and lowering your arousal.",
							"Taking a deep breath, you focus on calming yourself down a little.");
				case SUB_EAGER:
					return UtilText.returnStringAtRandom(
							Sex.isMasturbation()?"":"You take a moment to focus on something other than [npc.name], calming yourself down in the process.",
							"Closing your [pc.eyes], you take a deep breath, calming yourself down and lowering your arousal.",
							"Taking a deep breath, you focus on calming yourself down a little.");
				case SUB_NORMAL:
					return UtilText.returnStringAtRandom(
							Sex.isMasturbation()?"":"You take a moment to focus on something other than [npc.name], calming yourself down in the process.",
							"Closing your [pc.eyes], you take a deep breath, calming yourself down and lowering your arousal.",
							"Taking a deep breath, you focus on calming yourself down a little.");
				case SUB_RESISTING:
					return UtilText.returnStringAtRandom(
							Sex.isMasturbation()?"":"Still weakly struggling against [npc.name], you try to calm yourself down a little, reminding yourself that this will all be over soon.",
							"Scrunching your [pc.eyes] shut, you try to take a deep breath, pretending that this isn't happening as you seek to calm yourself down.",
							Sex.isMasturbation()?"":"Taking a deep breath, you try to calm down a little, before continuing to struggle against [npc.name].");
				default:
					return Sex.isMasturbation()?"":"You try to focus on something other than the [npc.race] you're currently having sex with. By doing so, you manage to calm yourself down a little, reducing your arousal.";
			}
		}
	};

	public static final SexAction PARTNER_NONE = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}
		@Override
		public String getActionTitle() {
			return "Do nothing";
		}

		@Override
		public String getActionDescription() {
			return "Decide to do nothing.";
		}

		@Override
		public String getDescription() {
			return "[npc.Name] doesn't make a move.";
		}
	};

	public static final SexAction PARTNER_ORGASM_SKIP = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}
		@Override
		public String getActionTitle() {
			return "";
		}

		@Override
		public String getActionDescription() {
			return "";
		}

		@Override
		public String getDescription() {
			return "[npc.Name] lets out [npc.a_moan+].";
		}
	};

	public static final SexAction PLAYER_USE_ITEM = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.PLAYER_ONLY;
		}
		@Override
		public String getActionTitle() {
			return "Use item";
		}

		@Override
		public String getActionDescription() {
			return "See what items you could use.";
		}

		@Override
		public String getDescription() {
			return Sex.getUsingItemText();

		}
	};

	public static final SexAction CLOTHING_REMOVAL = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.ZERO_NONE,
			ArousalIncrease.ZERO_NONE,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.PLAYER_ONLY;
		}
		@Override
		public String getActionTitle() {
			return "Manage clothing";
		}

		@Override
		public String getActionDescription() {
			return "";
		}

		@Override
		public String getDescription() {
			return Sex.getUnequipClothingText();
		}
	};

	public static final SexAction CLOTHING_DYE = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.ZERO_NONE,
			ArousalIncrease.ZERO_NONE,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.PLAYER_ONLY;
		}
		@Override
		public String getActionTitle() {
			return "Manage clothing";
		}

		@Override
		public String getActionDescription() {
			return "";
		}

		@Override
		public String getDescription() {
			return Sex.getDyeClothingText();
		}
	};

	public static final SexAction POSITION_SELECTION = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ZERO_NONE,
			ArousalIncrease.ZERO_NONE,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.PLAYER_ONLY;
		}
		@Override
		public String getActionTitle() {
			return "New Position";
		}

		@Override
		public String getActionDescription() {
			return "";
		}

		@Override
		public String getDescription() {
			return "";
		}

		@Override
		public void applyEffects() {
			PositioningMenu.setNewSexManager();
			Sex.setSexStarted(true);
		}
	};
}