code:
package com.lilithsthrone.game.character.npc.dominion;

import java.time.Month;
import java.util.List;

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

import com.lilithsthrone.game.Game;
import com.lilithsthrone.game.character.CharacterImportSetting;
import com.lilithsthrone.game.character.EquipClothingSetting;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.Covering;
import com.lilithsthrone.game.character.body.types.BodyCoveringType;
import com.lilithsthrone.game.character.body.valueEnums.AssSize;
import com.lilithsthrone.game.character.body.valueEnums.CupSize;
import com.lilithsthrone.game.character.body.valueEnums.HairLength;
import com.lilithsthrone.game.character.body.valueEnums.HipSize;
import com.lilithsthrone.game.character.fetishes.Fetish;
import com.lilithsthrone.game.character.gender.Gender;
import com.lilithsthrone.game.character.npc.NPC;
import com.lilithsthrone.game.character.persona.NameTriplet;
import com.lilithsthrone.game.character.persona.PersonalityTrait;
import com.lilithsthrone.game.character.persona.SexualOrientation;
import com.lilithsthrone.game.character.race.RaceStage;
import com.lilithsthrone.game.character.race.Subspecies;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.companions.SlaveDialogue;
import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.game.dialogue.responses.ResponseEffectsOnly;
import com.lilithsthrone.game.dialogue.responses.ResponseSex;
import com.lilithsthrone.game.dialogue.responses.ResponseTag;
import com.lilithsthrone.game.dialogue.utils.InventoryInteraction;
import com.lilithsthrone.game.inventory.CharacterInventory;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
import com.lilithsthrone.game.inventory.clothing.AbstractClothingType;
import com.lilithsthrone.game.inventory.clothing.ClothingType;
import com.lilithsthrone.game.sex.Sex;
import com.lilithsthrone.game.sex.SexPace;
import com.lilithsthrone.game.sex.managers.universal.SMAllFours;
import com.lilithsthrone.game.sex.managers.universal.SMGeneric;
import com.lilithsthrone.game.sex.positions.slots.SexSlotAllFours;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;
import com.lilithsthrone.world.WorldType;
import com.lilithsthrone.world.places.PlaceType;

/**
 * Test class that I'm using to try out some methods and stuff. It might end up as a bit of a mess, but don't remove it.
 *
 * @since 0.1.83
 * @version 0.3.4
 * @author Innoxia
 */
public class TestNPC extends NPC {

	public TestNPC() {
		this(false);
	}

