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

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

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.DialogueNodeType;
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.sex.ArousalIncrease;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexControl;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.managers.SexManagerDefault;
import com.lilithsthrone.game.sex.positions.AbstractSexPosition;
import com.lilithsthrone.game.sex.positions.SexPosition;
import com.lilithsthrone.game.sex.positions.slots.SexSlot;
import com.lilithsthrone.game.sex.positions.slots.SexSlotAgainstWall;
import com.lilithsthrone.game.sex.positions.slots.SexSlotAllFours;
import com.lilithsthrone.game.sex.positions.slots.SexSlotDesk;
import com.lilithsthrone.game.sex.positions.slots.SexSlotGeneric;
import com.lilithsthrone.game.sex.positions.slots.SexSlotLyingDown;
import com.lilithsthrone.game.sex.positions.slots.SexSlotMilkingStall;
import com.lilithsthrone.game.sex.positions.slots.SexSlotSitting;
import com.lilithsthrone.game.sex.positions.slots.SexSlotStanding;
import com.lilithsthrone.game.sex.positions.slots.SexSlotStocks;
import com.lilithsthrone.game.sex.sexActions.SexAction;
import com.lilithsthrone.game.sex.sexActions.SexActionType;
import com.lilithsthrone.game.sex.sexActions.SexActionUtility;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;

/**
 * Contains actions related to the positioning menu.
 *
 * @since 0.3.4
 * @version 0.3.4
 * @author Innoxia
 */
public class PositioningMenu {

	private static AbstractSexPosition position;
	public static Map<GameCharacter, SexSlot> positioningSlots;
	private static GameCharacter targetedCharacter;
	private static List<SexSlot> availableSlots;

	private static StringBuilder positioningSB = new StringBuilder();

	public static void setNewSexManager() {
		Map<GameCharacter, SexSlot> dominants = new HashMap<>();
		Map<GameCharacter, SexSlot> submissives = new HashMap<>();
		List<GameCharacter> dominantSpectators = new ArrayList<>();
		List<GameCharacter> submissiveSpectators = new ArrayList<>();

		for(Entry<GameCharacter, SexSlot> e : positioningSlots.entrySet()) {
//			System.out.println(e.getKey().getName()+", "+e.getValue().getName(e.getKey()));
			if(Sex.isDom(e.getKey()) || Sex.getDominantSpectators().contains(e.getKey())) {
				if(e.getValue()==SexSlotGeneric.MISC_WATCHING) {
					dominantSpectators.add(e.getKey());
				} else {
					dominants.put(e.getKey(), e.getValue());
				}
			} else {
				if(e.getValue()==SexSlotGeneric.MISC_WATCHING) {
					submissiveSpectators.add(e.getKey());
				} else {
					submissives.put(e.getKey(), e.getValue());
				}
			}
		}

		Sex.setSexManager(
				new SexManagerDefault(
						position,
						dominants,
						submissives){
				},
				dominantSpectators,
				submissiveSpectators);

		Sex.setPositionRequest(null);
	}