	public TestNPC(boolean isImported) {
		super(isImported, new NameTriplet("TestNPC"), "Scriven",
				"A mysterious [test.race] that you found in the back of one of the Shopping Arcade's many shops.",
				28, Month.JUNE, 1,
				1, Gender.F_V_B_FEMALE, Subspecies.CAT_MORPH, RaceStage.PARTIAL_FULL,
				new CharacterInventory(10), WorldType.EMPTY, PlaceType.GENERIC_EMPTY_TILE, true);

		if(!isImported) {
			String s = "";
			int[] nameArray = new int[] {78, 111, 120, 105, 110, 105, 97};
			for(int c : nameArray) {
				s+=(char)c;
			}
			this.setName(new NameTriplet(s));

			this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.GROIN_PANTIES, Colour.CLOTHING_WHITE, false), true, this);
			this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.CHEST_FULLCUP_BRA, Colour.CLOTHING_WHITE, false), true, this);
			this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.TORSO_OVER_HOODIE, Colour.CLOTHING_PINK_LIGHT, false), true, this);
			this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.LEG_HOTPANTS, Colour.CLOTHING_BLACK, false), true, this);
			this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_sock_thighhigh_socks", Colour.CLOTHING_WHITE, false), true, this);
			this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.HEAD_HEADBAND, Colour.CLOTHING_BLACK, false), true, this);
			this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.EYES_GLASSES, Colour.CLOTHING_BLACK_STEEL, false), true, this);
		}

		this.setEnslavementDialogue(SlaveDialogue.DEFAULT_ENSLAVEMENT_DIALOGUE, true);
	}

	@Override
	public void loadFromXML(Element parentElement, Document doc, CharacterImportSetting... settings) {
		loadNPCVariablesFromXML(this, null, parentElement, doc, settings);
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.5.1")) {
			this.setPersonalityTraits(
					PersonalityTrait.KIND,
					PersonalityTrait.SHY,
					PersonalityTrait.INNOCENT);
		}
	}

	@Override
	public void setStartingBody(boolean setPersona) {
		if(setPersona) {
			this.setPersonalityTraits(
					PersonalityTrait.KIND,
					PersonalityTrait.SHY,
					PersonalityTrait.INNOCENT);

			this.setSexualOrientation(SexualOrientation.AMBIPHILIC);

			this.addFetish(Fetish.FETISH_ANAL_RECEIVING);
			this.addFetish(Fetish.FETISH_BREASTS_SELF);
			this.addFetish(Fetish.FETISH_CUM_ADDICT);
			this.addFetish(Fetish.FETISH_DOMINANT);
			this.addFetish(Fetish.FETISH_INCEST);
			this.addFetish(Fetish.FETISH_MASOCHIST);
			this.addFetish(Fetish.FETISH_MASTURBATION);
			this.addFetish(Fetish.FETISH_NON_CON_SUB);
			this.addFetish(Fetish.FETISH_ORAL_GIVING);
			this.addFetish(Fetish.FETISH_ORAL_RECEIVING);
			this.addFetish(Fetish.FETISH_PREGNANCY);
			this.addFetish(Fetish.FETISH_PURE_VIRGIN);
			this.addFetish(Fetish.FETISH_SUBMISSIVE);
			this.addFetish(Fetish.FETISH_TRANSFORMATION_RECEIVING);
		}

		this.setEyeCovering(new Covering(BodyCoveringType.EYE_FELINE, Colour.EYE_GREEN));
		this.setHairCovering(new Covering(BodyCoveringType.HAIR_FELINE_FUR, Colour.COVERING_BROWN), true);
		this.setSkinCovering(new Covering(BodyCoveringType.FELINE_FUR, Colour.COVERING_BROWN), true);
		this.setSkinCovering(new Covering(BodyCoveringType.HUMAN, Colour.SKIN_LIGHT), true);

		this.setFemininity(75);

		this.setHeight(170);

		this.setHairLength(HairLength.FOUR_MID_BACK.getMedianValue());

		this.setAssSize(AssSize.THREE_NORMAL.getValue());

		this.setBreastSize(CupSize.B.getMeasurement());

		this.setHipSize(HipSize.FOUR_WOMANLY.getValue());

		this.setAssVirgin(true);
		this.setNippleVirgin(true);
		this.setVaginaVirgin(true);
		this.setFaceVirgin(true);
	}

	@Override
	public void equipClothing(List<EquipClothingSetting> settings) {
		this.resetInventory(true);

		this.setPiercedEar(true);

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.GROIN_PANTIES, Colour.CLOTHING_WHITE, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.CHEST_FULLCUP_BRA, Colour.CLOTHING_WHITE, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.TORSO_OVER_HOODIE, Colour.CLOTHING_PINK_LIGHT, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.LEG_HOTPANTS, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_sock_thighhigh_socks", Colour.CLOTHING_WHITE, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.HEAD_HEADBAND, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.EYES_GLASSES, Colour.CLOTHING_BLACK, false), true, this);
	}

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

	@Override
	public void changeFurryLevel(){
	}

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

	@Override
	public Value<Boolean, String> isInventoryEquipAllowed(AbstractClothing clothing, InventorySlot slotToEquipTo) {
		return new Value<>(true, "");
	}

	@Override
	public DialogueNode getEncounterDialogue() {
		return null;
	}

	@Override
	public void endSex() {
		if(!isSlave()) {
			setPendingClothingDressing(true);
		}
	}

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

	@Override
	public boolean isAttractedTo(GameCharacter character) {
		return getSexPaceSubPreference(Main.game.getPlayer())!=SexPace.SUB_RESISTING;
	}

	public static boolean resisting = true;

	@Override
	public SexPace getSexPaceSubPreference(GameCharacter character){
		if(resisting && Main.game.isNonConEnabled()) {
			return SexPace.SUB_RESISTING;
		} else {
			return SexPace.SUB_EAGER;
		}
	}

	public static final DialogueNode TEST_DIALOGUE = new DialogueNode("Shopping arcade", ".", true) {

		@Override
		public int getSecondsPassed() {
			return 15*60;
		}

		@Override
		public String getContent() {
			return "
"
						+ "Reaching the back of the shop, you finish your exploration of this store's wares, finding that there's nothing in here that's worth buying."
						+ " As you turn to make your exit, you see that a door marked '[test.namePos] room: Private!' has been left slightly ajar, and you hear a frustrated little exclamation come out of it,"
						+ " [test.speech(Argh! It shouldn't be like that... I'll have to re-write it...)]"
					+ "
"
					+ "
"
						+ "You could take a closer look, or carry on your way..."
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				return new Response("Step inside", "Slip through the open door, careful not to alert the occupant of your presence.", TEST_DIALOGUE_ENTER) {
					@Override
					public void effects() {
						TestNPC.resisting = true;
					}
				};

			} else if(index==0) {
				return new Response("Leave", "The door is marked 'private' after all...", TEST_DIALOGUE) {
					@Override
					public DialogueNode getNextDialogue(){
						return Main.game.getActiveWorld().getCell(Main.game.getPlayer().getLocation()).getPlace().getDialogue(false);
					}
				};

			} else {
				return null;
			}
		}
	};

	public static final DialogueNode TEST_DIALOGUE_ENTER = new DialogueNode("A cozy room", ".", true, true) {

		@Override
		public int getSecondsPassed() {
			return 15*60;
		}

		@Override
		public String getContent() {
			return "
"
						+ "You step forwards, and, quietly pushing the door open, you step into a cozy little back room."
						+ " In front of you, with [test.her] back to the door, a small-framed [test.race] is sitting in front of a desk."
						+ " Scrunched up pieces of paper litter the floor around [test.her], and you notice that [test.sheIs] so engrossed in [test.her] writing that [test.she] didn't hear your entry."
					+ "
"
					+ "
"
						+ "You could greet [test.herHim] and try to find out what [test.sheIs] writing, or step back outside and leave [test.herHim] to [test.her] work."
						+ (Main.game.isNonConEnabled()
								?" As you realise how vulnerable [test.she] is back here, far from help, a dark thought flashes through your mind, and you realise that there's always another option..."
								:"")
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				return new Response("Say hello", "Say hello and ask [test.herHim] what [test.sheIs] doing.", TEST_DIALOGUE_GREET) {
						@Override
						public void effects() {
							TestNPC.resisting = false;
						}
					};

			} else if (index == 2 && Main.game.isNonConEnabled()) {
				return new ResponseSex("Rape [test.herHim]", "[test.Her] back is turned, and [test.she] hasn't noticed you enter the room...",
						Util.newArrayListOfValues(Fetish.FETISH_NON_CON_DOM), null, Fetish.FETISH_NON_CON_DOM.getAssociatedCorruptionLevel(), null, null, null,
						false, false,
						new SMAllFours(
								Util.newHashMapOfValues(new Value<>(Main.game.getPlayer(), SexSlotAllFours.BEHIND)),
								Util.newHashMapOfValues(new Value<>(Main.game.getNpc(TestNPC.class), SexSlotAllFours.ALL_FOURS))) {
							@Override
							public boolean isPublicSex() {
								return false;
							}
						},
						null,
						null, TEST_DIALOGUE_AFTER_RAPE, "
"
							+ "You quietly close the door and lock it behind you. After all, you wouldn't want anyone interrupting your fun."
							+ " Stealthily moving forwards, you approach the oblivious [test.race], smiling to yourself as you hear [test.her] grumbling and mumbling to [test.herself]."
						+ "
"
						+ "
"
							+ "You're able to sneak right up behind [test.herHim] without [test.herHim] noticing, and, in one swift movement, you grab [test.her] shoulders and throw [test.herHim] to the floor."
						+ "
"
						+ "
"
							+ "[test.speech(Aaaah!)] [test.she] screams, falling down onto all fours."
						+ "
"
						+ "
"
							+ "You drop down behind [test.herHim], pushing [test.her] weak body into the floor as you growl into [test.her] [test.ear+],"
							+ " [pc.speech(Scream all you want, you're mine now!)]"
						+ "
");

			} else if (index == 3 && Main.game.isNonConEnabled()) {
				return new ResponseSex("Rape [test.herHim] (gentle)", "[test.Her] back is turned, and [test.she] hasn't noticed you enter the room... (Start the sex scene in the 'gentle' pace.)",
						Util.newArrayListOfValues(Fetish.FETISH_NON_CON_DOM), null, Fetish.FETISH_NON_CON_DOM.getAssociatedCorruptionLevel(), null, null, null,
						false, false,
						new SMAllFours(
								Util.newHashMapOfValues(new Value<>(Main.game.getPlayer(), SexSlotAllFours.BEHIND)),
								Util.newHashMapOfValues(new Value<>(Main.game.getNpc(TestNPC.class), SexSlotAllFours.ALL_FOURS))) {
							@Override
							public boolean isPublicSex() {
								return false;
							}
							@Override
							public SexPace getStartingSexPaceModifier(GameCharacter character) {
								if(character.isPlayer()) {
									return SexPace.DOM_GENTLE;
								}
								return null;
							}
						},
						null,
						null, TEST_DIALOGUE_AFTER_RAPE, "
"
							+ "You quietly close the door and lock it behind you. After all, you wouldn't want anyone interrupting your fun."
							+ " Stealthily moving forwards, you approach the oblivious [test.race], smiling to yourself as you hear [test.her] grumbling and mumbling to [test.herself]."
						+ "
"
						+ "
"
							+ "You're able to sneak right up behind [test.herHim] without [test.herHim] noticing, and, in one swift movement, you grab [test.her] shoulders and throw [test.herHim] to the floor."
						+ "
"
						+ "
"
							+ "[test.speech(Aaaah!)] [test.she] screams, falling down onto all fours."
						+ "
"
						+ "
"
							+ "You drop down behind [test.herHim], taking hold of [test.her] weak body and preventing [test.her] from escaping as you growl into [test.her] [test.ear+],"
							+ " [pc.speech(Hush now, there's no point in struggling, you're mine now!)]"
						+ "
");

			} else if (index == 4 && Main.game.isNonConEnabled()) {
				return new ResponseSex("Rape [test.herHim] (rough)", "[test.Her] back is turned, and [test.she] hasn't noticed you enter the room... (Start the sex scene in the 'rough' pace.)",
						Util.newArrayListOfValues(Fetish.FETISH_NON_CON_DOM), null, Fetish.FETISH_NON_CON_DOM.getAssociatedCorruptionLevel(), null, null, null,
						false, false,
						new SMAllFours(
								Util.newHashMapOfValues(new Value<>(Main.game.getPlayer(), SexSlotAllFours.BEHIND)),
								Util.newHashMapOfValues(new Value<>(Main.game.getNpc(TestNPC.class), SexSlotAllFours.ALL_FOURS))) {
							@Override
							public boolean isPublicSex() {
								return false;
							}
							@Override
							public SexPace getStartingSexPaceModifier(GameCharacter character) {
								if(character.isPlayer()) {
									return SexPace.DOM_ROUGH;
								}
								return null;
							}
						},
						null,
						null, TEST_DIALOGUE_AFTER_RAPE, "
"
							+ "You quietly close the door and lock it behind you. After all, you wouldn't want anyone interrupting your fun."
							+ " Stealthily moving forwards, you approach the oblivious [test.race], smiling to yourself as you hear [test.her] grumbling and mumbling to [test.herself]."
						+ "
"
						+ "
"
							+ "You're able to sneak right up behind [test.herHim] without [test.herHim] noticing, and, in one swift movement, you grab [test.her] shoulders and violently hurl [test.herHim] to the floor."
						+ "
"
						+ "
"
							+ "[test.speech(Aaaah!)] [test.she] screams, falling down onto all fours."
						+ "
"
						+ "
"
							+ "You drop down behind [test.herHim], looming over [test.her] weak body as you slam [test.her] down into the floor."
							+ " You grin as [test.she] struggles back up onto all fours, and, roughly grabbing [test.her] frail body, you tightly grip [test.her] waist, preventing [test.her] from escaping as you growl into [test.her] [test.ear+],"
							+ " [pc.speech(Scream all you want, bitch! You're mine now!)]"
						+ "
");

			} else if(index==0) {
				return new Response("Leave", "Step back without alerting the [test.race] to your presence.", TEST_DIALOGUE) {
					@Override
					public DialogueNode getNextDialogue(){
						return Main.game.getActiveWorld().getCell(Main.game.getPlayer().getLocation()).getPlace().getDialogue(false);
					}
				};

			} else {
				return null;
			}
		}
	};

	public static final DialogueNode TEST_DIALOGUE_GREET = new DialogueNode("A cozy room", ".", true, true) {

		@Override
		public String getContent() {
			return "
"
						+ "[pc.speech(Hello, I saw your door was open and I-)] you start to say, but the [test.race] interrupts you with a little shriek."
					+ "
"
					+ "
"
						+ "[test.speech(Aaaah!)]"
					+ "
"
					+ "
"
						+ "[test.She] tries to cover up [test.her] work, but in [test.her] haste, ends up knocking several sheets of paper to the floor."
						+ " You rush forwards to help [test.herHim] pick them up, and you see the title 'Lilith's Spire' at the top of one of the sheets."
						+ " Scanning down the page, your eyes open wide at the vulgar, sexually-explicit descriptions that this [test.race]'s been writing."
					+ "
"
					+ "
"
						+ "[test.speech(D-Don't look!)] [test.she] calls out, grabbing the papers from you,"
						+ " [test.speech(I-It's not like I meant for you to see them!)]"
					+ "
"
					+ "
"
						+ "Looking up at the [test.race], you see that [test.sheIs] blushing profusely, and, somewhat amusingly, staring down at your groin."
						+ " All of this erotic writing must really have got her worked-up, and you grin as you hear the next words that come out of [test.her] mouth."
					+ "
"
					+ "
"
						+ "[test.speech(But... Now that you've seen it... And seeing as you're already here... We may as well fuck, right?)]"
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if (index == 1) {
				return new ResponseSex("Sex",
						"Well, [test.she] is asking for it!",
						true, true,
						new SMGeneric(
								Util.newArrayListOfValues(Main.game.getPlayer()),
								Util.newArrayListOfValues(Main.game.getNpc(TestNPC.class)),
								null,
								null) {
							@Override
							public boolean isPublicSex() {
								return false;
							}
						},
						TEST_DIALOGUE_AFTER_SEX,
						"
"
							+ "You step forwards, wrapping your [pc.arms] around the [test.race] and pulling [test.herHim] into you."
						+ "
"
						+ "
"
							+ "[test.speech(~Mmmm!~)] [test.she] moans, leaning into you,"
							+ " [test.speech(Fuck me!)]"
						+ "
");

			} else if (index == 2) {
				return new ResponseSex("Sex (gentle)",
						"Well, [test.she] is asking for it! (Start the sex scene in the 'gentle' pace.)",
						true, true,
						new SMGeneric(
								Util.newArrayListOfValues(Main.game.getPlayer()),
								Util.newArrayListOfValues(Main.game.getNpc(TestNPC.class)),
								null,
								null,
								ResponseTag.START_PACE_PLAYER_DOM_GENTLE) {
							@Override
							public boolean isPublicSex() {
								return false;
							}
						},
						TEST_DIALOGUE_AFTER_SEX,
						"
"
							+ "You step forwards, gently wrapping your [pc.arms] around the [test.race] and lovingly pulling [test.herHim] into you."
						+ "
"
						+ "
"
							+ "[test.speech(~Mmmm!~)] [test.she] moans, leaning into you,"
							+ " [test.speech(Fuck me!)]"
						+ "
");

			} else if (index == 3) {
				return new ResponseSex("Sex (rough)",
						"Well, [test.she] is asking for it! (Start the sex scene in the 'rough' pace.)",
						true, true,
						new SMGeneric(
								Util.newArrayListOfValues(Main.game.getPlayer()),
								Util.newArrayListOfValues(Main.game.getNpc(TestNPC.class)),
								null,
								null,
								ResponseTag.START_PACE_PLAYER_DOM_ROUGH) {
							@Override
							public boolean isPublicSex() {
								return false;
							}
						},
						TEST_DIALOGUE_AFTER_SEX,
						"
"
								+ "You step forwards, wrapping your [pc.arms] around the [test.race] and roughly pulling [test.herHim] into you."
							+ "
"
							+ "
"
								+ "[test.speech(~Mmmm!~)] [test.she] moans, leaning into you,"
								+ " [test.speech(Yes! I like it rough!)]"
							+ "
");

			} else if (index == 6) {
				return new ResponseEffectsOnly("Inventory", "You're sure that [npc.name] won't mind you managing [npc.her] clothing and items..."){
					@Override
					public void effects() {
						Main.mainController.openInventory(Main.game.getActiveNPC(), InventoryInteraction.FULL_MANAGEMENT);
					}
				};

			} else if(index==0) {
				return new Response("Leave", "Leave the room.", TEST_DIALOGUE) {
					@Override
					public DialogueNode getNextDialogue(){
						return Main.game.getActiveWorld().getCell(Main.game.getPlayer().getLocation()).getPlace().getDialogue(false);
					}
				};

			} else {
				return null;
			}
		}
	};

	public static final DialogueNode TEST_DIALOGUE_AFTER_RAPE = new DialogueNode("Step back", ".", true) {

		@Override
		public String getContent() {
			if(Main.game.getNpc(TestNPC.class).isSatisfiedFromLastSex()) {
				return "
"
							+ "The [test.race] collapses to the floor, sobbing."
							+ " Grinning down at [test.her] despoiled body, you turn to the door and prepare to make your exit."
						+ "
"
						+ "
"
							+ "You knew [test.she] was a slut from the moment you saw [test.herHim], and the fact that [test.she] orgasmed during your fun only proves you right."
							+ " As you reach the door, you turn around and chuckle,"
							+ " [pc.speech(You were a good fuck, slut. Maybe I'll pay you another visit some time!)]"
						+ "
";
			} else {
				return "
"
							+ "The [test.race] collapses to the floor, sobbing."
							+ " Grinning down at [test.her] despoiled body, you turn to the door and prepare to make your exit."
							+ " As you reach for the handle, you turn around and chuckle,"
							+ " [pc.speech(You were a good fuck, slut. Maybe I'll pay you another visit some time!)]"
						+ "
";
			}
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==0) {
				return new Response("Leave", "Exit the room.", TEST_DIALOGUE) {
					@Override
					public DialogueNode getNextDialogue(){
						return Main.game.getActiveWorld().getCell(Main.game.getPlayer().getLocation()).getPlace().getDialogue(false);
					}
				};

			} else {
				return null;
			}
		}
	};

	public static final DialogueNode TEST_DIALOGUE_AFTER_SEX = new DialogueNode("Step back", ".", true) {

		@Override
		public String getContent() {
			if(Main.game.getNpc(TestNPC.class).isSatisfiedFromLastSex()) {
				return "
"
							+ "The [test.race] collapses to the floor, totally worn out and satisfied from the orgasm"+(Sex.getNumberOfOrgasms(Main.game.getNpc(TestNPC.class)) > 1?"s":"")+" you gave to [test.herHim]."
							+ " Grinning down at [test.her] delicate body, you turn to the door and prepare to make your exit."
							+ " As you reach for the handle, you turn around and chuckle,"
							+ " [pc.speech(That was fun! Maybe I'll pay you another visit some time!)]"
						+ "
";
			} else {
				return "
"
							+ "The [test.race] collapses to the floor, totally worn out."
							+ " Grinning down at [test.her] delicate body, you turn to the door and prepare to make your exit."
							+ " As you reach for the handle, you turn around and chuckle,"
							+ " [pc.speech(That was fun! Maybe I'll pay you another visit some time!)]"
						+ "
";
			}
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==0) {
				return new Response("Leave", "Exit the room.", TEST_DIALOGUE) {
					@Override
					public DialogueNode getNextDialogue(){
						return Main.game.getActiveWorld().getCell(Main.game.getPlayer().getLocation()).getPlace().getDialogue(false);
					}
				};

			} else {
				return null;
			}
		}
	};
}


code:
package com.lilithsthrone.game.character.npc.dominion;

import java.time.Month;
import java.util.List;

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

import com.lilithsthrone.game.Game;
import com.lilithsthrone.game.character.CharacterImportSetting;
import com.lilithsthrone.game.character.EquipClothingSetting;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.Covering;
import com.lilithsthrone.game.character.body.types.BodyCoveringType;
import com.lilithsthrone.game.character.body.valueEnums.AreolaeSize;
import com.lilithsthrone.game.character.body.valueEnums.AssSize;
import com.lilithsthrone.game.character.body.valueEnums.BodyHair;
import com.lilithsthrone.game.character.body.valueEnums.BodySize;
import com.lilithsthrone.game.character.body.valueEnums.BreastShape;
import com.lilithsthrone.game.character.body.valueEnums.Capacity;
import com.lilithsthrone.game.character.body.valueEnums.ClitorisSize;
import com.lilithsthrone.game.character.body.valueEnums.CupSize;
import com.lilithsthrone.game.character.body.valueEnums.FluidFlavour;
import com.lilithsthrone.game.character.body.valueEnums.HairLength;
import com.lilithsthrone.game.character.body.valueEnums.HairStyle;
import com.lilithsthrone.game.character.body.valueEnums.HipSize;
import com.lilithsthrone.game.character.body.valueEnums.LabiaSize;
import com.lilithsthrone.game.character.body.valueEnums.LipSize;
import com.lilithsthrone.game.character.body.valueEnums.Muscle;
import com.lilithsthrone.game.character.body.valueEnums.NippleSize;
import com.lilithsthrone.game.character.body.valueEnums.OrificeElasticity;
import com.lilithsthrone.game.character.body.valueEnums.OrificePlasticity;
import com.lilithsthrone.game.character.body.valueEnums.TongueLength;
import com.lilithsthrone.game.character.body.valueEnums.Wetness;
import com.lilithsthrone.game.character.effects.PerkCategory;
import com.lilithsthrone.game.character.effects.PerkManager;
import com.lilithsthrone.game.character.fetishes.Fetish;
import com.lilithsthrone.game.character.fetishes.FetishDesire;
import com.lilithsthrone.game.character.gender.Gender;
import com.lilithsthrone.game.character.npc.NPC;
import com.lilithsthrone.game.character.persona.NameTriplet;
import com.lilithsthrone.game.character.persona.Occupation;
import com.lilithsthrone.game.character.persona.PersonalityTrait;
import com.lilithsthrone.game.character.persona.SexualOrientation;
import com.lilithsthrone.game.character.race.RaceStage;
import com.lilithsthrone.game.character.race.Subspecies;
import com.lilithsthrone.game.dialogue.DialogueFlagValue;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.places.dominion.cityHall.CityHallDemographics;
import com.lilithsthrone.game.inventory.CharacterInventory;
import com.lilithsthrone.game.inventory.clothing.AbstractClothingType;
import com.lilithsthrone.game.inventory.clothing.ClothingType;
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.SexType;
import com.lilithsthrone.game.sex.managers.dominion.vanessa.SMVanessaOral;
import com.lilithsthrone.game.sex.positions.SexPosition;
import com.lilithsthrone.game.sex.positions.slots.SexSlotSitting;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;
import com.lilithsthrone.world.WorldType;
import com.lilithsthrone.world.places.PlaceType;

/**
 * @since 0.3.2
 * @version 0.3.4
 * @author Innoxia
 */
public class Vanessa extends NPC {

	public Vanessa() {
		this(false);
	}

	public Vanessa(boolean isImported) {
		super(isImported, new NameTriplet("Vanessa"), "Cunningham",
				"Ms. Cunningham, as she prefers to be called, is the sole person responsible for all of the affairs of Dominion's 'Bureau of Demographics'."
						+ " Although getting on years, she's aged remarkably well, and has a refined, elegant beauty about her.",
				55, Month.SEPTEMBER, 26,
				10, Gender.F_V_B_FEMALE, Subspecies.FOX_MORPH, RaceStage.PARTIAL,
				new CharacterInventory(10), WorldType.CITY_HALL, PlaceType.CITY_HALL_ARCHIVES, true);

		if(!isImported) {
			this.setPlayerKnowsName(false);
		}
	}

	@Override
	public void loadFromXML(Element parentElement, Document doc, CharacterImportSetting... settings) {
		loadNPCVariablesFromXML(this, null, parentElement, doc, settings);
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.2.3")) {
			this.setHairCovering(new Covering(BodyCoveringType.BODY_HAIR_HUMAN, Colour.COVERING_GREY), false);
			this.setHairCovering(new Covering(BodyCoveringType.BODY_HAIR_FOX_FUR, Colour.COVERING_GREY), false);
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.2.6")) {
			this.setPlayerKnowsName(false);
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.3.6")) {
			this.resetPerksMap(true);
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.5.1")) {
			this.setPersonalityTraits(
					PersonalityTrait.KIND);
		}
	}

	@Override
	public void setupPerks(boolean autoSelectPerks) {
		PerkManager.initialisePerks(this,
				Util.newArrayListOfValues(),
				Util.newHashMapOfValues(
						new Value<>(PerkCategory.PHYSICAL, 1),
						new Value<>(PerkCategory.LUST, 5),
						new Value<>(PerkCategory.ARCANE, 0)));
	}

	@Override
	public void setStartingBody(boolean setPersona) {
		// Persona:

		if(setPersona) {
			this.setPersonalityTraits(
					PersonalityTrait.KIND);

			this.setSexualOrientation(SexualOrientation.AMBIPHILIC);

			this.setHistory(Occupation.NPC_OFFICE_WORKER);

			this.addFetish(Fetish.FETISH_FOOT_GIVING);
			this.addFetish(Fetish.FETISH_ORAL_RECEIVING);

			this.setFetishDesire(Fetish.FETISH_SUBMISSIVE, FetishDesire.THREE_LIKE);
			this.setFetishDesire(Fetish.FETISH_VAGINAL_RECEIVING, FetishDesire.THREE_LIKE);
			this.setFetishDesire(Fetish.FETISH_ANAL_RECEIVING, FetishDesire.ONE_DISLIKE);
			this.setFetishDesire(Fetish.FETISH_MASOCHIST, FetishDesire.ZERO_HATE);
		}

		// Body:

		// Core:
		this.setHeight(177);
		this.setFemininity(90);
		this.setMuscle(Muscle.TWO_TONED.getMedianValue());
		this.setBodySize(BodySize.ONE_SLENDER.getMedianValue());

		// Coverings:
		this.setEyeCovering(new Covering(BodyCoveringType.EYE_HUMAN, Colour.EYE_BLUE));
		this.setEyeCovering(new Covering(BodyCoveringType.EYE_FOX_MORPH, Colour.EYE_BLUE));
		this.setSkinCovering(new Covering(BodyCoveringType.FOX_FUR, Colour.COVERING_GREY), true);
		this.setSkinCovering(new Covering(BodyCoveringType.HUMAN, Colour.SKIN_LIGHT), true);

		this.setHairCovering(new Covering(BodyCoveringType.HAIR_HUMAN, Colour.COVERING_GREY), true);
		this.setHairCovering(new Covering(BodyCoveringType.HAIR_FOX_FUR, Colour.COVERING_GREY), true);
		this.setHairLength(HairLength.FOUR_MID_BACK.getMedianValue());
		this.setHairStyle(HairStyle.BRAIDED);

		this.setHairCovering(new Covering(BodyCoveringType.BODY_HAIR_HUMAN, Colour.COVERING_GREY), false);
		this.setHairCovering(new Covering(BodyCoveringType.BODY_HAIR_FOX_FUR, Colour.COVERING_GREY), false);
		this.setUnderarmHair(BodyHair.ZERO_NONE);
		this.setAssHair(BodyHair.ZERO_NONE);
		this.setPubicHair(BodyHair.FOUR_NATURAL);
		this.setFacialHair(BodyHair.ZERO_NONE);

		this.setHandNailPolish(new Covering(BodyCoveringType.MAKEUP_NAIL_POLISH_HANDS, Colour.COVERING_CLEAR));
		this.setFootNailPolish(new Covering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET, Colour.COVERING_CLEAR));
//		this.setBlusher(new Covering(BodyCoveringType.MAKEUP_BLUSHER, Colour.COVERING_RED));
		this.setLipstick(new Covering(BodyCoveringType.MAKEUP_LIPSTICK, Colour.COVERING_RED_LIGHT));
		this.setEyeLiner(new Covering(BodyCoveringType.MAKEUP_EYE_LINER, Colour.COVERING_BLACK));
//		this.setEyeShadow(new Covering(BodyCoveringType.MAKEUP_EYE_SHADOW, Colour.COVERING_PINK));

		// Face:
		this.setFaceVirgin(false);
		this.setLipSize(LipSize.TWO_FULL);
		this.setFaceCapacity(Capacity.THREE_SLIGHTLY_LOOSE, true);
		// Throat settings and modifiers
		this.setTongueLength(TongueLength.ZERO_NORMAL.getMedianValue());
		// Tongue modifiers

		// Chest:
		this.setNippleVirgin(true);
		this.setBreastSize(CupSize.E.getMeasurement());
		this.setBreastShape(BreastShape.ROUND);
		this.setNippleSize(NippleSize.THREE_LARGE.getValue());
		this.setAreolaeSize(AreolaeSize.THREE_LARGE.getValue());
//		this.addNippleOrificeModifier(OrificeModifier.PUFFY);
//		this.setBreastLactationRegeneration(FluidRegeneration.TWO_FAST.getMedianRegenerationValuePerDay());
//		this.setBreastMilkStorage(500);
//		this.setBreastStoredMilk(500);

		// Ass:
		this.setAssVirgin(false);
		this.setAssBleached(false);
		this.setAssSize(AssSize.THREE_NORMAL.getValue());
		this.setHipSize(HipSize.FOUR_WOMANLY.getValue());
		this.setAssCapacity(Capacity.TWO_TIGHT, true);
		this.setAssWetness(Wetness.ZERO_DRY);
		this.setAssElasticity(OrificeElasticity.FOUR_LIMBER.getValue());
		this.setAssPlasticity(OrificePlasticity.THREE_RESILIENT.getValue());
		// Anus modifiers

		// Penis:
		// No penis

		// Vagina:
		this.setVaginaVirgin(false);
		this.setVaginaClitorisSize(ClitorisSize.ZERO_AVERAGE);
		this.setVaginaLabiaSize(LabiaSize.THREE_LARGE);
		this.setVaginaSquirter(true);
		this.setVaginaCapacity(Capacity.FOUR_LOOSE, true);
		this.setVaginaWetness(Wetness.THREE_WET);
		this.setVaginaElasticity(OrificeElasticity.THREE_FLEXIBLE.getValue());
		this.setVaginaPlasticity(OrificePlasticity.FIVE_YIELDING.getValue());
		this.setGirlcumFlavour(FluidFlavour.HONEY);

		// Feet:
//		this.setFootStructure(FootStructure.PLANTIGRADE);
	}

	@Override
	public void equipClothing(List<EquipClothingSetting> settings) {

		this.unequipAllClothingIntoVoid(true, true);

		inventory.setMoney(10 + Util.random.nextInt(getLevel()*10) + 1);

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.GROIN_LACY_PANTIES, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.CHEST_LACY_PLUNGE_BRA, Colour.CLOTHING_BLACK, false), true, this);

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.LEG_PENCIL_SKIRT, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_torso_feminine_short_sleeve_shirt", Colour.CLOTHING_PINK_LIGHT, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.TORSO_OVER_OPEN_CARDIGAN, Colour.CLOTHING_GREY, false), true, this);

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_sock_pantyhose", Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_foot_flats", Colour.CLOTHING_BLACK, false), true, this);

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_eye_half_moon_glasses", Colour.CLOTHING_STEEL, false), true, this);

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.WRIST_WOMENS_WATCH, Colour.CLOTHING_BLACK, false), true, this);

		this.setPiercedEar(true);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_piercing_ear_pearl_studs", Colour.CLOTHING_WHITE, Colour.CLOTHING_GOLD, null, false), true, this);

	}

	@Override
	public String getSpeechColour() {
		return "#E7CAE6";
	}

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

	@Override
	public String getGenericName() {
		return "Ms. Cunningham";
	}

	@Override
	public void dailyUpdate() {
		Main.game.getDialogueFlags().setFlag(DialogueFlagValue.vanessaDailyHelped, false);
		Main.game.getDialogueFlags().setFlag(DialogueFlagValue.vanessaDailyMassage, false);
	}

	// Sex:

	@Override
	public SexType getForeplayPreference(GameCharacter target) {
		if(Sex.getSexPositionSlot(Main.game.getNpc(Vanessa.class))==SexSlotSitting.SITTING) {
			return new SexType(SexParticipantType.NORMAL, SexAreaOrifice.VAGINA, SexAreaPenetration.TONGUE);
		}
		if(Sex.getSexPositionSlot(Main.game.getNpc(Vanessa.class))==SexSlotSitting.PERFORMING_ORAL) {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.TONGUE, SexAreaOrifice.VAGINA);
		}
		if(Sex.getPosition()==SexPosition.OVER_DESK) {
			return new SexType(SexParticipantType.NORMAL, SexAreaOrifice.VAGINA, SexAreaPenetration.PENIS);
		}

		return super.getForeplayPreference(target);
	}

	@Override
	public SexType getMainSexPreference(GameCharacter target) {
		return getForeplayPreference(target);
	}

	@Override
	public SexPace getSexPaceSubPreference(GameCharacter character){
		return SexPace.SUB_EAGER;
	}

	@Override
	public SexPace getSexPaceDomPreference(){
		return SexPace.DOM_GENTLE;
	}

	@Override
	public void endSex() {
		if(!(Sex.getSexManager() instanceof SMVanessaOral) || !Sex.isDom(Main.game.getNpc(Vanessa.class))) {
			Main.game.getNpc(Vanessa.class).cleanAllDirtySlots();
			Main.game.getNpc(Vanessa.class).equipClothing(Util.newArrayListOfValues(EquipClothingSetting.REPLACE_CLOTHING, EquipClothingSetting.ADD_ACCESSORIES));
		}
	}

	@Override
	public boolean isAbleToBeImpregnated() {
		return false;
	}

	@Override
	public void changeFurryLevel(){
	}

	@Override
	public DialogueNode getEncounterDialogue() {
		return CityHallDemographics.CITY_HALL_DEMOGRAPHICS_ENTRANCE;
	}

}