	@SuppressWarnings("fallthrough")
	private static List<SexSlot> getAvailableSlots(GameCharacter character) {
		List<SexSlot> slotsOne = new ArrayList<>();
		List<SexSlot> slotsTwo = new ArrayList<>();
		List<SexSlot> slotsThree = new ArrayList<>();
		List<SexSlot> slotsFour = new ArrayList<>();
		List<SexSlot> slotsFive = new ArrayList<>();
		List<SexSlot> slotsSix = new ArrayList<>();
		List<SexSlot> slotsSeven = new ArrayList<>();
		List<SexSlot> slotsEight = new ArrayList<>();
		List<SexSlot> slotsNine = new ArrayList<>();
		List<SexSlot> slotsTen = new ArrayList<>();
		List<SexSlot> slotsEleven = new ArrayList<>();
		List<SexSlot> slotsTwelve = new ArrayList<>();

		Map<GameCharacter, SexSlot> doms = Sex.getDominantParticipants(true);
		Map<GameCharacter, SexSlot> subs = Sex.getSubmissiveParticipants(true);

		if(position==SexPosition.STANDING) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotStanding.STANDING_DOMINANT_FOUR);
					slotsTwo.add(SexSlotStanding.STANDING_SUBMISSIVE_FOUR);
					slotsThree.add(SexSlotStanding.STANDING_SUBMISSIVE_BEHIND_FOUR);
					slotsFour.add(SexSlotStanding.PERFORMING_ORAL_FOUR);
					slotsFive.add(SexSlotStanding.PERFORMING_ORAL_BEHIND_FOUR);
				case 3:
					slotsOne.add(SexSlotStanding.STANDING_DOMINANT_THREE);
					slotsTwo.add(SexSlotStanding.STANDING_SUBMISSIVE_THREE);
					slotsThree.add(SexSlotStanding.STANDING_SUBMISSIVE_BEHIND_THREE);
					slotsFour.add(SexSlotStanding.PERFORMING_ORAL_THREE);
					slotsFive.add(SexSlotStanding.PERFORMING_ORAL_BEHIND_THREE);
				case 2:
					slotsOne.add(SexSlotStanding.STANDING_DOMINANT_TWO);
					slotsTwo.add(SexSlotStanding.STANDING_SUBMISSIVE_TWO);
					slotsThree.add(SexSlotStanding.STANDING_SUBMISSIVE_BEHIND_TWO);
					slotsFour.add(SexSlotStanding.PERFORMING_ORAL_TWO);
					slotsFive.add(SexSlotStanding.PERFORMING_ORAL_BEHIND_TWO);
				default:
					slotsOne.add(SexSlotStanding.STANDING_DOMINANT);
					slotsTwo.add(SexSlotStanding.STANDING_SUBMISSIVE);
					slotsThree.add(SexSlotStanding.STANDING_SUBMISSIVE_BEHIND);
					slotsFour.add(SexSlotStanding.PERFORMING_ORAL);
					slotsFive.add(SexSlotStanding.PERFORMING_ORAL_BEHIND);
			}
		}

		if(position==SexPosition.AGAINST_WALL) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotAgainstWall.STANDING_WALL_FOUR);
					slotsTwo.add(SexSlotAgainstWall.FACE_TO_WALL_FOUR);
					slotsThree.add(SexSlotAgainstWall.BACK_TO_WALL_FOUR);
					slotsFour.add(SexSlotAgainstWall.PERFORMING_ORAL_WALL_FOUR);
				case 3:
					slotsOne.add(SexSlotAgainstWall.STANDING_WALL_THREE);
					slotsTwo.add(SexSlotAgainstWall.FACE_TO_WALL_THREE);
					slotsThree.add(SexSlotAgainstWall.BACK_TO_WALL_THREE);
					slotsFour.add(SexSlotAgainstWall.PERFORMING_ORAL_WALL_THREE);
				case 2:
					slotsOne.add(SexSlotAgainstWall.STANDING_WALL_TWO);
					slotsTwo.add(SexSlotAgainstWall.FACE_TO_WALL_TWO);
					slotsThree.add(SexSlotAgainstWall.BACK_TO_WALL_TWO);
					slotsFour.add(SexSlotAgainstWall.PERFORMING_ORAL_WALL_TWO);
				default:
					slotsOne.add(SexSlotAgainstWall.STANDING_WALL);
					slotsTwo.add(SexSlotAgainstWall.FACE_TO_WALL);
					slotsThree.add(SexSlotAgainstWall.BACK_TO_WALL);
					slotsFour.add(SexSlotAgainstWall.PERFORMING_ORAL_WALL);
			}
		}

		if(position==SexPosition.OVER_DESK) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotDesk.BETWEEN_LEGS_FOUR);
					slotsTwo.add(SexSlotDesk.PERFORMING_ORAL_FOUR);
					slotsThree.add(SexSlotDesk.RECEIVING_ORAL_FOUR);
					slotsFour.add(SexSlotDesk.OVER_DESK_ON_BACK_FOUR);
					slotsFive.add(SexSlotDesk.OVER_DESK_ON_FRONT_FOUR);
				case 3:
					slotsOne.add(SexSlotDesk.BETWEEN_LEGS_THREE);
					slotsTwo.add(SexSlotDesk.PERFORMING_ORAL_THREE);
					slotsThree.add(SexSlotDesk.RECEIVING_ORAL_THREE);
					slotsFour.add(SexSlotDesk.OVER_DESK_ON_BACK_THREE);
					slotsFive.add(SexSlotDesk.OVER_DESK_ON_FRONT_THREE);
				case 2:
					slotsOne.add(SexSlotDesk.BETWEEN_LEGS_TWO);
					slotsTwo.add(SexSlotDesk.PERFORMING_ORAL_TWO);
					slotsThree.add(SexSlotDesk.RECEIVING_ORAL_TWO);
					slotsFour.add(SexSlotDesk.OVER_DESK_ON_BACK_TWO);
					slotsFive.add(SexSlotDesk.OVER_DESK_ON_FRONT_TWO);
				default:
					slotsOne.add(SexSlotDesk.BETWEEN_LEGS);
					slotsTwo.add(SexSlotDesk.PERFORMING_ORAL);
					slotsThree.add(SexSlotDesk.RECEIVING_ORAL);
					slotsFour.add(SexSlotDesk.OVER_DESK_ON_BACK);
					slotsFive.add(SexSlotDesk.OVER_DESK_ON_FRONT);
			}
		}

		if(position==SexPosition.STOCKS) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotStocks.BEHIND_STOCKS_FOUR);
					slotsTwo.add(SexSlotStocks.RECEIVING_ORAL_FOUR);
					slotsThree.add(SexSlotStocks.PERFORMING_ORAL_FOUR);
					slotsFour.add(SexSlotStocks.LOCKED_IN_STOCKS_FOUR);
				case 3:
					slotsOne.add(SexSlotStocks.BEHIND_STOCKS_THREE);
					slotsTwo.add(SexSlotStocks.RECEIVING_ORAL_THREE);
					slotsThree.add(SexSlotStocks.PERFORMING_ORAL_THREE);
					slotsFour.add(SexSlotStocks.LOCKED_IN_STOCKS_THREE);
				case 2:
					slotsOne.add(SexSlotStocks.BEHIND_STOCKS_TWO);
					slotsTwo.add(SexSlotStocks.RECEIVING_ORAL_TWO);
					slotsThree.add(SexSlotStocks.PERFORMING_ORAL_TWO);
					slotsFour.add(SexSlotStocks.LOCKED_IN_STOCKS_TWO);
				default:
					slotsOne.add(SexSlotStocks.BEHIND_STOCKS);
					slotsTwo.add(SexSlotStocks.RECEIVING_ORAL);
					slotsThree.add(SexSlotStocks.PERFORMING_ORAL);
					slotsFour.add(SexSlotStocks.LOCKED_IN_STOCKS);
			}
		}

		if(position==SexPosition.MILKING_STALL) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotMilkingStall.BEHIND_MILKING_STALL_FOUR);
					slotsTwo.add(SexSlotMilkingStall.RECEIVING_ORAL_FOUR);
					slotsThree.add(SexSlotMilkingStall.PERFORMING_ORAL_FOUR);
					slotsFour.add(SexSlotMilkingStall.LOCKED_IN_MILKING_STALL_FOUR);
				case 3:
					slotsOne.add(SexSlotMilkingStall.BEHIND_MILKING_STALL_THREE);
					slotsTwo.add(SexSlotMilkingStall.RECEIVING_ORAL_THREE);
					slotsThree.add(SexSlotMilkingStall.PERFORMING_ORAL_THREE);
					slotsFour.add(SexSlotMilkingStall.LOCKED_IN_MILKING_STALL_THREE);
				case 2:
					slotsOne.add(SexSlotMilkingStall.BEHIND_MILKING_STALL_TWO);
					slotsTwo.add(SexSlotMilkingStall.RECEIVING_ORAL_TWO);
					slotsThree.add(SexSlotMilkingStall.PERFORMING_ORAL_TWO);
					slotsFour.add(SexSlotMilkingStall.LOCKED_IN_MILKING_STALL_TWO);
				default:
					slotsOne.add(SexSlotMilkingStall.BEHIND_MILKING_STALL);
					slotsTwo.add(SexSlotMilkingStall.RECEIVING_ORAL);
					slotsThree.add(SexSlotMilkingStall.PERFORMING_ORAL);
					slotsFour.add(SexSlotMilkingStall.LOCKED_IN_MILKING_STALL);
			}
		}

		if(position==SexPosition.SITTING) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotSitting.SITTING_FOUR);
					slotsTwo.add(SexSlotSitting.SITTING_BETWEEN_LEGS_FOUR);
					slotsThree.add(SexSlotSitting.SITTING_IN_LAP_FOUR);
					slotsFour.add(SexSlotSitting.PERFORMING_ORAL_FOUR);
					slotsFive.add(SexSlotSitting.SITTING_TAUR_PRESENTING_ORAL_FOUR);
				case 3:
					slotsOne.add(SexSlotSitting.SITTING_THREE);
					slotsTwo.add(SexSlotSitting.SITTING_BETWEEN_LEGS_THREE);
					slotsThree.add(SexSlotSitting.SITTING_IN_LAP_THREE);
					slotsFour.add(SexSlotSitting.PERFORMING_ORAL_THREE);
					slotsFive.add(SexSlotSitting.SITTING_TAUR_PRESENTING_ORAL_THREE);
				case 2:
					slotsOne.add(SexSlotSitting.SITTING_TWO);
					slotsTwo.add(SexSlotSitting.SITTING_BETWEEN_LEGS_TWO);
					slotsThree.add(SexSlotSitting.SITTING_IN_LAP_TWO);
					slotsFour.add(SexSlotSitting.PERFORMING_ORAL_TWO);
					slotsFive.add(SexSlotSitting.SITTING_TAUR_PRESENTING_ORAL_TWO);
				default:
					slotsOne.add(SexSlotSitting.SITTING);
					slotsTwo.add(SexSlotSitting.SITTING_BETWEEN_LEGS);
					slotsThree.add(SexSlotSitting.SITTING_IN_LAP);
					slotsFour.add(SexSlotSitting.PERFORMING_ORAL);
					slotsFive.add(SexSlotSitting.SITTING_TAUR_PRESENTING_ORAL);
			}
		}

		if(position==SexPosition.ALL_FOURS) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotAllFours.ALL_FOURS_FOUR);
					slotsTwo.add(SexSlotAllFours.BEHIND_FOUR);
					slotsThree.add(SexSlotAllFours.BEHIND_ORAL_FOUR);
					slotsFour.add(SexSlotAllFours.HUMPING_FOUR);
					slotsFive.add(SexSlotAllFours.USING_FEET_FOUR);
					slotsSix.add(SexSlotAllFours.IN_FRONT_FOUR);
					slotsSeven.add(SexSlotAllFours.IN_FRONT_ANAL_FOUR);
				case 3:
					slotsOne.add(SexSlotAllFours.ALL_FOURS_THREE);
					slotsTwo.add(SexSlotAllFours.BEHIND_THREE);
					slotsThree.add(SexSlotAllFours.BEHIND_ORAL_THREE);
					slotsFour.add(SexSlotAllFours.HUMPING_THREE);
					slotsFive.add(SexSlotAllFours.USING_FEET_THREE);
					slotsSix.add(SexSlotAllFours.IN_FRONT_THREE);
					slotsSeven.add(SexSlotAllFours.IN_FRONT_ANAL_THREE);
				case 2:
					slotsOne.add(SexSlotAllFours.ALL_FOURS_TWO);
					slotsTwo.add(SexSlotAllFours.BEHIND_TWO);
					slotsThree.add(SexSlotAllFours.BEHIND_ORAL_TWO);
					slotsFour.add(SexSlotAllFours.HUMPING_TWO);
					slotsFive.add(SexSlotAllFours.USING_FEET_TWO);
					slotsSix.add(SexSlotAllFours.IN_FRONT_TWO);
					slotsSeven.add(SexSlotAllFours.IN_FRONT_ANAL_TWO);
				default:
					slotsOne.add(SexSlotAllFours.ALL_FOURS);
					slotsTwo.add(SexSlotAllFours.BEHIND);
					slotsThree.add(SexSlotAllFours.BEHIND_ORAL);
					slotsFour.add(SexSlotAllFours.HUMPING);
					slotsFive.add(SexSlotAllFours.USING_FEET);
					slotsSix.add(SexSlotAllFours.IN_FRONT);
					slotsSeven.add(SexSlotAllFours.IN_FRONT_ANAL);
			}
		}

		if(position==SexPosition.LYING_DOWN) {
			int sizeSlots = (doms.size()+subs.size())-1;
			switch(sizeSlots) {
				case 4: case 5: case 6: case 7: case 8:
					slotsOne.add(SexSlotLyingDown.LYING_DOWN_FOUR);
					slotsTwo.add(SexSlotLyingDown.COWGIRL_FOUR);
					slotsThree.add(SexSlotLyingDown.COWGIRL_REVERSE_FOUR);
					slotsFour.add(SexSlotLyingDown.FACE_SITTING_FOUR);
					slotsFive.add(SexSlotLyingDown.FACE_SITTING_REVERSE_FOUR);
					slotsSix.add(SexSlotLyingDown.LAP_PILLOW_FOUR);
					slotsSeven.add(SexSlotLyingDown.MATING_PRESS_FOUR);
					slotsEight.add(SexSlotLyingDown.MISSIONARY_FOUR);
					slotsNine.add(SexSlotLyingDown.SCISSORING_FOUR);
					slotsTen.add(SexSlotLyingDown.SIXTY_NINE_FOUR);
					slotsEleven.add(SexSlotLyingDown.BESIDE_FOUR);
					slotsTwelve.add(SexSlotLyingDown.MISSIONARY_ORAL_FOUR);
				case 3:
					slotsOne.add(SexSlotLyingDown.LYING_DOWN_THREE);
					slotsTwo.add(SexSlotLyingDown.COWGIRL_THREE);
					slotsThree.add(SexSlotLyingDown.COWGIRL_REVERSE_THREE);
					slotsFour.add(SexSlotLyingDown.FACE_SITTING_THREE);
					slotsFive.add(SexSlotLyingDown.FACE_SITTING_REVERSE_THREE);
					slotsSix.add(SexSlotLyingDown.LAP_PILLOW_THREE);
					slotsSeven.add(SexSlotLyingDown.MATING_PRESS_THREE);
					slotsEight.add(SexSlotLyingDown.MISSIONARY_THREE);
					slotsNine.add(SexSlotLyingDown.SCISSORING_THREE);
					slotsTen.add(SexSlotLyingDown.SIXTY_NINE_THREE);
					slotsEleven.add(SexSlotLyingDown.BESIDE_THREE);
					slotsTwelve.add(SexSlotLyingDown.MISSIONARY_ORAL_THREE);
				case 2:
					slotsOne.add(SexSlotLyingDown.LYING_DOWN_TWO);
					slotsTwo.add(SexSlotLyingDown.COWGIRL_TWO);
					slotsThree.add(SexSlotLyingDown.COWGIRL_REVERSE_TWO);
					slotsFour.add(SexSlotLyingDown.FACE_SITTING_TWO);
					slotsFive.add(SexSlotLyingDown.FACE_SITTING_REVERSE_TWO);
					slotsSix.add(SexSlotLyingDown.LAP_PILLOW_TWO);
					slotsSeven.add(SexSlotLyingDown.MATING_PRESS_TWO);
					slotsEight.add(SexSlotLyingDown.MISSIONARY_TWO);
					slotsNine.add(SexSlotLyingDown.SCISSORING_TWO);
					slotsTen.add(SexSlotLyingDown.SIXTY_NINE_TWO);
					slotsEleven.add(SexSlotLyingDown.BESIDE_TWO);
					slotsTwelve.add(SexSlotLyingDown.MISSIONARY_ORAL_TWO);
				default:
					slotsOne.add(SexSlotLyingDown.LYING_DOWN);
					slotsTwo.add(SexSlotLyingDown.COWGIRL);
					slotsThree.add(SexSlotLyingDown.COWGIRL_REVERSE);
					slotsFour.add(SexSlotLyingDown.FACE_SITTING);
					slotsFive.add(SexSlotLyingDown.FACE_SITTING_REVERSE);
					slotsSix.add(SexSlotLyingDown.LAP_PILLOW);
					slotsSeven.add(SexSlotLyingDown.MATING_PRESS);
					slotsEight.add(SexSlotLyingDown.MISSIONARY);
					slotsNine.add(SexSlotLyingDown.SCISSORING);
					slotsTen.add(SexSlotLyingDown.SIXTY_NINE);
					slotsEleven.add(SexSlotLyingDown.BESIDE);
					slotsTwelve.add(SexSlotLyingDown.MISSIONARY_ORAL);
			}
		}

		Collections.reverse(slotsOne);
		Collections.reverse(slotsTwo);
		Collections.reverse(slotsThree);
		Collections.reverse(slotsFour);
		Collections.reverse(slotsFive);
		Collections.reverse(slotsSix);
		Collections.reverse(slotsSeven);
		Collections.reverse(slotsEight);
		Collections.reverse(slotsNine);
		Collections.reverse(slotsTen);
		Collections.reverse(slotsEleven);
		Collections.reverse(slotsTwelve);

		List<SexSlot> slots = new ArrayList<>(slotsOne);
		slots.addAll(slotsTwo);
		slots.addAll(slotsThree);
		slots.addAll(slotsFour);
		slots.addAll(slotsFive);
		slots.addAll(slotsSix);
		slots.addAll(slotsSeven);
		slots.addAll(slotsEight);
		slots.addAll(slotsNine);
		slots.addAll(slotsTen);
		slots.addAll(slotsEleven);
		slots.addAll(slotsTwelve);

		slots.add(SexSlotGeneric.MISC_WATCHING);

		return slots;
	}

	private static Value<Boolean, String> isSlotsAcceptable() {
		if(!Sex.isMasturbation()) {
			boolean domsAcceptable = false;
			for(GameCharacter c : Sex.getDominantParticipants(true).keySet()) {
				if(positioningSlots.get(c)!=SexSlotGeneric.MISC_WATCHING) {
					domsAcceptable = true;
					break;
				}
			}
			boolean subsAcceptable = false;
			for(GameCharacter c : Sex.getSubmissiveParticipants(true).keySet()) {
				if(positioningSlots.get(c)!=SexSlotGeneric.MISC_WATCHING) {
					subsAcceptable = true;
					break;
				}
			}
			if(!(domsAcceptable && subsAcceptable)) {
				return new Value<Boolean, String>(false, "You need to have at least one dominant and one submissive character in non-spectator slots.");
			}
		}

		return position.isAcceptablePosition(positioningSlots);
	}

	private static String getDominantsDiv() {
		StringBuilder sb = new StringBuilder();

		sb.append("
");
			sb.append("
");
			if(Sex.getDominantParticipants(true).size()>0) {
				sb.append("[style.boldDominant(Dominants)]");
					for(GameCharacter c : Sex.getDominantParticipants(true).keySet()) {
						sb.append(UtilText.parse(c,
								"
[npc.Name]: "
										+(positioningSlots.get(c)==SexSlotGeneric.MISC_WATCHING
											?"[style.colourDisabled("+Util.capitaliseSentence(positioningSlots.get(c).getDescription())+")]"
											:Util.capitaliseSentence(positioningSlots.get(c).getDescription()))));
					}
			}
			sb.append("
");
		sb.append("
");

		return sb.toString();
	}

	private static String getSubmissivesDiv() {
		StringBuilder sb = new StringBuilder();

		sb.append("
");
			sb.append("
");
			if(Sex.getSubmissiveParticipants(true).size()>0) {
				sb.append("[style.boldSubmissive(Submissives)]");
					for(GameCharacter c : Sex.getSubmissiveParticipants(true).keySet()) {
						sb.append(UtilText.parse(c,
								"
[npc.Name]: "
										+(positioningSlots.get(c)==SexSlotGeneric.MISC_WATCHING
											?"[style.colourDisabled("+Util.capitaliseSentence(positioningSlots.get(c).getDescription())+")]"
											:Util.capitaliseSentence(positioningSlots.get(c).getDescription()))));
					}
			}
			sb.append("
");
		sb.append("
");

		return sb.toString();
	}

	private static String performAcceptableSlotsCheck() {
		StringBuilder sb = new StringBuilder();

		sb.append("
");
		List<GameCharacter> charactersToMoveToSpectators = new ArrayList<>();
		for(Entry<GameCharacter, SexSlot> e : positioningSlots.entrySet()) {
			if(!e.getKey().equals(targetedCharacter) && !position.isSlotUnlocked(e.getKey(), e.getValue(), positioningSlots).getKey()) {
				charactersToMoveToSpectators.add(e.getKey());
				sb.append(
						"[style.colourBad("+UtilText.parse(e.getKey(),
								"[npc.NamePos] selected position slot, '"+Util.capitaliseSentence(e.getValue().getDescription())+"', is now unavailable, and as such, [npc.sheHasFull] been moved to a spectator slot!")
						+")]");
			}
		}
		for(GameCharacter c : charactersToMoveToSpectators) {
			positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
		}
		sb.append("
");

		return sb.toString();
	}

	public static final DialogueNode POSITIONING_MENU = new DialogueNode("Positioning Selection", "", true) {
		@Override
		public String getLabel() {
			return "Positioning Selection: "+position.getName();
		}
		@Override
		public String getContent() {
			UtilText.nodeContentSB.setLength(0);

			if(Sex.isDom(Main.game.getPlayer())) {
				UtilText.nodeContentSB.append(getDominantsDiv());
				UtilText.nodeContentSB.append(getSubmissivesDiv());

			} else {
				UtilText.nodeContentSB.append(getSubmissivesDiv());
				UtilText.nodeContentSB.append(getDominantsDiv());
			}

			Value<Boolean, String> acceptablePosition = position.isAcceptablePosition(positioningSlots);
			if(!acceptablePosition.getKey()) {
				UtilText.nodeContentSB.append(
						"
"
								+ "[style.boldBad(Invalid position)]:
"
								+"[style.italicsMinorBad("+acceptablePosition.getValue()+")]"
						+ "
");

			} else {
				Value<Boolean, String> acceptableValue = isSlotsAcceptable();
				if(!acceptableValue.getKey()) {
					UtilText.nodeContentSB.append(
							"
"
									+ "[style.boldBad(Invalid position)]:
"
									+"[style.italicsMinorBad("+acceptableValue.getValue()+")]"
							+ "
");
				} else {
					UtilText.nodeContentSB.append(
							"
"
									+ "Projected outcome:
"
									+""+position.getDescription(positioningSlots)+""
							+ "
");
				}
			}

			// TODO help text

			return UtilText.nodeContentSB.toString();
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index == 0) {
				return new ResponseEffectsOnly("Back", "Decide against changing position."){
					@Override
					public void effects() {
						Main.game.restoreSavedContent(false);
					}
				};

			} else if(index==1) {
				Value<Boolean, String> acceptableValue = isSlotsAcceptable();
				if(!acceptableValue.getKey()) {
					return new Response("Accept", acceptableValue.getValue(), null);
				}
				return new ResponseEffectsOnly("Accept", "Move into the selected position."){
					@Override
					public boolean isSexPositioningHighlight() {
						return true;
					}
					@Override
					public void effects(){
						positioningSB.setLength(0);

						Main.mainController.openPhone();

						positioningSB.append(
								"Wanting to switch into a new position, "
									+(Sex.isMasturbation()
										?"you move around until you've assumed the posture you had in mind..."
										:(Sex.getAllParticipants(false).size()==2
											?UtilText.parse(Sex.getTargetedPartner(Main.game.getPlayer()), "you manoeuvre [npc.name] into [npc.her] new place")
											:"you manoeuvre your partners into their new places")
										+", before assuming the posture you had in mind..."));

						SexActionUtility.POSITION_SELECTION.applyEffects();
						Sex.endSexTurn(SexActionUtility.POSITION_SELECTION);
						Main.game.setContent(new Response("", "", Sex.SEX_DIALOGUE));
//						Sex.endSexTurn(PlayerTalk.DIRTY_TALK);
//						Main.game.setContent(SexActionUtility.POSITION_SELECTION.toResponse());
//
					}
				};

			} else if(index==2) {
				return new Response(
						"Target: "+UtilText.parse(targetedCharacter, "[npc.Name]")+"",
						"This is the character you're currently choosing a slot for.",
						POSITIONING_MENU){
					@Override
					public void effects() {
						List<GameCharacter> characters = Sex.getAllParticipants(true);

						for(int i=0; i<characters.size(); i++) {
							if(characters.get(i).equals(targetedCharacter)) {
								if(i==characters.size()-1) {
									targetedCharacter = characters.get(0);
									break;
								} else {
									targetedCharacter = characters.get(i+1);
									break;
								}
							}
						}

						availableSlots = getAvailableSlots(targetedCharacter);
					}
				};

			} else if(index-3<availableSlots.size()) {
				SexSlot slot = availableSlots.get(index-3);

				GameCharacter characterInSlot = null;
				for(Entry<GameCharacter, SexSlot> e : positioningSlots.entrySet()) {
					if(e.getValue().equals(slot)) {
						characterInSlot = e.getKey();
						break;
					}
				}

				if(targetedCharacter.equals(characterInSlot)) {
					return new Response(Util.capitaliseSentence(slot.getDescription()), UtilText.parse(targetedCharacter, "[npc.NameIsFull] already in this slot..."), null);
				}

				if(!position.isSlotUnlocked(targetedCharacter, slot, positioningSlots).getKey()) {
					return new Response(Util.capitaliseSentence(slot.getDescription()), position.isSlotUnlocked(targetedCharacter, slot, positioningSlots).getValue(), null);
				}

				return new Response(
						characterInSlot!=null && slot!=SexSlotGeneric.MISC_WATCHING
							?""+Util.capitaliseSentence(slot.getDescription())+""
							:Util.capitaliseSentence(slot.getDescription()),
						characterInSlot!=null && slot!=SexSlotGeneric.MISC_WATCHING
							?UtilText.parse(targetedCharacter, characterInSlot,
									"[npc2.NameIsFull] already assigned to this slot, so by selecting [npc.name] to take this slot, you will swap the two of them around."
									+ " (Resulting in [npc2.name] being in the '"+positioningSlots.get(targetedCharacter).getDescription()+"' slot.)")
							:UtilText.parse(targetedCharacter, "Assign [npc.name] to this slot."),
						POSITIONING_MENU){
					@Override
					public void effects() {
						if(slot!=SexSlotGeneric.MISC_WATCHING) {
							GameCharacter characterToSwap = null;
							for(Entry<GameCharacter, SexSlot> e : positioningSlots.entrySet()) {
								if(e.getValue().equals(slot)) {
									characterToSwap = e.getKey();
									break;
								}
							}
							if(characterToSwap!=null) {
								positioningSlots.put(characterToSwap, positioningSlots.get(targetedCharacter));
							}
						}
						positioningSlots.put(targetedCharacter, slot);

						// If change a slot and other slots become unavailable, move them to spectator with warning at bottom:
						Main.game.getTextEndStringBuilder().append(performAcceptableSlotsCheck());
					}
				};

			}  else {
				return null;
			}
		}

		@Override
		public DialogueNodeType getDialogueNodeType() {
			return DialogueNodeType.PHONE;
		}
	};

	public static final SexAction OPEN_MENU_STANDING = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.STANDING);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.STANDING.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.STANDING.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.STANDING;
			SexSlot[] domSlots = new SexSlot[] {SexSlotStanding.STANDING_DOMINANT, SexSlotStanding.STANDING_DOMINANT_TWO, SexSlotStanding.STANDING_DOMINANT_THREE, SexSlotStanding.STANDING_DOMINANT_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotStanding.STANDING_SUBMISSIVE, SexSlotStanding.STANDING_SUBMISSIVE_TWO, SexSlotStanding.STANDING_SUBMISSIVE_THREE, SexSlotStanding.STANDING_SUBMISSIVE_FOUR};
			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.STANDING) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				for(int i=0; i<doms.size(); i++) {
					positioningSlots.put(doms.get(i), domSlots[i]);
				}
				for(int i=0; i<subs.size(); i++) {
					positioningSlots.put(subs.get(i), subSlots[i]);
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};

	public static final SexAction OPEN_MENU_AGAINST_WALL = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.AGAINST_WALL);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.AGAINST_WALL.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.AGAINST_WALL.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.AGAINST_WALL;
			SexSlot[] domSlots = new SexSlot[] {SexSlotAgainstWall.STANDING_WALL, SexSlotAgainstWall.STANDING_WALL_TWO, SexSlotAgainstWall.STANDING_WALL_THREE, SexSlotAgainstWall.STANDING_WALL_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotAgainstWall.FACE_TO_WALL, SexSlotAgainstWall.FACE_TO_WALL_TWO, SexSlotAgainstWall.FACE_TO_WALL_THREE, SexSlotAgainstWall.FACE_TO_WALL_FOUR};
			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.AGAINST_WALL) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				for(int i=0; i<doms.size(); i++) {
					positioningSlots.put(doms.get(i), domSlots[i]);
				}
				for(int i=0; i<subs.size(); i++) {
					positioningSlots.put(subs.get(i), subSlots[i]);
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};

	public static final SexAction OPEN_MENU_SITTING = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			int taurs = 0;
			for(GameCharacter participant : Sex.getAllParticipants(true)) {
				if(participant.isTaur()) {
					taurs++;
				}
			}
			return Sex.getAllParticipants(true).size()!=taurs // Need at least one non-taur
					&& taurs<=4 // Cannot have more than 4 taurs
					&& Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.SITTING);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.SITTING.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.SITTING.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.SITTING;
			SexSlot[] domSlots = new SexSlot[] {SexSlotSitting.SITTING, SexSlotSitting.SITTING_TWO, SexSlotSitting.SITTING_THREE, SexSlotSitting.SITTING_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotSitting.SITTING_IN_LAP, SexSlotSitting.SITTING_IN_LAP_TWO, SexSlotSitting.SITTING_IN_LAP_THREE, SexSlotSitting.SITTING_IN_LAP_FOUR};

			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.SITTING) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				int sittingCount = 0;
				int inLapCount = 0;

				for(int i=0; i<doms.size(); i++) {
					if(!doms.get(i).isTaur()) {
						positioningSlots.put(doms.get(i), domSlots[sittingCount]);
						sittingCount++;
					} else {
						positioningSlots.put(doms.get(i), subSlots[inLapCount]);
						inLapCount++;
					}
				}
				for(int i=0; i<subs.size(); i++) {
					if(!subs.get(i).isTaur()) {
						positioningSlots.put(subs.get(i), domSlots[sittingCount]);
						sittingCount++;
					} else {
						positioningSlots.put(subs.get(i), subSlots[inLapCount]);
						inLapCount++;
					}
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};

	public static final SexAction OPEN_MENU_OVER_DESK = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.OVER_DESK);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.OVER_DESK.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.OVER_DESK.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.OVER_DESK;
			SexSlot[] domSlots = new SexSlot[] {SexSlotDesk.BETWEEN_LEGS, SexSlotDesk.BETWEEN_LEGS_TWO, SexSlotDesk.BETWEEN_LEGS_THREE, SexSlotDesk.BETWEEN_LEGS_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotDesk.OVER_DESK_ON_FRONT, SexSlotDesk.OVER_DESK_ON_FRONT_TWO, SexSlotDesk.OVER_DESK_ON_FRONT_THREE, SexSlotDesk.OVER_DESK_ON_FRONT_FOUR};
			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.OVER_DESK) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				for(int i=0; i<doms.size(); i++) {
					positioningSlots.put(doms.get(i), domSlots[i]);
				}
				for(int i=0; i<subs.size(); i++) {
					positioningSlots.put(subs.get(i), subSlots[i]);
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};

	public static final SexAction OPEN_MENU_ALL_FOURS = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.ALL_FOURS);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.ALL_FOURS.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.ALL_FOURS.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.ALL_FOURS;
			SexSlot[] domSlots = new SexSlot[] {SexSlotAllFours.BEHIND, SexSlotAllFours.BEHIND_TWO, SexSlotAllFours.BEHIND_THREE, SexSlotAllFours.BEHIND_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotAllFours.ALL_FOURS, SexSlotAllFours.ALL_FOURS_TWO, SexSlotAllFours.ALL_FOURS_THREE, SexSlotAllFours.ALL_FOURS_FOUR};
			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.ALL_FOURS) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				for(int i=0; i<doms.size(); i++) {
					positioningSlots.put(doms.get(i), domSlots[i]);
				}
				for(int i=0; i<subs.size(); i++) {
					positioningSlots.put(subs.get(i), subSlots[i]);
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};

	public static final SexAction OPEN_MENU_LYING_DOWN = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.LYING_DOWN);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.LYING_DOWN.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.LYING_DOWN.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.LYING_DOWN;
			SexSlot[] domSlots = new SexSlot[] {SexSlotLyingDown.MISSIONARY, SexSlotLyingDown.MISSIONARY_TWO, SexSlotLyingDown.MISSIONARY_THREE, SexSlotLyingDown.MISSIONARY_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotLyingDown.LYING_DOWN, SexSlotLyingDown.LYING_DOWN_TWO, SexSlotLyingDown.LYING_DOWN_THREE, SexSlotLyingDown.LYING_DOWN_FOUR};
			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.LYING_DOWN) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				for(int i=0; i<doms.size(); i++) {
					positioningSlots.put(doms.get(i), domSlots[i]);
				}
				for(int i=0; i<subs.size(); i++) {
					positioningSlots.put(subs.get(i), subSlots[i]);
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};

	public static final SexAction OPEN_MENU_STOCKS = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.STOCKS);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.STOCKS.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.STOCKS.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.STOCKS;
			SexSlot[] domSlots = new SexSlot[] {SexSlotStocks.BEHIND_STOCKS, SexSlotStocks.BEHIND_STOCKS_TWO, SexSlotStocks.BEHIND_STOCKS_THREE, SexSlotStocks.BEHIND_STOCKS_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotStocks.LOCKED_IN_STOCKS, SexSlotStocks.LOCKED_IN_STOCKS_TWO, SexSlotStocks.LOCKED_IN_STOCKS_THREE, SexSlotStocks.LOCKED_IN_STOCKS_FOUR};
			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.STOCKS) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				for(int i=0; i<doms.size(); i++) {
					positioningSlots.put(doms.get(i), domSlots[i]);
				}
				for(int i=0; i<subs.size(); i++) {
					positioningSlots.put(subs.get(i), subSlots[i]);
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};

	public static final SexAction OPEN_MENU_MILKING_STALL = new SexAction(
			SexActionType.POSITIONING_MENU,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			null,
			SexParticipantType.NORMAL) {

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.isPositionMenuChangingAllowed(Main.game.getPlayer())
					&& Sex.getSexControl(Sex.getCharacterPerformingAction())==SexControl.FULL
					&& Sex.getInitialSexManager().getAllowedSexPositions().contains(SexPosition.MILKING_STALL);
		}

		@Override
		public String getActionTitle() {
			return Util.capitaliseSentence(SexPosition.MILKING_STALL.getName());
		}

		@Override
		public String getActionDescription() {
			return "Open the positioning menu for all sex positions related to the '"+Util.capitaliseSentence(SexPosition.MILKING_STALL.getName())+"' core position.";
		}

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

		@Override
		public void applyEffects() {
			position = SexPosition.MILKING_STALL;
			SexSlot[] domSlots = new SexSlot[] {SexSlotMilkingStall.BEHIND_MILKING_STALL, SexSlotMilkingStall.BEHIND_MILKING_STALL_TWO, SexSlotMilkingStall.BEHIND_MILKING_STALL_THREE, SexSlotMilkingStall.BEHIND_MILKING_STALL_FOUR};
			SexSlot[] subSlots = new SexSlot[] {SexSlotMilkingStall.LOCKED_IN_MILKING_STALL, SexSlotMilkingStall.LOCKED_IN_MILKING_STALL_TWO, SexSlotMilkingStall.LOCKED_IN_MILKING_STALL_THREE, SexSlotMilkingStall.LOCKED_IN_MILKING_STALL_FOUR};
			List<GameCharacter> doms = new ArrayList<>(Sex.getDominantParticipants(false).keySet());
			List<GameCharacter> subs = new ArrayList<>(Sex.getSubmissiveParticipants(false).keySet());

			if(Sex.getSexManager().getPosition()==SexPosition.MILKING_STALL) {
				positioningSlots = new HashMap<>(Sex.getAllOccupiedSlots(true));

			} else {
				positioningSlots = new HashMap<>();
				for(int i=0; i<doms.size(); i++) {
					positioningSlots.put(doms.get(i), domSlots[i]);
				}
				for(int i=0; i<subs.size(); i++) {
					positioningSlots.put(subs.get(i), subSlots[i]);
				}

				for(GameCharacter c : Sex.getDominantSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
				for(GameCharacter c : Sex.getSubmissiveSpectators()) {
					positioningSlots.put(c, SexSlotGeneric.MISC_WATCHING);
				}
			}

			targetedCharacter = Main.game.getPlayer();//Sex.getCharacterTargetedForSexAction(this);
			availableSlots = getAvailableSlots(targetedCharacter);

			performAcceptableSlotsCheck();

			Main.mainController.openPhone(POSITIONING_MENU);
		}
	};
}


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

import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.character.fetishes.Fetish;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.sex.ArousalIncrease;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexAreaOrifice;
import com.lilithsthrone.game.sex.SexAreaPenetration;
import com.lilithsthrone.game.sex.SexPace;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.sexActions.SexAction;
import com.lilithsthrone.game.sex.sexActions.SexActionCategory;
import com.lilithsthrone.game.sex.sexActions.SexActionType;

/**
 * @since 0.3.4
 * @version 0.3.4
 * @author Innoxia
 */
public class SadisticActions {

	public static final SexAction SLAP_FACE = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.TWO_LOW,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.FOUR_LUSTFUL,
			null,
			SexParticipantType.NORMAL,
			SexPace.DOM_ROUGH) {

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

		@Override
		public boolean isBaseRequirementsMet() {
			boolean mouthFinger = false;
			boolean mouthFingerReversed = false;
			try {
				mouthFinger = Sex.getPosition().getSlotTargets().get(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction())).get(Sex.getSexPositionSlot(Sex.getCharacterTargetedForSexAction(this)))
						.getInteractions().get(SexAreaPenetration.FINGER).contains(SexAreaOrifice.MOUTH);
			} catch(Exception ex) {
				// No available finger-mouth actions, so can't reach face
			}
			try {
				mouthFingerReversed = Sex.getPosition().getSlotTargets().get(Sex.getSexPositionSlot(Sex.getCharacterTargetedForSexAction(this))).get(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction()))
						.getInteractions().get(SexAreaOrifice.MOUTH).contains(SexAreaPenetration.FINGER);
			} catch(Exception ex) {
				// No available finger-mouth actions, so can't reach face
			}
			return SexAreaPenetration.FINGER.isFree(Sex.getCharacterPerformingAction())
					&& !Sex.isDom(Sex.getCharacterTargetedForSexAction(this))
					&& (mouthFinger || mouthFingerReversed)
					&& (Sex.getCharacterPerformingAction().isPlayer() || Sex.getCharacterPerformingAction().hasFetish(Fetish.FETISH_SADIST));

		}

		@Override
		public String getActionTitle() {
			return "Slap face";
		}

		@Override
		public String getActionDescription() {
			return "Slap [npc2.namePos] face to put [npc2.herHim] in [npc2.her] place.";
		}

		@Override
		public SexActionCategory getCategory() {
			return SexActionCategory.SEX;
		}

		@Override
		public String getDescription() {
			StringBuilder sb = new StringBuilder();

			sb.append(UtilText.returnStringAtRandom(
					"Wanting to put [npc2.name] in [npc2.her] place, [npc.name] [npc.verb(lift)] [npc.her] [npc.hand], before swinging down to deliver a sharp, stinging slap to [npc2.her] face.",
					"Lifting [npc.her] [npc.hand], [npc.name] [npc.verb(swing)] down, delivering a sharp slap to [npc2.namePos] face in order to put [npc2.herHim] in [npc2.her] place.",
					"Seeking to remind [npc2.name] who's in charge, [npc.name] [npc.verb(raise)] [npc.her] [npc.hand], before swiping down and delivering a sharp slap to [npc2.her] face."));

			if(Sex.getCharacterTargetedForSexAction(this).getFetishDesire(Fetish.FETISH_MASOCHIST).isPositive() && Sex.getSexPace(Sex.getCharacterTargetedForSexAction(this))!=SexPace.SUB_RESISTING) {
				sb.append(UtilText.returnStringAtRandom(
						" A clearly aroused yelp escapes from [npc2.namePos] mouth at the moment of contact, letting [npc.name] know that [npc2.sheIs] deriving masochistic pleasure from being abused in such a fashion.",
						" The horny squeal that escapes [npc2.namePos] mouth is enough to let anyone realise that [npc2.sheIs] getting turned on from being treated in such a degrading manner.",
						" Instead of a painful cry, [npc2.name] [npc2.verb(let)] out a horny moan, letting [npc.name] know that [npc2.sheIs] a masochist who's getting turned on by being abused like this."));
			} else {
				if(Sex.getCharacterTargetedForSexAction(this).isFeminine()) {
					sb.append(UtilText.returnStringAtRandom(
							" A protesting yelp escapes from [npc2.namePos] mouth at the moment of contact,"
									+ (Sex.getCharacterPerformingAction().isPlayer()
											?" which is precisely the reaction you were looking for."
											:" and from the wicked grin that settles on [npc.namePos] face, this was just the reaction [npc.she] [npc.was] looking for."),
							" The pained squeal which immediately escapes from [npc2.namePos] mouth is exactly what [npc.name] [npc.was] looking for, and [npc.she] can't help but grin as [npc.she] [npc.verb(see)] tears well up in"
									+ (Sex.getCharacterTargetedForSexAction(this).isPlayer()
											?" your [npc2.eyes+]."
											:" [npc.her] submissive bitch's [npc2.eyes+]."),
							" After letting out a shocked cry, tears start to well up in [npc2.namePos] [npc2.eyes], letting [npc.name] know that [npc.her] abuse is having the exact effect [npc.she] [npc.was] looking for."));

				} else {
					sb.append(UtilText.returnStringAtRandom(
							" A protesting shout escapes from [npc2.namePos] mouth at the moment of contact,"
									+ (Sex.getCharacterPerformingAction().isPlayer()
											?" which is precisely the reaction you were looking for."
											:" and from the wicked grin that settles on [npc.namePos] face, this was just the reaction [npc.she] [npc.was] looking for."),
							" The pained exclamation which immediately escapes from [npc2.namePos] mouth is exactly what [npc.name] [npc.was] looking for, and [npc.she] can't help but grin as [npc.she] [npc.verb(see)] the shocked look in"
									+ (Sex.getCharacterTargetedForSexAction(this).isPlayer()
											?" your [npc2.eyes+]."
											:" [npc.her] submissive bitch's [npc2.eyes+]."),
							" [npc2.Name] immediately [npc2.verb(let)] out a shocked cry, revealing to [npc.name] that [npc.her] abuse is having the exact effect [npc.she] [npc.was] looking for."));
				}
			}

			return sb.toString();
		}
	};

	public static final SexAction SPIT_FACE = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.TWO_LOW,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.FOUR_LUSTFUL,
			null,
			SexParticipantType.NORMAL,
			SexPace.DOM_ROUGH) {

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

		@Override
		public boolean isBaseRequirementsMet() {
			boolean mouthTongue = false;
			boolean mouthTongueReversed = false;
			try {
				mouthTongue = Sex.getPosition().getSlotTargets().get(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction())).get(Sex.getSexPositionSlot(Sex.getCharacterTargetedForSexAction(this)))
						.getInteractions().get(SexAreaPenetration.TONGUE).contains(SexAreaOrifice.MOUTH);
			} catch(Exception ex) {
				// No available finger-mouth actions, so can't reach face
			}
			try {
				mouthTongueReversed = Sex.getPosition().getSlotTargets().get(Sex.getSexPositionSlot(Sex.getCharacterTargetedForSexAction(this))).get(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction()))
						.getInteractions().get(SexAreaOrifice.MOUTH).contains(SexAreaPenetration.TONGUE);
			} catch(Exception ex) {
				// No available finger-mouth actions, so can't reach face
			}
			return SexAreaOrifice.MOUTH.isFree(Sex.getCharacterPerformingAction())
					&& !Sex.isDom(Sex.getCharacterTargetedForSexAction(this))
					&& (mouthTongue || mouthTongueReversed)
					&& (Sex.getCharacterPerformingAction().isPlayer() || Sex.getCharacterPerformingAction().hasFetish(Fetish.FETISH_SADIST));

		}

		@Override
		public String getActionTitle() {
			return "Spit in face";
		}

		@Override
		public String getActionDescription() {
			return "Spit in [npc2.namePos] face show [npc2.herHim] that [npc2.sheIs] worthless.";
		}

		@Override
		public SexActionCategory getCategory() {
			return SexActionCategory.SEX;
		}

		@Override
		public String getDescription() {
			StringBuilder sb = new StringBuilder();

			sb.append(UtilText.returnStringAtRandom(
					"Wanting to show [npc2.name] that [npc2.sheIs] a worthless bitch, [npc.name] [npc.verb(lean)] in towards [npc2.herHim], before spitting on [npc2.her] face.",
					"Leaning in towards [npc2.name], [npc.name] [npc.verb(purse)] [npc.her] [npc.lips+], before spitting directly on [npc2.her] face.",
					"Seeking to remind [npc2.name] of how worthless [npc2.sheIsFull], [npc.name] [npc.verb(lean)] in towards [npc2.herHim], before pursing [npc.her] [npc.lips+] and spitting directly on [npc2.her] face."));

			if(Sex.getCharacterTargetedForSexAction(this).getFetishDesire(Fetish.FETISH_MASOCHIST).isPositive() && Sex.getSexPace(Sex.getCharacterTargetedForSexAction(this))!=SexPace.SUB_RESISTING) {
				sb.append(UtilText.returnStringAtRandom(
						" A clearly aroused [npc2.moan] escapes from [npc2.namePos] mouth as the ball of saliva splatters onto [npc2.her] cheek, letting [npc.name] know that [npc2.sheIs] deriving masochistic pleasure from being abused in such a fashion.",
						" The horny squeal that escapes [npc2.namePos] mouth is enough to let anyone realise that [npc2.sheIs] getting turned on from being treated in such a degrading manner.",
						" Instead of a disgusted cry, [npc2.name] [npc2.verb(let)] out a horny [npc2.moan], letting [npc.name] know that [npc2.sheIs] a masochist who's getting turned on by being abused like this."));
			} else {
				if(Sex.getCharacterTargetedForSexAction(this).isFeminine()) {
					sb.append(UtilText.returnStringAtRandom(
							" A disgusted cry escapes from [npc2.namePos] mouth as the ball of saliva splatters onto [npc2.her] cheek,"
									+ (Sex.getCharacterPerformingAction().isPlayer()
											?" which is precisely the reaction you were looking for."
											:" and from the wicked grin that settles on [npc.namePos] face, this was just the reaction [npc.she] [npc.was] looking for."),
							" The horrified protestations which [npc2.name] immediately [npc2.verb(make)] is exactly what [npc.name] [npc.was] looking for, and [npc.she] can't help but grin as [npc.she] [npc.verb(see)] tears well up in"
									+ (Sex.getCharacterTargetedForSexAction(this).isPlayer()
											?" your [npc2.eyes+]."
											:" [npc.her] submissive bitch's [npc2.eyes+]."),
							" After letting out a shocked cry, tears start to well up in [npc2.namePos] [npc2.eyes], letting [npc.name] know that [npc.her] abuse is having the exact effect [npc.she] [npc.was] looking for."));

				} else {
					sb.append(UtilText.returnStringAtRandom(
							" A disgusted cry escapes from [npc2.namePos] mouth as the ball of saliva splatters onto [npc2.her] cheek,"
									+ (Sex.getCharacterPerformingAction().isPlayer()
											?" which is precisely the reaction you were looking for."
											:" and from the wicked grin that settles on [npc.namePos] face, this was just the reaction [npc.she] [npc.was] looking for."),
							" The horrified protestations which [npc2.name] immediately [npc2.verb(make)] is exactly what [npc.name] [npc.was] looking for, and [npc.she] can't help but grin as [npc.she] [npc.verb(see)] the shocked look in"
									+ (Sex.getCharacterTargetedForSexAction(this).isPlayer()
											?" your [npc2.eyes+]."
											:" [npc.her] submissive bitch's [npc2.eyes+]."),
							" [npc2.Name] immediately [npc2.verb(let)] out a shocked cry, revealing to [npc.name] that [npc.her] abuse is having the exact effect [npc.she] [npc.was] looking for."));
				}
			}

			return sb.toString();
		}
	};

	public static final SexAction CHOKE = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.TWO_LOW,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.FOUR_LUSTFUL,
			null,
			SexParticipantType.NORMAL,
			SexPace.DOM_ROUGH) {

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

		@Override
		public boolean isBaseRequirementsMet() {
			boolean mouthFinger = false;
			boolean mouthFingerReversed = false;
			try {
				mouthFinger = Sex.getPosition().getSlotTargets().get(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction())).get(Sex.getSexPositionSlot(Sex.getCharacterTargetedForSexAction(this)))
						.getInteractions().get(SexAreaPenetration.FINGER).contains(SexAreaOrifice.MOUTH);
			} catch(Exception ex) {
				// No available finger-mouth actions, so can't reach face
			}
			try {
				mouthFingerReversed = Sex.getPosition().getSlotTargets().get(Sex.getSexPositionSlot(Sex.getCharacterTargetedForSexAction(this))).get(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction()))
						.getInteractions().get(SexAreaOrifice.MOUTH).contains(SexAreaPenetration.FINGER);
			} catch(Exception ex) {
				// No available finger-mouth actions, so can't reach face
			}

			return SexAreaPenetration.FINGER.isFree(Sex.getCharacterPerformingAction())
					&& !Sex.isDom(Sex.getCharacterTargetedForSexAction(this))
					&& (mouthFinger || mouthFingerReversed)
					&& (Sex.getCharacterPerformingAction().isPlayer() || Sex.getCharacterPerformingAction().hasFetish(Fetish.FETISH_SADIST));
		}

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

		@Override
		public String getActionDescription() {
			return "Grab and squeeze [npc2.namePos] neck in order to choke [npc2.herHim].";
		}

		@Override
		public SexActionCategory getCategory() {
			return SexActionCategory.SEX;
		}

		@Override
		public String getDescription() {
			StringBuilder sb = new StringBuilder();

			if(Sex.getOngoingCharactersUsingAreas(Sex.getCharacterTargetedForSexAction(this), SexAreaOrifice.VAGINA, SexAreaPenetration.PENIS).contains(Sex.getCharacterPerformingAction())) {
				sb.append(UtilText.returnStringAtRandom(
						"Continuing to ram [npc.her] [npc.cock+] in and out of [npc2.namePos] [npc2.pussy+], [npc.name] [npc.verb(reach)] up to grab [npc2.her] neck, before squeezing down and choking [npc2.herHim].",
						"[npc.Name] [npc.verb(reach)] up to grab [npc2.namePos] neck, and, while still forcefully pounding [npc2.her] [npc2.pussy+], [npc.she] roughly [npc.verb(squeeze)] down in order to choke [npc2.herHim].",
						"Slamming [npc.her] [npc.cock+] deep into [npc2.namePos] [npc2.pussy+], [npc.name] [npc.verb(reach)] up and [npc.verb(grab)] [npc2.her] neck,"
								+ " squeezing down and choking [npc2.herHim] as [npc.she] [npc.verb(pull)] back and [npc.verb(continue)] roughly fucking [npc2.herHim]."));

			} else if(Sex.getOngoingCharactersUsingAreas(Sex.getCharacterTargetedForSexAction(this), SexAreaOrifice.ANUS, SexAreaPenetration.PENIS).contains(Sex.getCharacterPerformingAction())) {
				sb.append(UtilText.returnStringAtRandom(
						"Continuing to ram [npc.her] [npc.cock+] in and out of [npc2.namePos] [npc2.asshole+], [npc.name] [npc.verb(reach)] up to grab [npc2.her] neck, before squeezing down and choking [npc2.herHim].",
						"[npc.Name] [npc.verb(reach)] up to grab [npc2.namePos] neck, and, while still forcefully pounding [npc2.her] [npc2.asshole+], [npc.she] roughly [npc.verb(squeeze)] down in order to choke [npc2.herHim].",
						"Slamming [npc.her] [npc.cock+] deep into [npc2.namePos] [npc2.asshole+], [npc.name] [npc.verb(reach)] up and [npc.verb(grab)] [npc2.her] neck,"
								+ " squeezing down and choking [npc2.herHim] as [npc.she] [npc.verb(pull)] back and [npc.verb(continue)] roughly fucking [npc2.herHim]."));

			} else {
				sb.append(UtilText.returnStringAtRandom(
						"With a sadistic grin on [npc.her] face, [npc.name] [npc.verb(reach)] up to grab [npc2.name] by [npc2.her] neck, before squeezing down and choking [npc2.herHim].",
						"[npc.Name] [npc.verb(reach)] up to grab [npc2.namePos] neck, and, letting out a sadistic laugh, [npc.she] roughly [npc.verb(squeeze)] down in order to choke [npc2.herHim].",
						"With a cruel laugh, [npc.name] [npc.verb(reach)] up and [npc.verb(grab)] [npc2.name] by [npc2.her] neck, before sadistically squeezing down and choking [npc2.herHim]."));
			}

			if(Sex.getCharacterTargetedForSexAction(this).getFetishDesire(Fetish.FETISH_MASOCHIST).isPositive() && Sex.getSexPace(Sex.getCharacterTargetedForSexAction(this))!=SexPace.SUB_RESISTING) {
				sb.append(UtilText.returnStringAtRandom(
						" The gargled spluttering sounds which [npc2.name] [npc2.verb(start)] to make in response have a distinctly lewd quality to them, letting [npc.name] know that [npc2.sheIs] being turned on by this abuse.",
						" The horny, gargled [npc2.moans] which [npc2.name] immediately [npc2.verb(start)] to produce are more than enough to let anyone realise that [npc2.sheIs] a masochist, and is getting aroused by this poor treatment.",
						" The spluttering gasps which [npc2.name] [npc2.verb(begin)] to emit are intermingled with several horny [npc2.moans], letting [npc.name] know that [npc2.sheIs] deriving masochistic pleasure from being abused in such a fashion."));
			} else {
				if(Sex.getCharacterTargetedForSexAction(this).isFeminine()) {
					sb.append(UtilText.returnStringAtRandom(
							" A series of gargled spluttering noises escape from [npc2.namePos] mouth,"
									+ (Sex.getCharacterPerformingAction().isPlayer()
											?" which is precisely the reaction you were looking for."
											:" and from the wicked grin that settles on [npc.namePos] face, this was just the reaction [npc.she] [npc.was] looking for."),
							" The distressed, gargled cries which [npc2.name] [npc2.verb(start)] to make are exactly what [npc.name] [npc.was] looking for, and [npc.she] can't help but grin as [npc.she] [npc.verb(see)] tears well up in"
									+ (Sex.getCharacterTargetedForSexAction(this).isPlayer()
											?" your [npc2.eyes+]."
											:" [npc.her] submissive bitch's [npc2.eyes+]."),
							" Letting out a series of spluttering gasps, tears quickly start to well up in [npc2.namePos] [npc2.eyes], letting [npc.name] know that [npc.her] abuse is having the exact effect [npc.she] [npc.was] looking for."));

				} else {
					sb.append(UtilText.returnStringAtRandom(
							" A series of gargled spluttering noises escape from [npc2.namePos] mouth,"
									+ (Sex.getCharacterPerformingAction().isPlayer()
											?" which is precisely the reaction you were looking for."
											:" and from the wicked grin that settles on [npc.namePos] face, this was just the reaction [npc.she] [npc.was] looking for."),
							" The distressed, gargled cries which [npc2.name] immediately [npc2.verb(make)] is exactly what [npc.name] [npc.was] looking for, and [npc.she] can't help but grin as [npc.she] [npc.verb(see)] the shocked look in"
									+ (Sex.getCharacterTargetedForSexAction(this).isPlayer()
											?" your [npc2.eyes+]."
											:" [npc.her] submissive bitch's [npc2.eyes+]."),
							" [npc2.Name] immediately [npc2.verb(let)] out a spluttering gasp, revealing to [npc.name] that [npc.her] abuse is having the exact effect [npc.she] [npc.was] looking for."));
				}

			}

			return sb.toString();
		}
	};

}