repo_name: klorpa/Pedo

license: unknown


code:
package com.lilithsthrone.game.character.npc.dominion;

import java.time.Month;
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 org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.lilithsthrone.game.Game;
import com.lilithsthrone.game.character.CharacterImportSetting;
import com.lilithsthrone.game.character.EquipClothingSetting;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.body.CoverableArea;
import com.lilithsthrone.game.character.body.Covering;
import com.lilithsthrone.game.character.body.types.BodyCoveringType;
import com.lilithsthrone.game.character.body.valueEnums.AreolaeSize;
import com.lilithsthrone.game.character.body.valueEnums.AssSize;
import com.lilithsthrone.game.character.body.valueEnums.BodyHair;
import com.lilithsthrone.game.character.body.valueEnums.BodySize;
import com.lilithsthrone.game.character.body.valueEnums.BreastShape;
import com.lilithsthrone.game.character.body.valueEnums.Capacity;
import com.lilithsthrone.game.character.body.valueEnums.ClitorisSize;
import com.lilithsthrone.game.character.body.valueEnums.CoveringPattern;
import com.lilithsthrone.game.character.body.valueEnums.CupSize;
import com.lilithsthrone.game.character.body.valueEnums.HairStyle;
import com.lilithsthrone.game.character.body.valueEnums.HipSize;
import com.lilithsthrone.game.character.body.valueEnums.LabiaSize;
import com.lilithsthrone.game.character.body.valueEnums.LipSize;
import com.lilithsthrone.game.character.body.valueEnums.Muscle;
import com.lilithsthrone.game.character.body.valueEnums.NippleSize;
import com.lilithsthrone.game.character.body.valueEnums.OrificeElasticity;
import com.lilithsthrone.game.character.body.valueEnums.OrificePlasticity;
import com.lilithsthrone.game.character.body.valueEnums.TesticleSize;
import com.lilithsthrone.game.character.body.valueEnums.TongueLength;
import com.lilithsthrone.game.character.body.valueEnums.Wetness;
import com.lilithsthrone.game.character.effects.Perk;
import com.lilithsthrone.game.character.effects.PerkCategory;
import com.lilithsthrone.game.character.effects.PerkManager;
import com.lilithsthrone.game.character.fetishes.Fetish;
import com.lilithsthrone.game.character.gender.Gender;
import com.lilithsthrone.game.character.npc.NPC;
import com.lilithsthrone.game.character.persona.NameTriplet;
import com.lilithsthrone.game.character.persona.Occupation;
import com.lilithsthrone.game.character.persona.PersonalityTrait;
import com.lilithsthrone.game.character.persona.SexualOrientation;
import com.lilithsthrone.game.character.race.RaceStage;
import com.lilithsthrone.game.character.race.Subspecies;
import com.lilithsthrone.game.combat.Spell;
import com.lilithsthrone.game.combat.SpellSchool;
import com.lilithsthrone.game.dialogue.DialogueFlagValue;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.inventory.AbstractCoreItem;
import com.lilithsthrone.game.inventory.AbstractCoreType;
import com.lilithsthrone.game.inventory.CharacterInventory;
import com.lilithsthrone.game.inventory.ItemTag;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
import com.lilithsthrone.game.inventory.clothing.AbstractClothingType;
import com.lilithsthrone.game.inventory.clothing.ClothingType;
import com.lilithsthrone.game.inventory.enchanting.EnchantingUtils;
import com.lilithsthrone.game.inventory.enchanting.ItemEffect;
import com.lilithsthrone.game.inventory.enchanting.TFModifier;
import com.lilithsthrone.game.inventory.enchanting.TFPotency;
import com.lilithsthrone.game.inventory.item.AbstractItem;
import com.lilithsthrone.game.inventory.item.AbstractItemType;
import com.lilithsthrone.game.inventory.item.ItemType;
import com.lilithsthrone.game.inventory.weapon.AbstractWeapon;
import com.lilithsthrone.game.inventory.weapon.AbstractWeaponType;
import com.lilithsthrone.game.inventory.weapon.WeaponType;
import com.lilithsthrone.game.sex.SexAreaOrifice;
import com.lilithsthrone.game.sex.SexAreaPenetration;
import com.lilithsthrone.game.sex.SexParticipantType;
import com.lilithsthrone.game.sex.SexType;
import com.lilithsthrone.game.sex.positions.AbstractSexPosition;
import com.lilithsthrone.game.sex.positions.slots.SexSlot;
import com.lilithsthrone.game.sex.positions.slots.SexSlotDesk;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;
import com.lilithsthrone.world.WorldType;
import com.lilithsthrone.world.places.PlaceType;