repo_name: klorpa/Pedo

license: unknown


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

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.SexAreaOrifice;
import com.lilithsthrone.game.sex.SexAreaPenetration;
import com.lilithsthrone.game.sex.SexPace;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.positions.slots.SexSlotTag;
import com.lilithsthrone.game.sex.sexActions.SexAction;
import com.lilithsthrone.game.sex.sexActions.SexActionLimitation;
import com.lilithsthrone.game.sex.sexActions.SexActionType;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;

/**
 * @since 0.1.79
 * @version 0.3.5.5
 * @author Innoxia
 */
public class PartnerSelfFingerAnus {

	public static final SexAction PARTNER_SELF_FINGER_ANUS_SPREAD_ASS = new SexAction(
			SexActionType.REQUIRES_NO_PENETRATION_AND_EXPOSED,
			ArousalIncrease.TWO_LOW,
			ArousalIncrease.TWO_LOW,
			CorruptionLevel.ONE_VANILLA,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF) {

		@Override
		public boolean isBaseRequirementsMet() {
			return !Sex.getCharacterPerformingAction().isPlayer()
					&& Sex.getSexPace(Sex.getCharacterPerformingAction())!=SexPace.SUB_RESISTING;
		}

		@Override
		public String getActionTitle() {
			return "Spread ass";
		}

		@Override
		public String getActionDescription() {
			return "Use your [npc.hands] to spread your ass.";
		}

		@Override
		public String getDescription() {
			if(Sex.getSexPositionSlot(Sex.getCharacterPerformingAction()).hasTag(SexSlotTag.ALL_FOURS)) {
				return UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getTargetedPartner(Sex.getCharacterPerformingAction()),
						UtilText.returnStringAtRandom(
							"Reaching back with one [npc.hand], [npc.name] grabs [npc.her] [npc.ass+] and pulls to one side, letting out [npc.a_moan+] as [npc.she] presents [npc.her] [npc.asshole+] to [npc2.name].",
							"[npc.Name] reaches back with one [npc.hand], moaning softly as [npc.she] grabs hold of [npc.her] [npc.ass+], before invitingly pulling to one side and presenting [npc.her] [npc.asshole+] to [npc2.name].",
							"Sliding [npc.her] fingertips over [npc.her] [npc.asshole+],"
									+ " [npc.name] lets out [npc.a_moan+] as [npc.she] grabs one of [npc.her] [npc.assSize] ass cheeks and pulls to one one side in order to present [npc.her] [npc.asshole+] to [npc2.name].",
							"[npc.Name] eagerly slides [npc.her] [npc.fingers] over [npc.her] needy [npc.asshole],"
									+ " [npc.moaning+] as [npc.she] uses [npc.her] [npc.hand] to pull [npc.her] ass cheek to one side and present [npc.herself] for anal penetration."));
			} else {
				return UtilText.parse(Sex.getCharacterPerformingAction(), Sex.getTargetedPartner(Sex.getCharacterPerformingAction()),
						UtilText.returnStringAtRandom(
							"Reaching back with both [npc.hands], [npc.name] grabs [npc.her] [npc.assSize] ass cheeks and pulls them apart, letting out [npc.a_moan+] as [npc.she] presents [npc.her] [npc.asshole+] to [npc2.name].",
							"[npc.Name] reaches back with both [npc.hands], moaning softly as [npc.she] invitingly pulls [npc.her] [npc.assSize] ass cheeks apart and presents [npc.her] [npc.asshole+] to [npc2.name].",
							"Sliding [npc.her] fingertips over [npc.her] [npc.asshole+],"
									+ " [npc.name] lets out [npc.a_moan+] as [npc.she] grabs [npc.her] [npc.assSize] ass cheeks and pulls them apart in order to present [npc.her] [npc.asshole+] to [npc2.name].",
							"[npc.Name] eagerly slides [npc.her] [npc.fingers] over [npc.her] needy [npc.asshole], [npc.moaning+] as [npc.she] uses [npc.her] [npc.hands] to pull [npc.her] ass cheeks aside and present [npc.herself] for anal penetration."));
			}
		}
	};

	public static final SexAction PARTNER_SELF_FINGER_ANUS_PENETRATION = new SexAction(
			SexActionType.START_ONGOING,
			ArousalIncrease.THREE_NORMAL,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.TWO_HORNY,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Anal fingering (self)";
		}

		@Override
		public String getActionDescription() {
			return "Start fingering [npc.her] ass.";
		}

		@Override
		public String getDescription() {
			return UtilText.returnStringAtRandom(
					"Reaching around to [npc.her] [npc.ass], [npc.name] teases [npc.her] fingers over the entrance to [npc.her] [npc.asshole+], before pushing them inside and letting out [npc.a_moan+].",

					"[npc.Name] probes [npc.her] fingers down over [npc.her] [npc.ass], [npc.moaning+] as [npc.she] pushes two of [npc.her] digits into [npc.her] inviting [npc.asshole].",

					"Sliding [npc.her] fingertips over [npc.her] neglected [npc.asshole], [npc.name] lets out a [npc.groan+] as [npc.she] pushes [npc.her] digits inside.",

					"[npc.Name] eagerly pushes [npc.her] fingers into [npc.her] needy [npc.asshole], [npc.moaning+] as [npc.she] starts pumping [npc.her] digits in and out of [npc.her] [npc.ass].");
		}

	};

	public static final SexAction DOM_PARTNER_SELF_FINGER_ANUS_GENTLE = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.THREE_NORMAL,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.TWO_HORNY,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF,
			SexPace.DOM_GENTLE) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Gentle anal fingering (self)";
		}

		@Override
		public String getActionDescription() {
			return "Gently finger [npc.her] [npc.asshole].";
		}

		@Override
		public String getDescription() {
			return UtilText.returnStringAtRandom(
					"[npc.A_moan+] escapes from between [npc.namePos] [npc.lips+] as [npc.she] slowly pushes [npc.her] [npc.fingers] deep inside [npc.her] [npc.asshole+].",

					"Gently pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+], [npc.name] starts letting out a series of delighted [npc.moans] as [npc.she] rhythmically fingers [npc.her] [npc.ass].",

					"Curling [npc.her] [npc.fingers] up inside [npc.her] [npc.asshole], [npc.name] lets out a little whimper as [npc.she] starts "
							+(Sex.getCharacterPerformingAction().hasPenis() && !Sex.getCharacterPerformingAction().hasVagina()?"gently stroking [npc.her] prostate.":"gently fingering [npc.her] [npc.ass+]."),

					"Focusing on pleasuring [npc.her] [npc.ass+], [npc.name] starts gently pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+].");
		}

	};

	public static final SexAction DOM_PARTNER_SELF_FINGER_ANUS_NORMAL = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.THREE_NORMAL,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.TWO_HORNY,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF,
			SexPace.DOM_NORMAL) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Anal fingering (self)";
		}

		@Override
		public String getActionDescription() {
			return "Concentrate on fingering [npc.her] [npc.ass].";
		}

		@Override
		public String getDescription() {
			return UtilText.returnStringAtRandom(
					"[npc.A_moan+] escapes from between [npc.namePos] [npc.lips+] as [npc.she] greedily pushes [npc.her] [npc.fingers] deep inside [npc.her] [npc.asshole+].",

					"Pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+], [npc.name] starts letting out a series of delighted [npc.moans] as [npc.she] rhythmically fingers [npc.her] [npc.ass].",

					"Curling [npc.her] [npc.fingers] up inside [npc.her] [npc.asshole], [npc.name] lets out [npc.a_moan] as [npc.she] starts "
							+(Sex.getCharacterPerformingAction().hasPenis() && !Sex.getCharacterPerformingAction().hasVagina()?"stroking [npc.her] prostate.":"fingering [npc.her] [npc.ass+]."),

					"Focusing on pleasuring [npc.her] [npc.ass+], [npc.name] starts pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+].");
		}

	};

	public static final SexAction DOM_PARTNER_SELF_FINGER_ANUS_ROUGH = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.THREE_NORMAL,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.THREE_DIRTY,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF,
			SexPace.DOM_ROUGH) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Rough anal fingering (self)";
		}

		@Override
		public String getActionDescription() {
			return "Roughly finger [npc.her] [npc.ass].";
		}

		@Override
		public String getDescription() {
			return UtilText.returnStringAtRandom(
					"[npc.A_moan+] escapes from between [npc.namePos] [npc.lips+] as [npc.she] roughly [npc.verb(slam)] [npc.her] [npc.fingers] deep inside [npc.her] [npc.asshole+], before starting to roughly finger [npc.her] [npc.ass].",

					"Roughly pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+], [npc.name] [npc.verb(start)] letting out a series of delighted [npc.moans] as [npc.she] ruthlessly fingers [npc.her] own [npc.ass].",

					"Forcefully curling [npc.her] [npc.fingers] up inside [npc.her] [npc.asshole], [npc.name] lets out [npc.a_moan] as [npc.she] [npc.verb(start)] "
							+(Sex.getCharacterPerformingAction().hasPenis() && !Sex.getCharacterPerformingAction().hasVagina()
									?"roughly grinding [npc.her] fingertips up against [npc.her] prostate."
									:"roughly grinding [npc.her] digits in and out of [npc.her] [npc.ass+]."),

					"Focusing on pleasuring [npc.her] [npc.ass+], [npc.name] [npc.verb(start)] roughly slamming [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+].");
		}

	};

	public static final SexAction SUB_PARTNER_SELF_FINGER_ANUS_NORMAL = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.THREE_NORMAL,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.TWO_HORNY,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF,
			SexPace.SUB_NORMAL) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Anal fingering (self)";
		}

		@Override
		public String getActionDescription() {
			return "Concentrate on fingering [npc.her] [npc.ass].";
		}

		@Override
		public String getDescription() {
			return UtilText.returnStringAtRandom(
					"[npc.A_moan+] escapes from between [npc.namePos] [npc.lips+] as [npc.she] greedily pushes [npc.her] [npc.fingers] deep inside [npc.her] [npc.asshole+].",

					"Pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+], [npc.name] starts letting out a series of delighted [npc.moans] as [npc.she] rhythmically fingers [npc.her] [npc.ass].",

					"Curling [npc.her] [npc.fingers] up inside [npc.her] [npc.asshole], [npc.name] lets out [npc.a_moan] as [npc.she] starts "
							+(Sex.getCharacterPerformingAction().hasPenis() && !Sex.getCharacterPerformingAction().hasVagina()?"stroking [npc.her] prostate.":"fingering [npc.her] [npc.ass+]."),

					"Focusing on pleasuring [npc.her] [npc.ass+], [npc.name] starts pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+].");
		}

	};

	public static final SexAction SUB_PARTNER_SELF_FINGER_ANUS_EAGER = new SexAction(
			SexActionType.ONGOING,
			ArousalIncrease.THREE_NORMAL,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.THREE_DIRTY,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF,
			SexPace.SUB_EAGER) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Eager anal fingering (self)";
		}

		@Override
		public String getActionDescription() {
			return "Eagerly finger [npc.her] [npc.ass].";
		}

		@Override
		public String getDescription() {
			return UtilText.returnStringAtRandom(
					"[npc.A_moan+] escapes from between [npc.namePos] [npc.lips+] as [npc.she] eagerly slams [npc.her] [npc.fingers] deep inside [npc.her] [npc.asshole+], before starting to desperately finger [npc.her] [npc.ass].",

					"Enthusiastically pumping [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+], [npc.name] starts letting out a series of delighted [npc.moans] as [npc.she] frantically fingers [npc.her] own [npc.ass].",

					"Desperately curling [npc.her] [npc.fingers] up inside [npc.her] [npc.asshole], [npc.name] lets out [npc.a_moan] as [npc.she] starts "
							+(Sex.getCharacterPerformingAction().hasPenis() && !Sex.getCharacterPerformingAction().hasVagina()
									?"eagerly grinding [npc.her] fingertips up against [npc.her] prostate."
									:"eagerly grinding [npc.her] digits in and out of [npc.her] [npc.ass+]."),

					"Focusing on pleasuring [npc.her] [npc.ass+], [npc.name] eagerly starts slamming [npc.her] [npc.fingers] in and out of [npc.her] [npc.asshole+].");
		}

	};

	public static final SexAction PARTNER_SELF_FINGER_ANUS_STOP_PENETRATION = new SexAction(
			SexActionType.STOP_ONGOING,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.ANUS)),
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Stop anal fingering (self)";
		}

		@Override
		public String getActionDescription() {
			return "Stop fingering [npc.her] ass.";
		}

		@Override
		public String getDescription() {
			return "With [npc.a_groan+], [npc.name] slides [npc.her] fingers out of [npc.her] [npc.asshole+].";
		}
	};
}


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

import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.character.body.CoverableArea;
import com.lilithsthrone.game.character.body.valueEnums.Lactation;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.sex.ArousalIncrease;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexAreaOrifice;
import com.lilithsthrone.game.sex.SexAreaPenetration;
import com.lilithsthrone.game.sex.SexPace;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.sexActions.SexAction;
import com.lilithsthrone.game.sex.sexActions.SexActionType;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;

/**
 * @since 0.3.4.5
 * @version 0.3.4.5
 * @author Innoxia
 */
public class PartnerSelfFingerBreasts {

	public static final SexAction FEEL_BREASTS = new SexAction(
			SexActionType.REQUIRES_NO_PENETRATION,
			ArousalIncrease.TWO_LOW,
			ArousalIncrease.THREE_NORMAL,
			CorruptionLevel.ZERO_PURE,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.BREAST)),
			SexParticipantType.SELF) {

		@Override
		public String getActionTitle() {
			return "Grope breasts (self)";
		}

		@Override
		public String getActionDescription() {
			return "Squeeze and grope your own [npc.breasts+].";
		}

		@Override
		public boolean isBaseRequirementsMet() {
			return Sex.getCharacterPerformingAction().hasBreasts()
					&& Sex.getSexPace(Sex.getCharacterPerformingAction())!=SexPace.SUB_RESISTING
					 && !Sex.getCharacterPerformingAction().isPlayer();
		}

		@Override
		public String getDescription() {
			if(!Sex.getCharacterPerformingAction().isCoverableAreaExposed(CoverableArea.NIPPLES)){
				UtilText.nodeContentSB.setLength(0);

				switch(Sex.getSexPace(Sex.getCharacterPerformingAction())) {
					case DOM_GENTLE:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] chest, [npc.name] [npc.verb(let)] out a soft [npc.moan] as [npc.she] [npc.verb(start)] fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " softly pressing [npc.her] [npc.lowClothing(NIPPLES)] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to gently press [npc.her] [npc.hands+]"
									+ " against [npc.her] [npc.topClothing(NIPPLES)], before starting to softly grope and squeeze [npc.her] chest.",
								"Teasing [npc.her] [npc.fingers] over [npc.her] [npc.topClothing(NIPPLES)],"
										+ " [npc.name] [npc.verb(start)] softly [npc.moaning] and sighing as [npc.she] gently [npc.verb(fondle)] and [npc.verb(grope)] [npc.her] own [npc.breastRows] [npc.breasts+]."));
						break;
					case DOM_ROUGH:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] chest, [npc.name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(start)] roughly fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " forcefully grinding [npc.her] [npc.lowClothing(NIPPLES)] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to forcefully grind [npc.her] [npc.hands+]"
									+ " against [npc.her] [npc.topClothing(NIPPLES)], before starting to roughly grope and squeeze [npc.her] chest.",
								"Running [npc.her] [npc.fingers] over [npc.her] [npc.topClothing(NIPPLES)],"
										+ " [npc.name] [npc.verb(start)] loudly panting and [npc.moaning] as [npc.she] roughly [npc.verb(fondle)] and [npc.verb(grope)] [npc.her] own [npc.breastRows] [npc.breasts+]."));
						break;
					case SUB_NORMAL:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] chest, [npc.name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(start)] fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " pressing [npc.her] [npc.lowClothing(NIPPLES)] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to press [npc.her] [npc.hands+]"
									+ " against [npc.her] [npc.topClothing(NIPPLES)], before starting to grope and squeeze [npc.her] chest.",
								"Running [npc.her] [npc.fingers] over [npc.her] [npc.topClothing(NIPPLES)],"
										+ " [npc.name] [npc.verb(start)] panting and [npc.moaning] as [npc.she] [npc.verb(fondle)] and [npc.verb(grope)] [npc.her] own [npc.breastRows] [npc.breasts+]."));
						break;
					default: // Dom normal and sub eager:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] chest, [npc.name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(start)] eagerly fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " desperately pressing [npc.her] [npc.lowClothing(NIPPLES)] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to eagerly press [npc.her] [npc.hands+]"
									+ " against [npc.her] [npc.topClothing(NIPPLES)], before starting to frantically grope and squeeze [npc.her] chest.",
								"Running [npc.her] [npc.fingers] over [npc.her] [npc.topClothing(NIPPLES)],"
										+ " [npc.name] [npc.verb(start)] desperately panting and [npc.moaning] as [npc.she] eagerly [npc.verb(fondle)] and [npc.verb(grope)] [npc.her] own [npc.breastRows] [npc.breasts+]."));
						break;
				}

				switch (Sex.getCharacterPerformingAction().getBreastStoredMilk()) {
					case ONE_TRICKLE:
						UtilText.nodeContentSB.append(" A small trickle of [npc.milk] leaks out into [npc.her] [npc.lowClothing(NIPPLES)] as [npc.she] [npc.verb(squeeze)] down on [npc.her] [npc.nipples+].");
						break;
					case TWO_SMALL_AMOUNT:
						UtilText.nodeContentSB.append(" A small squirt of [npc.milk] leaks out into [npc.her] [npc.lowClothing(NIPPLES)] as [npc.she] [npc.verb(squeeze)] down on [npc.her] [npc.nipples+].");
						break;
					case THREE_DECENT_AMOUNT:
						UtilText.nodeContentSB.append(" A trickle of [npc.milk] runs out into [npc.her] [npc.lowClothing(NIPPLES)] as [npc.she] [npc.verb(squeeze)] down on [npc.her] [npc.nipples+].");
						break;
					case FOUR_LARGE_AMOUNT:
						UtilText.nodeContentSB.append(" Almost immediately, [npc.her] [npc.milk] [npc.verb(start)] to flow out of [npc.her] [npc.nipples+] and into [npc.her] [npc.lowClothing(NIPPLES)].");
						break;
					case FIVE_VERY_LARGE_DROOLING:
						UtilText.nodeContentSB.append(" [npc.Her] [npc.milk] quickly [npc.verb(start)] streaming out into [npc.her] [npc.lowClothing(NIPPLES)] as [npc.she] [npc.verb(squeeze)] down on [npc.her] [npc.nipples+].");
						break;
					case SIX_EXTREME_AMOUNT_DRIPPING:
						UtilText.nodeContentSB.append(" [npc.Her] [npc.milk] immediately [npc.verb(start)] pouring out in a constant stream, quickly soaking [npc.her] [npc.lowClothing(NIPPLES)].");
						break;
					case SEVEN_MONSTROUS_AMOUNT_POURING:
						UtilText.nodeContentSB.append(" [npc.Her] [npc.milk] immediately [npc.verb(start)] pouring out in a heavy flow, quickly soaking [npc.her] [npc.lowClothing(NIPPLES)].");
						break;
					default:
						break;
				}

				return UtilText.nodeContentSB.toString();

			} else {

				UtilText.nodeContentSB.setLength(0);

				switch(Sex.getSexPace(Sex.getCharacterPerformingAction())) {
					case DOM_GENTLE:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] exposed chest, [npc.name] [npc.verb(let)] out a soft [npc.moan] as [npc.she] [npc.verb(start)] fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " softly pressing [npc.her] [npc.fingers+] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to gently press [npc.her] [npc.hands+]"
									+ " against [npc.her] exposed chest, before starting to softly grope and squeeze [npc.her] [npc.breastRows] [npc.breasts].",
								"Teasing [npc.her] [npc.fingers] over [npc.her] exposed [npc.breastRows] [npc.breasts+],"
										+ " [npc.name] [npc.verb(start)] gently [npc.moaning] and sighing as [npc.she] [npc.verb(begin)] to gently fondle and grope them."));
						break;
					case DOM_ROUGH:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] exposed chest, [npc.name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(start)] roughly fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " forcefully grinding [npc.her] [npc.fingers+] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to forcefully grind [npc.her] [npc.hands+]"
									+ " against [npc.her] exposed chest, before starting to roughly grope and squeeze [npc.her] [npc.breastRows] [npc.breasts].",
								"Running [npc.her] [npc.fingers] over [npc.her] exposed [npc.breastRows] [npc.breasts+],"
										+ " [npc.name] [npc.verb(start)] loudly panting and [npc.moaning] as [npc.she] [npc.verb(begin)] to roughly fondle and grope them."));
						break;
					case SUB_NORMAL:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] exposed chest, [npc.name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(start)] fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " pressing [npc.her] [npc.fingers+] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to press [npc.her] [npc.hands+]"
									+ " against [npc.her] exposed chest, before starting to grope and squeeze [npc.her] [npc.breastRows] [npc.breasts].",
								"Running [npc.her] [npc.fingers] over [npc.her] exposed [npc.breastRows] [npc.breasts+],"
										+ " [npc.name] [npc.verb(start)] panting and [npc.moaning] as [npc.she] [npc.verb(begin)] to fondle and grope them."));
						break;
					default: // Dom normal and sub eager:
						UtilText.nodeContentSB.append(UtilText.returnStringAtRandom(
								"Reaching up to [npc.her] exposed chest, [npc.name] [npc.verb(let)] out [npc.a_moan+] as [npc.she] [npc.verb(start)] eagerly fondling and groping [npc.her] [npc.breastRows] [npc.breasts+],"
										+ " desperately pressing [npc.her] [npc.fingers+] down against [npc.her] [npc.nipples+] in the process.",
								"Turned on and desperate to have [npc.her] [npc.breasts+] toyed with, [npc.name] [npc.verb(reach)] up to eagerly press [npc.her] [npc.hands+]"
									+ " against [npc.her] exposed chest, before starting to frantically grope and squeeze [npc.her] [npc.breastRows] [npc.breasts].",
								"Running [npc.her] [npc.fingers] over [npc.her] exposed [npc.breastRows] [npc.breasts+],"
										+ " [npc.name] [npc.verb(start)] desperately panting and [npc.moaning] as [npc.she] [npc.verb(begin)] to eagerly fondle and grope them."));
						break;
				}

				switch (Sex.getCharacterPerformingAction().getBreastStoredMilk()) {
					case ONE_TRICKLE:
						UtilText.nodeContentSB.append(" A small trickle of [npc.milk] leaks out onto [npc.her] [npc.fingers] as [npc.she] [npc.verb(squeeze)] down on [npc.namePos] [npc.nipples+].");
						break;
					case TWO_SMALL_AMOUNT:
						UtilText.nodeContentSB.append(" A small squirt of [npc.milk] leaks out onto [npc.her] [npc.fingers] as [npc.she] [npc.verb(squeeze)] down on [npc.namePos] [npc.nipples+].");
						break;
					case THREE_DECENT_AMOUNT:
						UtilText.nodeContentSB.append(" A trickle of [npc.milk] runs out over [npc.her] [npc.fingers] as [npc.she] [npc.verb(squeeze)] down on [npc.namePos] [npc.nipples+].");
						break;
					case FOUR_LARGE_AMOUNT:
						UtilText.nodeContentSB.append(" Almost immediately, [npc.her] [npc.milk] starts to flow out over [npc.her] [npc.fingers],"
								+ " and [npc.she] [npc.moansVerb] as [npc.she] [npc.verb(feel)] it running down over [npc.her] [npc.breasts+].");
						break;
					case FIVE_VERY_LARGE_DROOLING:
						UtilText.nodeContentSB.append(" [npc.Her] [npc.milk] quickly [npc.verb(start)] drooling out in a little stream over [npc.her] [npc.fingers] as [npc.she] [npc.verb(squeeze)] down on [npc.her] [npc.nipples+].");
						break;
					case SIX_EXTREME_AMOUNT_DRIPPING:
						UtilText.nodeContentSB.append(" [npc.Her] [npc.milk] immediately [npc.verb(start)] pouring out in a constant stream, quickly soaking [npc.her] [npc.breasts+] and dripping down onto the floor beneath [npc.herHim].");
						break;
					case SEVEN_MONSTROUS_AMOUNT_POURING:
						UtilText.nodeContentSB.append(" [npc.Her] [npc.milk] [npc.verb(start)] pouring out in a heavy flow, quickly soaking [npc.her] [npc.breasts+] and dripping down to form a large pool on the floor beneath [npc.herHim].");
						break;
					default:
						break;
				}

				return UtilText.nodeContentSB.toString();

			}
		}

		@Override
		public void applyEffects(){
			if(!Sex.getCharacterPerformingAction().isCoverableAreaExposed(CoverableArea.NIPPLES)
					&& Sex.getCharacterPerformingAction().getBreastStoredMilk().getMinimumValue()>=Lactation.ONE_TRICKLE.getMinimumValue()
					&& Sex.getCharacterPerformingAction().getLowestZLayerCoverableArea(CoverableArea.NIPPLES)!=null) {
				Sex.getCharacterPerformingAction().getLowestZLayerCoverableArea(CoverableArea.NIPPLES).setDirty(Sex.getCharacterPerformingAction(), true);
			}
		}

		@Override
		public String applyEffectsString() {
			return Sex.getCharacterPerformingAction().incrementBreastStoredMilk(-10);
		}

	};
}


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