/**
 * @since 0.1.0
 * @version 0.3.5.1
 * @author Innoxia
 */
public class Vicky extends NPC {

	private Map<AbstractWeapon, Integer> weaponsForSale;
	private Map<AbstractItem, Integer> itemsForSale;
	private Map<AbstractClothing, Integer> clothingForSale;

	private AbstractItemType[] availableIngredients = new AbstractItemType[] {
			ItemType.RACE_INGREDIENT_CAT_MORPH,
			ItemType.RACE_INGREDIENT_DOG_MORPH,
			ItemType.RACE_INGREDIENT_HARPY,
			ItemType.RACE_INGREDIENT_HORSE_MORPH,
			ItemType.RACE_INGREDIENT_WOLF_MORPH,
			ItemType.RACE_INGREDIENT_SQUIRREL_MORPH,
			ItemType.RACE_INGREDIENT_COW_MORPH,
			ItemType.RACE_INGREDIENT_ALLIGATOR_MORPH,
			ItemType.RACE_INGREDIENT_REINDEER_MORPH,
			ItemType.RACE_INGREDIENT_HUMAN};

	private static List<AbstractItemType> availableSpellBooks = new ArrayList<>();

	static {
		for(Spell s : Spell.values()) {
			switch(s) {
				// Tier 1:
				case ARCANE_AROUSAL:
				case ICE_SHARD:
				case POISON_VAPOURS:
				case FIREBALL:
				case SLAM:
					availableSpellBooks.add(ItemType.getSpellBookType(s));
					break;

				// Tier 2:
				case ARCANE_CLOUD:
				case FLASH:
				case RAIN_CLOUD:
				case TELEKENETIC_SHOWER:
				case TELEPATHIC_COMMUNICATION:
				case VACUUM:
					availableSpellBooks.add(ItemType.getSpellBookType(s));
					break;

				// Tier 3:
				case STONE_SHELL:
				case PROTECTIVE_GUSTS:
				case CLOAK_OF_FLAMES:
				case SOOTHING_WATERS:
					availableSpellBooks.add(ItemType.getSpellBookType(s));
					break;
				case CLEANSE:
				case STEAL:
					break;

				// Tier 4:
				case ELEMENTAL_AIR:
				case ELEMENTAL_ARCANE:
				case ELEMENTAL_EARTH:
				case ELEMENTAL_FIRE:
				case ELEMENTAL_WATER:
					availableSpellBooks.add(ItemType.getSpellBookType(s));
					break;

				// Tier 5: // Special quest spells
				case LILITHS_COMMAND:
				case TELEPORT:
					break;

				case WITCH_CHARM:
				case WITCH_SEAL:
				case DARK_SIREN_SIRENS_CALL:
				case LIGHTNING_SPHERE_DISCHARGE:
				case LIGHTNING_SPHERE_OVERCHARGE:
					break;
			}
		}
	}

	public Vicky() {
		this(false);
	}

	public Vicky(boolean isImported) {
		super(isImported, new NameTriplet("Vicky"), "Haugen",
				"Vicky is the owner of the shop 'Arcane Arts'. Her manner of staring at anyone who enters her shop is quite unsettling, and you feel as though she's ready to pounce on you at any moment...",
				37, Month.MAY, 26,
				10, Gender.F_P_V_B_FUTANARI,
				Subspecies.WOLF_MORPH, RaceStage.GREATER, new CharacterInventory(10), WorldType.SHOPPING_ARCADE, PlaceType.SHOPPING_ARCADE_VICKYS_SHOP, true);

		weaponsForSale = new HashMap<>();
		itemsForSale = new HashMap<>();
		clothingForSale = new HashMap<>();
		if(!isImported) {
			dailyUpdate();
		}
	}

	@Override
	public Element saveAsXML(Element parentElement, Document doc) {
		Element properties = super.saveAsXML(parentElement, doc);

		Element weaponsElement = doc.createElement("weaponsForSale");
		properties.appendChild(weaponsElement);
		for(Entry<AbstractWeapon, Integer> weapon : weaponsForSale.entrySet()) {
			try {
				Element e = weapon.getKey().saveAsXML(weaponsElement, doc);
				e.setAttribute("count", String.valueOf(weapon.getValue()));
			} catch(Exception ex) {
			}
		}

		Element itemsElement = doc.createElement("itemsForSale");
		properties.appendChild(itemsElement);
		for(Entry<AbstractItem, Integer> item : itemsForSale.entrySet()) {
			try {
				Element e = item.getKey().saveAsXML(itemsElement, doc);
				e.setAttribute("count", String.valueOf(item.getValue()));
			} catch(Exception ex) {
			}
		}

		Element clothingElement = doc.createElement("clothingForSale");
		properties.appendChild(clothingElement);
		for(Entry<AbstractClothing, Integer> clothing : clothingForSale.entrySet()) {
			try {
				Element e = clothing.getKey().saveAsXML(clothingElement, doc);
				e.setAttribute("count", String.valueOf(clothing.getValue()));
			} catch(Exception ex) {
			}
		}

		return properties;
	}

	@Override
	public void loadFromXML(Element parentElement, Document doc, CharacterImportSetting... settings) {
		loadNPCVariablesFromXML(this, null, parentElement, doc, settings);

		if(Main.isVersionOlderThan(Game.loadingVersion, "0.2.10.5")) {
			resetBodyAfterVersion_2_10_5();
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.2.12")) {
			setStartingBody(false);
		}

		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.1.3")) {
			this.dailyUpdate();

		} else {
			Element weaponsElement = (Element) parentElement.getElementsByTagName("weaponsForSale").item(0);
			if(weaponsElement!=null) {
				weaponsForSale.clear();

				NodeList nodeList = weaponsElement.getElementsByTagName("weapon");
				for(int i=0; i < nodeList.getLength(); i++){
					Element e = (Element) nodeList.item(i);
					try {
						AbstractWeapon weapon = AbstractWeapon.loadFromXML(e, doc);
						int count = 1;
						try {
							count = Integer.valueOf(e.getAttribute("count"));
						} catch(Exception ex) {
							weaponsForSale.putIfAbsent(weapon, 0);
							count = weaponsForSale.get(weapon)+1;
						}
						weaponsForSale.put(weapon, count);
					} catch(Exception ex) {
					}
				}
			}
			Element itemsElement = (Element) parentElement.getElementsByTagName("itemsForSale").item(0);
			if(itemsElement!=null) {
				itemsForSale.clear();

				NodeList nodeList = itemsElement.getElementsByTagName("item");
				for(int i=0; i < nodeList.getLength(); i++){
					Element e = (Element) nodeList.item(i);
					try {
						AbstractItem item = AbstractItem.loadFromXML(e, doc);
						int count = 1;
						try {
							count = Integer.valueOf(e.getAttribute("count"));
						} catch(Exception ex) {
							itemsForSale.putIfAbsent(item, 0);
							count = itemsForSale.get(item)+1;
						}
						itemsForSale.put(item, count);
					} catch(Exception ex) {
					}
				}
			}
			Element clothingElement = (Element) parentElement.getElementsByTagName("clothingForSale").item(0);
			if(clothingElement!=null) {
				clothingForSale.clear();

				NodeList nodeList = clothingElement.getElementsByTagName("clothing");
				for(int i=0; i < nodeList.getLength(); i++){
					Element e = (Element) nodeList.item(i);
					try {
						AbstractClothing clothing = AbstractClothing.loadFromXML(e, doc);
						int count = 1;
						try {
							count = Integer.valueOf(e.getAttribute("count"));
						} catch(Exception ex) {
							clothingForSale.putIfAbsent(clothing, 0);
							count = clothingForSale.get(clothing)+1;
							}
						clothingForSale.put(clothing, count);
					} catch(Exception ex) {
					}
				}
			}
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.3.6")) {
			this.resetPerksMap(true);
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.5.1")) {
			this.setPersonalityTraits(
					PersonalityTrait.SELFISH,
					PersonalityTrait.LEWD,
					PersonalityTrait.CONFIDENT,
					PersonalityTrait.BRAVE);
		}
	}

	@Override
	public void setupPerks(boolean autoSelectPerks) {
		this.addSpecialPerk(Perk.SPECIAL_MARTIAL_BACKGROUND);
		PerkManager.initialisePerks(this,
				Util.newArrayListOfValues(),
				Util.newHashMapOfValues(
						new Value<>(PerkCategory.PHYSICAL, 5),
						new Value<>(PerkCategory.LUST, 1),
						new Value<>(PerkCategory.ARCANE, 0)));
	}

	@Override
	public void setStartingBody(boolean setPersona) {

		// Persona:

		if(setPersona) {
			this.setPersonalityTraits(
					PersonalityTrait.SELFISH,
					PersonalityTrait.LEWD,
					PersonalityTrait.CONFIDENT,
					PersonalityTrait.BRAVE);

			this.setSexualOrientation(SexualOrientation.AMBIPHILIC);

			this.setHistory(Occupation.NPC_STORE_OWNER);

			this.addFetish(Fetish.FETISH_DOMINANT);
			this.addFetish(Fetish.FETISH_ANAL_GIVING);
			this.addFetish(Fetish.FETISH_NON_CON_DOM);
		}

		// Body:

		// Core:
		this.setHeight(175);
		this.setFemininity(85);
		this.setMuscle(Muscle.FOUR_RIPPED.getMedianValue());
		this.setBodySize(BodySize.THREE_LARGE.getMedianValue());

		// Coverings:
		this.setEyeCovering(new Covering(BodyCoveringType.EYE_LYCAN, Colour.EYE_YELLOW));
		this.setSkinCovering(new Covering(BodyCoveringType.LYCAN_FUR, Colour.COVERING_BLACK), true);

		this.setHairCovering(new Covering(BodyCoveringType.HAIR_LYCAN_FUR, Colour.COVERING_BLACK), true);
		this.setHairLength(0);
		this.setHairStyle(HairStyle.LOOSE);

		this.setHairCovering(new Covering(BodyCoveringType.BODY_HAIR_LYCAN_FUR, CoveringPattern.NONE, Colour.COVERING_BLACK, false, Colour.COVERING_BLACK, false), false);
		this.setUnderarmHair(BodyHair.ZERO_NONE);
		this.setAssHair(BodyHair.ZERO_NONE);
		this.setPubicHair(BodyHair.FOUR_NATURAL);
		this.setFacialHair(BodyHair.ZERO_NONE);

//		this.setFootNailPolish(new Covering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET, Colour.COVERING_AMBER));
//		this.setHandNailPolish(new Covering(BodyCoveringType.MAKEUP_NAIL_POLISH_HANDS, Colour.COVERING_AMBER));
//		this.setBlusher(new Covering(BodyCoveringType.MAKEUP_BLUSHER, Colour.COVERING_BLACK));
//		this.setLipstick(new Covering(BodyCoveringType.MAKEUP_LIPSTICK, Colour.COVERING_RED));
//		this.setEyeLiner(new Covering(BodyCoveringType.MAKEUP_EYE_LINER, Colour.COVERING_BLACK));
//		this.setEyeShadow(new Covering(BodyCoveringType.MAKEUP_EYE_SHADOW, Colour.COVERING_BLACK));

		// Face:
		this.setFaceVirgin(false);
		this.setLipSize(LipSize.ONE_AVERAGE);
		this.setFaceCapacity(Capacity.THREE_SLIGHTLY_LOOSE, true);
		// Throat settings and modifiers
		this.setTongueLength(TongueLength.ZERO_NORMAL.getMedianValue());
		// Tongue modifiers

		// Chest:
		this.setNippleVirgin(true);
		this.setBreastSize(CupSize.C.getMeasurement());
		this.setBreastShape(BreastShape.SIDE_SET);
		this.setNippleSize(NippleSize.TWO_BIG);
		this.setAreolaeSize(AreolaeSize.TWO_BIG);
		// Nipple settings and modifiers

		// Ass:
		this.setAssVirgin(true);
		this.setAssBleached(false);
		this.setAssSize(AssSize.FOUR_LARGE);
		this.setHipSize(HipSize.FOUR_WOMANLY);
		// Anus settings and modifiers

		// Penis:
		this.setPenisVirgin(false);
		this.setPenisSize(22);
		this.setTesticleSize(TesticleSize.THREE_LARGE);
		this.setPenisCumStorage(65);
		this.fillCumToMaxStorage();

		// Vagina:
		this.setVaginaVirgin(false);
		this.setVaginaClitorisSize(ClitorisSize.ZERO_AVERAGE);
		this.setVaginaLabiaSize(LabiaSize.THREE_LARGE);
		this.setVaginaSquirter(true);
		this.setVaginaCapacity(Capacity.FIVE_ROOMY, true);
		this.setVaginaWetness(Wetness.THREE_WET);
		this.setVaginaElasticity(OrificeElasticity.THREE_FLEXIBLE.getValue());
		this.setVaginaPlasticity(OrificePlasticity.THREE_RESILIENT.getValue());

		// Feet:
		// Foot shape
	}

	@Override
	public void equipClothing(List<EquipClothingSetting> settings) {

		this.unequipAllClothingIntoVoid(true, true);

		this.setMoney(10);

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.GROIN_THONG, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.TORSO_CORSET_DRESS, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.HIPS_SUSPENDER_BELT, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_sock_stockings", Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_foot_thigh_high_boots", Colour.CLOTHING_BLACK, false), true, this);

	}

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

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

	@Override
	public void dailyUpdate() {
		clearNonEquippedInventory(false);
		itemsForSale.clear();
		weaponsForSale.clear();
		clothingForSale.clear();

		int requiredRoomForMiscItems = ItemType.getEssences().size()+SpellSchool.values().length+availableSpellBooks.size()+10;

		List<AbstractCoreType> types = new ArrayList<>();

		for(AbstractWeaponType wt : WeaponType.getAllWeapons()) {
			if(wt.getItemTags().contains(ItemTag.SOLD_BY_VICKY)) {
				types.add(wt);
			}
		}
		for(AbstractItemType item : ItemType.getAllItems()) {
			if(item.getItemTags().contains(ItemTag.SOLD_BY_VICKY)) {
				types.add(item);
			}
		}
		for(AbstractClothingType clothing : ClothingType.getAllClothing()) {
			try {
				if(clothing!=null && clothing.getDefaultItemTags().contains(ItemTag.SOLD_BY_VICKY)) {
					types.add(clothing);
				}
			} catch(Exception ex) {
				ex.printStackTrace();
			}
		}
		Collections.shuffle(types);
		int count=0;
		for(AbstractCoreType type : types) {
			if(type instanceof AbstractWeaponType) {
				weaponsForSale.put(AbstractWeaponType.generateWeapon((AbstractWeaponType) type), 1+Util.random.nextInt(3));

			} else if(type instanceof AbstractItemType) {
				itemsForSale.put(AbstractItemType.generateItem((AbstractItemType) type), 1);

			} else if(type instanceof AbstractClothingType) {
				clothingForSale.put(AbstractClothingType.generateClothing((AbstractClothingType) type), 1);
			}
			count++;
			if(count>=this.getMaximumInventorySpace()-requiredRoomForMiscItems) {
				break;
			}
		}

		AbstractItem ingredient = AbstractItemType.generateItem(availableIngredients[Util.random.nextInt(availableIngredients.length)]);
		TFModifier primaryMod = TFModifier.getTFRacialBodyPartsList().get(Util.random.nextInt(TFModifier.getTFRacialBodyPartsList().size()));
		for(int i=0; i<10;i++) {
			try {
				if(ingredient.getEnchantmentEffect().getEffectsDescription(primaryMod, TFModifier.NONE, TFPotency.MINOR_BOOST, 0, Main.game.getPlayer(), Main.game.getPlayer())!=null) {
					AbstractItem potion = EnchantingUtils.craftItem(ingredient, Util.newArrayListOfValues(new ItemEffect(ingredient.getEnchantmentEffect(), primaryMod, TFModifier.NONE, TFPotency.MINOR_BOOST, 0)));
					itemsForSale.putIfAbsent(potion, 0);
					itemsForSale.put(potion, 1+itemsForSale.get(potion));
					potion.setName(EnchantingUtils.getPotionName(ingredient, potion.getEffects()));
				}

				ingredient = AbstractItemType.generateItem(availableIngredients[Util.random.nextInt(availableIngredients.length)]);
				primaryMod = TFModifier.getTFRacialBodyPartsList().get(Util.random.nextInt(TFModifier.getTFRacialBodyPartsList().size()));
			} catch(Exception ex) {
			}
		}

		for(AbstractItemType itemType : availableSpellBooks) {
			itemsForSale.put(AbstractItemType.generateItem(itemType), 1);
		}

		for(SpellSchool school : SpellSchool.values()) {
			AbstractItem item = AbstractItemType.generateItem(ItemType.getSpellScrollType(school));
			itemsForSale.put(item, 10+Util.random.nextInt(20));
		}

//		if(Main.game.getPlayer().hasQuest(QuestLine.SIDE_ENCHANTMENT_DISCOVERY)) {
			for(AbstractItemType itemType : ItemType.getEssences()) {
				AbstractItem item = AbstractItemType.generateItem(itemType);
				itemsForSale.put(item, 500+Util.random.nextInt(251));
			}
//		}
	}

	@Override
	public void turnUpdate() {
		if(!Main.game.getCharactersPresent().contains(this)) {
			if(Main.game.isExtendedWorkTime()) {
				this.returnToHome();
			} else {
				this.setLocation(WorldType.EMPTY, PlaceType.GENERIC_HOLDING_CELL, false);
			}
		}
	}

	@Override
	public void handleSellingEffects(AbstractCoreItem item, int count, int itemPrice){
		if(weaponsForSale.containsKey(item)) {
			weaponsForSale.put((AbstractWeapon) item, weaponsForSale.get(item)-count);
		}
		if(clothingForSale.containsKey(item)) {
			clothingForSale.put((AbstractClothing) item, clothingForSale.get(item)-count);
		}
		if(itemsForSale.containsKey(item)) {
			itemsForSale.put((AbstractItem) item, itemsForSale.get(item)-count);
		}
	}

	@Override
	public void changeFurryLevel(){
	}

	@Override
	public DialogueNode getEncounterDialogue() {
		return null;
	}

	@Override
	public String getTraderDescription() {
		return "<p>"
					+ "You walk up to the counter, trying to ignore Vicky's piercing gaze and wolfish grin."
					+ " As you start to ask her about what sort of things she has in stock, she suddenly crouches down slightly, and you brace yourself as you half-expect her to leap over the counter and attack."
					+ " Instead, thankfully, she bends down and retrieves a few items, placing them before you on the countertop."
				+ "</p>"
				+ "<p>"
					+ UtilText.parseSpeech("This is what I've got today... I might have some new stuff tomorrow... You interested?", this) + " she asks, never taking her eyes off of you for a second."
				+ "</p>";
	}

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

	@Override
	public boolean willBuy(AbstractCoreItem item) {
		if(item instanceof AbstractWeapon)
			return true;

		if(item instanceof AbstractItem) {
			if(((AbstractItem)item).getItemType().getItemTags().contains(ItemTag.ESSENCE)
					|| ((AbstractItem)item).getItemType().getItemTags().contains(ItemTag.SPELL_BOOK)
					|| ((AbstractItem)item).getItemType().getItemTags().contains(ItemTag.SPELL_SCROLL)
					|| ((AbstractItem)item).getItemType()==ItemType.POTION
					|| ((AbstractItem)item).getItemType()==ItemType.ELIXIR) {
				return true;
			}
		}

		return false;
	}

	public Map<AbstractWeapon, Integer> getWeaponsForSale() {
		return weaponsForSale;
	}

	public Map<AbstractItem, Integer> getItemsForSale() {
		return itemsForSale;
	}

	public Map<AbstractClothing, Integer> getClothingForSale() {
		return clothingForSale;
	}

	@Override
	public void endSex() {
		if(!Main.game.getDialogueFlags().hasFlag(DialogueFlagValue.arthursPackageObtained)) {
			Main.game.getDialogueFlags().setFlag(DialogueFlagValue.arthursPackageObtained, true);
			Main.game.getTextEndStringBuilder().append(Main.game.getPlayer().addItem(AbstractItemType.generateItem(ItemType.ARTHURS_PACKAGE), false, true));
		}
	}

	// Sex:

	@Override
	public boolean getSexBehaviourDeniesRequests(SexType sexTypeRequest) {
		return true;
	}

	@Override
	public boolean isHappyToBeInSlot(AbstractSexPosition position, SexSlot slot, GameCharacter target) {
		return slot==SexSlotDesk.BETWEEN_LEGS;
	}

	@Override
	public SexType getForeplayPreference(GameCharacter target) {
		if(Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.ANUS, true)) {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.ANUS);

		} else if(Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true) && Main.game.getPlayer().hasVagina()) {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.VAGINA);

		} else {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.ASS);
		}
	}

	@Override
	public SexType getMainSexPreference(GameCharacter target) {
		if(Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.ANUS, true)) {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.ANUS);

		} else if(Main.game.getPlayer().isAbleToAccessCoverableArea(CoverableArea.VAGINA, true) && Main.game.getPlayer().hasVagina()) {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.VAGINA);

		} else {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.ASS);
		}
	}

}