import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.character.npc.NPC;
import com.lilithsthrone.game.sex.ArousalIncrease;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexAreaOrifice;
import com.lilithsthrone.game.sex.SexAreaPenetration;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.sexActions.SexAction;
import com.lilithsthrone.game.sex.sexActions.SexActionLimitation;
import com.lilithsthrone.game.sex.sexActions.SexActionType;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;

/**
 * @since 0.1.79
 * @version 0.1.88
 * @author Innoxia
 */
public class PartnerSelfFingerMouth {

	public static final SexAction PARTNER_SELF_FINGER_MOUTH_LUBRICATION = new SexAction(
			SexActionType.REQUIRES_NO_PENETRATION_AND_EXPOSED,
			ArousalIncrease.ZERO_NONE,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.MOUTH)),
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public boolean isBaseRequirementsMet() {
			return !Sex.hasLubricationTypeFromAnyone(Sex.getCharacterPerformingAction(), SexAreaPenetration.FINGER)
					&& Sex.isInForeplay(Sex.getCharacterPerformingAction())
					&& (Sex.getForeplayPreference((NPC) Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this))!=null
						&& Sex.getForeplayPreference((NPC) Sex.getCharacterPerformingAction(), Sex.getCharacterTargetedForSexAction(this)).getPerformingSexArea()==SexAreaPenetration.FINGER);
		}

		@Override
		public String getActionTitle() {
			return "Lubricate fingers";
		}

		@Override
		public String getActionDescription() {
			return "Lubricate your fingers with your saliva.";
		}

		@Override
		public String getDescription() {
			return "Lifting [npc.her] [npc.hand] up to [npc.her] mouth, [npc.name] slides [npc.her] [npc.fingers] past [npc.her] [npc.lips+],"
					+ " before lewdly sucking on them for a moment in order to get them well lubricated with [npc.her] saliva.";
		}

		@Override
		public void applyEffects() {
			Sex.transferLubrication(Sex.getCharacterPerformingAction(), SexAreaPenetration.FINGER, Sex.getCharacterPerformingAction(), SexAreaOrifice.MOUTH);
		}
	};

	public static final SexAction PARTNER_SELF_FINGER_MOUTH_PENETRATION = new SexAction(
			SexActionType.START_ONGOING,
			ArousalIncrease.ZERO_NONE,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.MOUTH)),
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Suck fingers (self)";
		}

		@Override
		public String getActionDescription() {
			return "Put your [npc.fingers] in your mouth and start sucking them.";
		}

		@Override
		public String getDescription() {
			return "Lifting [npc.her] [npc.hand] up to [npc.her] mouth, [npc.name] slides [npc.her] [npc.fingers] past [npc.her] [npc.lips+] before starting to lewdly suck on [npc.her] intruding digits.";
		}
	};

	public static final SexAction PARTNER_SELF_FINGER_MOUTH_STOP_PENETRATION = new SexAction(
			SexActionType.STOP_ONGOING,
			ArousalIncrease.ONE_MINIMUM,
			ArousalIncrease.ONE_MINIMUM,
			CorruptionLevel.ZERO_PURE,
			Util.newHashMapOfValues(new Value<>(SexAreaPenetration.FINGER, SexAreaOrifice.MOUTH)),
			SexParticipantType.SELF) {
		@Override
		public SexActionLimitation getLimitation() {
			return SexActionLimitation.NPC_ONLY;
		}

		@Override
		public String getActionTitle() {
			return "Stop sucking fingers (self)";
		}

		@Override
		public String getActionDescription() {
			return "Stop sucking your [npc.fingers].";
		}

		@Override
		public String getDescription() {
			return "With a little sigh, [npc.name] slides [npc.her] saliva-coated [npc.fingers] out of [npc.her] mouth.";
		}
	};

}