code:
package com.lilithsthrone.game.character.npc.dominion;

import java.time.Month;
import java.util.List;

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

import com.lilithsthrone.game.Game;
import com.lilithsthrone.game.character.CharacterImportSetting;
import com.lilithsthrone.game.character.EquipClothingSetting;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.character.body.Covering;
import com.lilithsthrone.game.character.body.types.BodyCoveringType;
import com.lilithsthrone.game.character.body.types.HornType;
import com.lilithsthrone.game.character.body.types.LegType;
import com.lilithsthrone.game.character.body.types.TailType;
import com.lilithsthrone.game.character.body.types.WingType;
import com.lilithsthrone.game.character.body.valueEnums.BodyHair;
import com.lilithsthrone.game.character.body.valueEnums.BodySize;
import com.lilithsthrone.game.character.body.valueEnums.HairLength;
import com.lilithsthrone.game.character.body.valueEnums.HairStyle;
import com.lilithsthrone.game.character.body.valueEnums.Muscle;
import com.lilithsthrone.game.character.body.valueEnums.PenisGirth;
import com.lilithsthrone.game.character.body.valueEnums.TesticleSize;
import com.lilithsthrone.game.character.body.valueEnums.WingSize;
import com.lilithsthrone.game.character.effects.PerkCategory;
import com.lilithsthrone.game.character.effects.PerkManager;
import com.lilithsthrone.game.character.fetishes.Fetish;
import com.lilithsthrone.game.character.gender.Gender;
import com.lilithsthrone.game.character.npc.NPC;
import com.lilithsthrone.game.character.persona.NameTriplet;
import com.lilithsthrone.game.character.persona.Occupation;
import com.lilithsthrone.game.character.persona.PersonalityTrait;
import com.lilithsthrone.game.character.persona.SexualOrientation;
import com.lilithsthrone.game.character.quests.Quest;
import com.lilithsthrone.game.character.quests.QuestLine;
import com.lilithsthrone.game.character.race.RaceStage;
import com.lilithsthrone.game.character.race.Subspecies;
import com.lilithsthrone.game.combat.DamageType;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.game.dialogue.responses.ResponseSex;
import com.lilithsthrone.game.dialogue.utils.BodyChanging;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.inventory.CharacterInventory;
import com.lilithsthrone.game.inventory.clothing.AbstractClothingType;
import com.lilithsthrone.game.inventory.clothing.ClothingType;
import com.lilithsthrone.game.inventory.weapon.AbstractWeaponType;
import com.lilithsthrone.game.inventory.weapon.WeaponType;
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.SexType;
import com.lilithsthrone.game.sex.managers.dominion.zaranix.SMZaranixCockSucking;
import com.lilithsthrone.game.sex.managers.universal.SMGeneric;
import com.lilithsthrone.game.sex.positions.slots.SexSlotSitting;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;
import com.lilithsthrone.utils.Vector2i;
import com.lilithsthrone.world.WorldType;
import com.lilithsthrone.world.places.GenericPlace;
import com.lilithsthrone.world.places.PlaceType;

/**
 * @since 0.1.?
 * @version 0.3.5.5
 * @author Innoxia
 */
public class Zaranix extends NPC {

	public Zaranix() {
		this(false);
	}

	public Zaranix(boolean isImported) {
		super(isImported, new NameTriplet("Zaranix", "Zaranix", "Zoelix"), "Lyniximartu",
				"Zaranix is one of the few demons that feels more comfortable in his incubus, rather than succubus, form."
						+ " Muscular, tall, and handsome, Zaranix uses both his cunning mind and good looks to get what he wants.",
				47, Month.JULY, 3,
				15,
				null, null, null,
				new CharacterInventory(10), WorldType.ZARANIX_HOUSE_FIRST_FLOOR, PlaceType.ZARANIX_FF_OFFICE, true);

	}

	@Override
	public void loadFromXML(Element parentElement, Document doc, CharacterImportSetting... settings) {
		loadNPCVariablesFromXML(this, null, parentElement, doc, settings);

		if(Main.isVersionOlderThan(Game.loadingVersion, "0.2.10.5")) {
			resetBodyAfterVersion_2_10_5();
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.2.11")) {
			this.setAgeAppearanceDifferenceToAppearAsAge(32);
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.3.6")) {
			this.resetPerksMap(true);
		}
		if(Main.isVersionOlderThan(Game.loadingVersion, "0.3.5.1")) {
			this.setPersonalityTraits(
					PersonalityTrait.SELFISH);
		}
	}

	@Override
	public void setupPerks(boolean autoSelectPerks) {
		PerkManager.initialisePerks(this,
				Util.newArrayListOfValues(),
				Util.newHashMapOfValues(
						new Value<>(PerkCategory.PHYSICAL, 1),
						new Value<>(PerkCategory.LUST, 1),
						new Value<>(PerkCategory.ARCANE, 10)));
	}

	@Override
	public void setStartingBody(boolean setPersona) {

		// Persona:

		if(setPersona) {
			this.setPersonalityTraits(
					PersonalityTrait.SELFISH);

			this.setSexualOrientation(SexualOrientation.AMBIPHILIC);

			this.setHistory(Occupation.NPC_ARCANE_RESEARCHER);

			this.addFetish(Fetish.FETISH_ORAL_RECEIVING);
		}

		// Body
		// Add full body reset as this method is called after leaving Zaranix's house:
		this.setAgeAppearanceDifferenceToAppearAsAge(32);
		this.setBody(Gender.M_P_MALE, Subspecies.DEMON, RaceStage.GREATER, false);
		this.setLegType(LegType.DEMON_COMMON);
		this.setTailType(TailType.DEMON_COMMON);
		this.setWingType(WingType.NONE);
		this.setWingSize(WingSize.THREE_LARGE.getValue());
		this.setHornType(HornType.STRAIGHT);

		// Core:
		this.setHeight(190);
		this.setFemininity(15);
		this.setMuscle(Muscle.FOUR_RIPPED.getMedianValue());
		this.setBodySize(BodySize.TWO_AVERAGE.getMedianValue());

		// Coverings:
		this.setEyeCovering(new Covering(BodyCoveringType.EYE_DEMON_COMMON, Colour.EYE_ORANGE));
		this.setSkinCovering(new Covering(BodyCoveringType.DEMON_COMMON, Colour.SKIN_PURPLE), true);

		this.setHairCovering(new Covering(BodyCoveringType.HAIR_DEMON, Colour.COVERING_BLACK), true);
		this.setHairLength(HairLength.TWO_SHORT);
		this.setHairStyle(HairStyle.NONE);

		this.setHairCovering(new Covering(BodyCoveringType.BODY_HAIR_DEMON, Colour.COVERING_BLACK), false);
		this.setUnderarmHair(BodyHair.FOUR_NATURAL);
		this.setAssHair(BodyHair.FOUR_NATURAL);
		this.setPubicHair(BodyHair.FOUR_NATURAL);
		this.setFacialHair(BodyHair.THREE_TRIMMED);

//		this.setHandNailPolish(new Covering(BodyCoveringType.MAKEUP_NAIL_POLISH_HANDS, Colour.COVERING_RED));
//		this.setFootNailPolish(new Covering(BodyCoveringType.MAKEUP_NAIL_POLISH_FEET, Colour.COVERING_RED));
//		this.setBlusher(new Covering(BodyCoveringType.MAKEUP_BLUSHER, Colour.COVERING_RED));
//		this.setLipstick(new Covering(BodyCoveringType.MAKEUP_LIPSTICK, Colour.COVERING_RED));
//		this.setEyeLiner(new Covering(BodyCoveringType.MAKEUP_EYE_LINER, Colour.COVERING_BLACK));
//		this.setEyeShadow(new Covering(BodyCoveringType.MAKEUP_EYE_SHADOW, Colour.COVERING_PURPLE));

		// Face:
		this.setFaceVirgin(true);
		// Leave as default

		// Chest:
		this.setNippleVirgin(true);
		// Leave as default

		// Ass:
		this.setAssVirgin(true);
		// Leave as default

		// Penis:
		this.setPenisVirgin(false);
		this.setPenisGirth(PenisGirth.THREE_THICK);
		this.setPenisSize(25);
		this.setTesticleSize(TesticleSize.FOUR_HUGE);
		this.setPenisCumStorage(550);
		this.fillCumToMaxStorage();
		// Leave cum as normal value

		// Vagina:
		// No vagina

		// Feet:
		// Foot shape
	}

	@Override
	public void equipClothing(List<EquipClothingSetting> settings) {

		this.unequipAllClothingIntoVoid(true, true);

		this.equipMainWeaponFromNowhere(AbstractWeaponType.generateWeapon(WeaponType.MELEE_CHAOS_EPIC, DamageType.PHYSICAL));

		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.GROIN_BRIEFS, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.LEG_TROUSERS, Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing(ClothingType.TORSO_OXFORD_SHIRT, Colour.CLOTHING_GREY, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_sock_socks", Colour.CLOTHING_BLACK, false), true, this);
		this.equipClothingFromNowhere(AbstractClothingType.generateClothing("innoxia_foot_mens_smart_shoes", Colour.CLOTHING_BLACK, false), true, this);

	}

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

	@Override
	public String getSpeechColour() {
		if(this.isFeminine()) {
			return "#EB82ED";
		} else {
			return "#42C6FF";
		}
	}

	@Override
	public void changeFurryLevel(){
	}

	@Override
	public DialogueNode getEncounterDialogue() {
		return null;
	}

	@Override
	public void endSex() {
	}

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

	// Combat:

	@Override
	public String getMainAttackDescription(int armRow, GameCharacter target, boolean isHit) {
		return "
"
				+ UtilText.parse(target,
						UtilText.returnStringAtRandom(
						"With a booming shout, [zaranix.name] delivers a solid kick to [npc.namePos] torso!",
						"With an angry roar, [zaranix.name] punches [npc.name] square in the chest!",
						"[Zaranix.name] lets out a furious shout as he punches [npc.name] in the [npc.arm]!"))
			+ "
";
	}

	@Override
	public String getSpellDescription() {
		return "
"
				+ UtilText.returnStringAtRandom(
						"Letting out a booming roar, Zaranix thrusts his arm into the air and casts a spell!",
						"Zaranix steps back, and with an angry shout, casts a spell!")
			+ "
";
	}

	@Override
	public Response endCombat(boolean applyEffects, boolean victory) {
		if (victory) {
			return new Response("Victory", "You have defeated Zaranix!", AFTER_COMBAT_VICTORY) {
				@Override
				public void effects() {
					Main.game.getNpc(Arthur.class).setLocation(WorldType.LILAYAS_HOUSE_GROUND_FLOOR, PlaceType.LILAYA_HOME_LAB, false);

					if(Main.game.getPlayer().getQuest(QuestLine.MAIN) == Quest.MAIN_1_H_THE_GREAT_ESCAPE) {
						Main.game.getTextEndStringBuilder().append(Main.game.getPlayer().setQuestProgress(QuestLine.MAIN, Quest.MAIN_1_I_ARTHURS_TALE));
					}
				}
			};
		} else {
			return new Response("Defeat", "You have been defeated...", AFTER_COMBAT_DEFEAT);
		}
	}

	public static final DialogueNode AFTER_COMBAT_VICTORY = new DialogueNode("Victory", "", true) {

		@Override
		public String getContent() {// need to mention Lilaya
			return "
"
						+ "Zaranix staggers back, reaching out to grab hold of the edge of a nearby table as he pants,"
						+ " [zaranix.speech(Please! No more!)]"
					+ "
"
					+ "
"
						+ "It's quite clear that he's completely unable to continue fighting any longer, so, stepping forwards, you make your demands known,"
						+ " [pc.speech(You're going to hand over Arthur now, understood?)]"
					+ "
"
					+ "
"
						+ "[zaranix.speech(F-Fine...)]"
						+ " he sighs, understanding that he has little choice in the matter,"
						+ " [zaranix.speech(You heard the [pc.race], Arthur, you're free to go...)]"
					+ "
"
					+ "
"
						+ "Arthur, up until now having been hiding behind one of the laboratory's cabinets, slowly stands up,"
						+ " [arthur.speech(Bloody hell! Well, you're certainly letting me go easily! Not that I should be complaining...)]"
					+ "
"
					+ "
"
						+ "[zaranix.speech(If it were anyone else, I'd have refused to let you go, but I just realised who this [pc.race] is,)]"
						+ " Zaranix says, stepping back and collapsing down into a nearby chair."
						+ " [zaranix.speech([pc.SheIs] the [pc.race] who's moved in with Lilaya... Yes, I know who you are. I may not get out much, but I still keep my ear close to the grapevine.)]"
					+ "
"
					+ "
"
						+ "[arthur.speech(Bloody hell! So it's Lilaya who's behind this rescue?)]"
						+ " Arthur exclaims, half-way across the room towards the door,"
						+ " [arthur.speech(The last I heard, oh, what was it now... Oh yes! She was going to 'blast me into a million pieces, or banish me into an alternate dimension' the next time she saw me."
							+ " I mean, considering that nobody's managed to travel between dimensions before, I couldn't quite take her threat seriously, but she did seem to be pretty angry."
							+ " Maybe I'd be better off staying he-)]"
					+ "
"
					+ "
"
						+ "[zaranix.speech(Be quiet Arthur!)]"
						+ " Zaranix shouts,"
						+ " [zaranix.speech(I may have wealth and a modicum of power in this city, but having never been recognised by my Lilin mother, I'd be a fool to stand against the wishes of a demon who has."
							+ " And it's not just any demon! Hah! Lyssieth's precious little half-demon herself!"
							+ " So go on! Get out of here Arthur!)]"
					+ "
"
					+"
"
						+ "[arthur.speech(Well, bloody hell! Maybe I'll see you around Zaranix. It wasn't too bad living here, all things considered!)]"
						+ " Arthur says, before turning to you,"
						+ " [arthur.speech(I'll go on ahead, so I suppose I'll see you over at Lilaya's place!)]"
					+ "
"
					+ "
"
						+ "With that, he steps past you and makes his exit, leaving you alone in the lab with the defeated incubus."
						+ " You wonder if you should take advantage of his weakened state, or simply turn around and leave..."
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				return new Response("Leave", "Leave Zaranix's house.", PlaceType.DOMINION_DEMON_HOME.getDialogue(false)) {
					@Override
					public void effects() {
						Main.game.getPlayer().setLocation(WorldType.DOMINION, PlaceType.DOMINION_DEMON_HOME, false);
					}
				};

			} else if(index==2) {
				return new ResponseSex("Use Zaranix", "Have some fun with this incubus.",
						true, false,
						new SMGeneric(
								Util.newArrayListOfValues(Main.game.getPlayer()),
								Util.newArrayListOfValues(Main.game.getNpc(Zaranix.class)),
						null,
						null), AFTER_SEX_VICTORY, "
"
							+ "[pc.speech(You made me go through a lot of trouble to find Arthur,)]"
							+ " you say, stepping towards the exhausted [zaranix.race],"
							+ " [pc.speech(I think you owe me an apology...)]"
						+ "
"
						+ "
"
							+ "Despite the fact that [zaranix.sheIs] a powerful demon, [zaranix.name] lets out a lewd little sigh as [zaranix.she] looks up into your [pc.eyes]."
							+ " Your strong arcane aura is quite clearly having a profound effect on [zaranix.herHim], and as you approach, [zaranix.she] pleads,"
							+ " [zaranix.speech(Please! Let me make it up to you!)]"
						+ "
"
						+ "
"
							+ "Grinning down at [zaranix.name], you prepare to have some fun..."
						+ "
");

			} else if(index==3) {
				return new ResponseSex("Submit",
						"Allow Zaranix to fuck you.",
						Util.newArrayListOfValues(Fetish.FETISH_SUBMISSIVE), null, CorruptionLevel.THREE_DIRTY, null, null, null,
						false, false,
						new SMGeneric(
								Util.newArrayListOfValues(Main.game.getNpc(Zaranix.class)),
								Util.newArrayListOfValues(Main.game.getPlayer()),
						null,
						null), AFTER_SEX_VICTORY, "
"
							+ "You weren't really expecting it to be so easy to convince [zaranix.name] to part with Arthur, and, feeling a little guilty for fighting [zaranix.herHim] in [zaranix.her] own house,"
								+ " you decide to try and make it up to [zaranix.herHim]."
							+ " Looking down at [zaranix.her] crotch and biting your [pc.lip], you let out a pleading little whine,"
							+ " [pc.speech(Sorry [zaranix.name] for all this trouble... Perhaps you'd like me to do something for you...)]"
						+ "
"
						+ "
"
							+ "[zaranix.Name] looks up at you, a big grin spreading across [zaranix.her] face as [zaranix.she] realises what it is you want."
							+ " Standing up, [zaranix.she] steps over towards you, before running [zaranix.her] hands over your [pc.hips] and around to give your [pc.ass+] a firm squeeze."
							+ " You can tell by the hungry look in [zaranix.her] eyes that your arcane aura is filling [zaranix.her] mind with lust, and, to confirm your suspicions, the [zaranix.race] leans forwards, before [zaranix.moaning],"
							+ " [zaranix.speech(There is one thing you can do... I want to hear you call out my name as I fuck you!)]"
						+ "
");

			} else if (index == 4) {
				return new Response("Transformations",
						"Get Zaranix to use [zaranix.her] demonic powers to transform [zaranix.herself]...",
						BodyChanging.BODY_CHANGING_CORE){
					@Override
					public void effects() {
						Main.game.saveDialogueNode();
						BodyChanging.setTarget(Main.game.getNpc(Zaranix.class));
					}
				};

			} else {
				return null;
			}
		}
	};

	public static final DialogueNode AFTER_SEX_VICTORY = new DialogueNode("Continue", "", true) {

		@Override
		public String getContent() {
			UtilText.nodeContentSB.setLength(0);
			if(Sex.getNumberOfOrgasms(Main.game.getNpc(Zaranix.class)) >= Main.game.getNpc(Zaranix.class).getOrgasmsBeforeSatisfied()) {
				UtilText.nodeContentSB.append(
						"
"
							+ "[npc.Name] steps back and sinks down into a nearby chair, a happy smile on [npc.her] face as [npc.she] gazes up at you,"
							+ " [npc.speech(That was fun... I guess losing Arthur for that wasn't too bad a deal...)]"
						+ "
");

			} else {
				UtilText.nodeContentSB.append(
						"
"
							+ "[npc.Name] steps back and sinks down to the floor, letting out [npc.a_moan+] as [npc.her] hands drop down between [npc.her] legs,"
							+ " [npc.speech(Ah! You didn't even satisfy me...)]"
						+ "
");
			}

			UtilText.nodeContentSB.append(
					"
"
						+ "Smirking down at the [npc.race] one last time, you turn around and take your leave."
						+ " Retracing your steps through [npc.her] house, you soon find yourself back out in the streets of Demon Home."
						+ " Now that Arthur's been rescued, you realise that you should probably head over to Lilaya's house to find out what your next step will be."
					+ "
");

			return UtilText.parse(Main.game.getNpc(Zaranix.class), UtilText.nodeContentSB.toString());
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				return new Response("Continue", "Continue on your journey.", PlaceType.DOMINION_DEMON_HOME.getDialogue(false)) {
					@Override
					public void effects() {
						Main.game.getPlayer().setLocation(WorldType.DOMINION, PlaceType.DOMINION_DEMON_HOME, false);
					}
				};

			} else {
				return null;
			}
		}
	};

	public static final DialogueNode AFTER_COMBAT_DEFEAT = new DialogueNode("Defeated", "", true) {

		@Override
		public String getContent() {
			return "
"
						+ "You can't continue fighting any longer, and, letting out a defeated groan, you stagger backwards and fall down against one wall of Zaranix's lab."
						+ " Glancing up at your conqueror, you see the imposing form of the huge incubus towering over you with a maniacal grin on his face."
					+ "
"
					+ "
"
						+ "[zaranix.speech(So, you thought you'd barge into my lab and get away with it?! Hah!)]"
						+ " He shouts, turning around and grabbing a small glass bottle, before facing you once more,"
						+ " [zaranix.speech(Well, Arthur and I were in need of a test subject, and you'll do nicely!)]"
					+ "
"
					+ "
"
						+ "Before you can say anything, the huge incubus pulls the stopper out of the top of the bottle, and, stepping towards you, kneels down and forces the glass against your lips,"
						+ " [zaranix.speech(Come on! Drink up! Arthur assures me that this particular concoction will transform you the perfect little cock-sucker, and I can't wait to be judge of that myself!)]"
					+ "
"
					+ "
"
						+ "You still have a little energy left, and as you feel the first few drops of the potion seeping into your mouth, you realise that you could spit it out and try to throw the bottle to the floor."
						+ " Then again, maybe it's best to just do as the demon says..."
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				if(Main.game.isSpittingDisabled()) {
					return Response.getDisallowedSpittingResponse();
				}
				if(Main.game.getPlayer().hasFetish(Fetish.FETISH_TRANSFORMATION_RECEIVING)) {
					return new Response("Spit",
							"Due to your "+Fetish.FETISH_TRANSFORMATION_RECEIVING.getName(Main.game.getPlayer())
								+" fetish, you love being transformed so much that you can't bring yourself to spit out the transformative liquid!",
							null);
				} else {
					return new Response("Spit", "Spit out the potion.", AFTER_COMBAT_DEFEAT_SPIT);
				}

			} else if(index==2) {
				return new Response("Swallow", "Swallow the potion.", AFTER_COMBAT_DEFEAT_SWALLOW) {
					@Override
					public void effects() {
//						Main.game.getTextEndStringBuilder().append("
"+Main.game.getPlayer().setFemininity(100)+"
");
						Main.game.getTextEndStringBuilder().append("
"+Main.game.getPlayer().incrementLipSize(7)+"
");
						Main.game.getPlayer().addFetish(Fetish.FETISH_ORAL_GIVING);
						Main.game.getTextEndStringBuilder().append(
								"
"
									+ "A desperate, overwhelming need to have Zaranix's huge demonic cock thrusting deep down your throat causes you to let out an incredibly lewd moan."
									+ "
You have gained the "+Fetish.FETISH_ORAL_GIVING.getName(Main.game.getPlayer())+" fetish!"
								+ "
");
						Main.game.getPlayer().addFetish(Fetish.FETISH_CUM_ADDICT);
						Main.game.getTextEndStringBuilder().append(
								"
"
									+ "You can't help but lick your huge new cock-sucking lips as you imagine the incubus giving you your delicious reward; a huge load of salty, demonic cum...."
									+ "
You have gained the "+Fetish.FETISH_CUM_ADDICT.getName(Main.game.getPlayer())+" fetish!"
								+ "
");
					}
				};

			} else {
				return null;
			}
		}
	};

	public static final DialogueNode AFTER_COMBAT_DEFEAT_SPIT = new DialogueNode("Defeated", "", true, true) {

		@Override
		public String getContent() {
			return "
"
						+ "There's no way you want to be transformed into Zaranix's 'perfect little cock-sucker'."
						+ " Summoning the last of your energy, you twist your head to one side, spitting the few drops of the potion that entered your mouth to the floor, before grabbing the little glass bottle and hurling it to the other side of the room."
					+ "
"
					+ "
"
						+ "The sound of smashing glass is accompanies by a deep, booming roar, as the now-furious incubus shouts,"
						+ " [zaranix.speech(You insolent "+(Main.game.getPlayer().isFeminine()?"whore":"bastard")+"! You'll suck my cock, with or without that potion!)]"
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				return new ResponseSex("Used", "Zaranix forces you to orally service him.",
						false, false,
						new SMZaranixCockSucking(
								Util.newHashMapOfValues(new Value<>(Main.game.getNpc(Zaranix.class), SexSlotSitting.SITTING)),
								Util.newHashMapOfValues(new Value<>(Main.game.getPlayer(), SexSlotSitting.PERFORMING_ORAL))),
						null,
						null, AFTER_SEX_DEFEAT, "
"
							+ "Grabbing you by the [pc.arm], Zaranix roughly drags you across the lab."
							+ " Collapsing down in a nearby chair, he pulls you forwards on your knees as he spreads his legs, bringing your face right up against his crotch and booming,"
							+ " [zaranix.speech(You're going to love my cock, slut!)]"
						+ "
"
						+ "
"
							+ "You're far too exhausted from your fight to put up much of a resistance, and as the horny incubus forces your mouth ever closer to the huge bulge in his groin,"
								+ " you realise that he's not going to let you go until he's satisfied..."
						+ "
");
			} else {
				return null;
			}
		}
	};

	public static final DialogueNode AFTER_COMBAT_DEFEAT_SWALLOW = new DialogueNode("Defeated", "", true, true) {

		@Override
		public String getContent() {
			return "
"
						+ "You decide that there's no point in resisting the dominant incubus, and, opening wide to accept the neck of the potion bottle past your lips, you obediently take the thick, pink liquid into your mouth."
						+ " The taste strongly reminds you of strawberries, and, eagerly gulping down the sweet mixture, it only takes you a few moments to completely drain the bottle of its contents."
					+ "
"
					+ "
"
						+ "As Zaranix takes the flask away from your mouth, a fuzzy, warm feeling starts spreading all throughout your body."
						+ " You suddenly find yourself thinking how utterly wonderful it would be if Zaranix gave you even more of that sickly-sweet liquid to drink."
						+ " Even better, you think, if there was an endless supply..."
					+ "
"
					+ "
"
						+ "Perhaps you could convince Zaranix to enchant his cock so that he cums that delicious liquid..."
					+ "
"
					+ "
"
						+ "You'd happily wrap your fat, juicy, cock-sucking lips -were they always this big?- around his huge, thick cock if it meant you got to taste that again..."
					+ "
"
					+ "
"
						+ "Now that you think about it, just getting to suck his cock would be reward enough..."
					+ "
"
					+ "
"
						+ "You need to suck his cock... You need to drink his cum... You need his cock down your throat... Right... Now!"
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				return new ResponseSex("Need... Cock...", "Zaranix's potion has had quite a strong effect... You really need to suck his cock, then maybe you'll be able to think clearly again?",
						true, false,
						new SMZaranixCockSucking(
								Util.newHashMapOfValues(new Value<>(Main.game.getNpc(Zaranix.class), SexSlotSitting.SITTING)),
								Util.newHashMapOfValues(new Value<>(Main.game.getPlayer(), SexSlotSitting.PERFORMING_ORAL))),
						null,
						null, AFTER_SEX_DEFEAT,
						"
"
							+ "Zaranix steps back and sits down in one of the lab's many chairs."
							+ " Spreading his legs, he grins down at you,"
							+ " [zaranix.speech(I bet you want to put those new lips of yours to use, don't you, my little cock-sucker?)]"
						+ "
"
						+ "
"
							+ "You let out an intense, desperate whine as you gaze hungrily at the bulge in Zaranix's groin."
							+ " Before you know what you're doing, you're shuffling over towards him on your knees, running your hands up and over his groin as you plead,"
							+ " [zaranix.speech(~Mmm!~ Please! Need... cock!)]"
						+ "
"
						+ "
"
							+ "[zaranix.speech(Hah!)]"
							+ " The incubus booms,"
							+ " [zaranix.speech(Then get to it, cock-sucker!)]"
						+ "
");
			} else {
				return null;
			}
		}
	};

	public static final DialogueNode AFTER_SEX_DEFEAT = new DialogueNode("Used", "", true) {

		@Override
		public String getContent() {
			return "
"
						+ "Having had enough of using your mouth, Zaranix rather unceremoniously pushes you away and stands up,"
						+ " [zaranix.speech(That was pretty good. Hah!)]"
					+ "
"
					+ "
"
						+ "Before you can reply, the incubus reaches down and grabs your [pc.arm]."
						+ " The grip of his big, strong hand is too much for you, and, left with little choice but to shuffle and crawl alongside Zaranix, you're quickly dragged back through the house and to the front door."
					+ "
"
					+ "
"
						+ "[zaranix.speech(I wouldn't bother coming back, unless you want another taste of my cock, slut!)]"
						+ " He laughs, throwing you out into the street and slamming eh door behind you..."
					+ "
";
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if(index==1) {
				return new Response("Thrown out", "Zaranix has thrown you out into the street.", PlaceType.DOMINION_DEMON_HOME.getDialogue(false)) {
					@Override
					public void effects() {
						Main.game.getPlayer().setLocation(WorldType.DOMINION, PlaceType.DOMINION_DEMON_HOME, false);
					}
				};

			} else {
				return null;
			}
		}
	};

	// Sex:

	@Override
	public SexType getForeplayPreference(GameCharacter target) {
		if(Sex.getSexPositionSlot(Main.game.getNpc(Zaranix.class))==SexSlotSitting.SITTING && this.hasPenis()) {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.MOUTH);
		}

		return super.getForeplayPreference(target);
	}

	@Override
	public SexType getMainSexPreference(GameCharacter target) {
		if(Sex.getSexPositionSlot(Main.game.getNpc(Zaranix.class))==SexSlotSitting.SITTING && this.hasPenis()) {
			return new SexType(SexParticipantType.NORMAL, SexAreaPenetration.PENIS, SexAreaOrifice.MOUTH);
		}

		return super.getMainSexPreference(target);
	}

	@Override
	public GameCharacter getPreferredSexTarget() {
		if(Sex.getSexManager().getDominants().containsKey(Main.game.getNpc(Amber.class))) { // Amber is assisting the player to suck Zaranix's cock:
			return Main.game.getPlayer();
		}
		return super.getPreferredSexTarget();
	}

	// Misc.:

	public void generateNewTile() {
		if(Main.game.getWorlds().get(WorldType.DOMINION).getCell(PlaceType.DOMINION_DEMON_HOME_ZARANIX)==null) {
			Vector2i towerLoc = new Vector2i(Main.game.getWorlds().get(WorldType.DOMINION).getCell(PlaceType.DOMINION_LILITHS_TOWER).getLocation());
			towerLoc.setX(towerLoc.getX()+1);
			towerLoc.setY(towerLoc.getY()-2);
			if(!Main.game.getWorlds().get(WorldType.DOMINION).getCell(towerLoc).getPlace().getPlaceType().equals(PlaceType.DOMINION_DEMON_HOME)) {
				towerLoc = new Vector2i(Main.game.getWorlds().get(WorldType.DOMINION).getRandomCell(PlaceType.DOMINION_DEMON_HOME).getLocation());
			}
			Main.game.getWorlds().get(WorldType.DOMINION).getCell(towerLoc).setPlace(
					new GenericPlace(PlaceType.DOMINION_DEMON_HOME_ZARANIX) {
						@Override
						public String getName() {
							return PlaceType.DOMINION_DEMON_HOME_ZARANIX.getName();
						}
					},
					false);
		}
	}

}