code:
package com.lilithsthrone.controller;

import com.lilithsthrone.main.Main;

import javafx.application.Platform;

/**
 * @since 0.1.3
 * @version 0.1.7
 * @author Innoxia
 */
public class TooltipUpdateThread extends Thread {

	public static boolean cancelThreads = false;

	public static void updateToolTip(double xPosition, double yPosition) {
		cancelThreads = false;

		try {
			sleep(50);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		// While this probably isn't needed, since we run the game on the JavaFX thread most of the time
		// I'm keeping it in just in case since JavaFX isn't threadsafe.
		Platform.runLater(new Runnable() {
			@Override
			public void run() {

				if (!cancelThreads) {
					Main.mainController.getTooltip().show(Main.primaryStage);

					if(xPosition != -1){
						Main.mainController.getTooltip().setAnchorY(yPosition);
					}
				}
			}
		});
	}
}


code:
package com.lilithsthrone.controller.eventListeners;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.game.dialogue.utils.EnchantmentDialogue;
import com.lilithsthrone.game.inventory.AbstractCoreItem;
import com.lilithsthrone.game.inventory.enchanting.TFModifier;
import com.lilithsthrone.game.inventory.enchanting.TFPotency;
import com.lilithsthrone.main.Main;

/**
 * @since 0.1.7
 * @version 0.3.5.1
 * @author Innoxia
 */
public class EnchantmentEventListener implements EventListener {
	private AbstractCoreItem itemToEnchant;
	private TFModifier primaryModifier, secondaryModifier;
	private TFPotency potency;
	private boolean effect;
	private int effectIndex;
	private int limit;

	@Override
	public void handleEvent(Event event) {

		if (itemToEnchant != null) {
			if(itemToEnchant.getEnchantmentEffect()!=null) {
				EnchantmentDialogue.resetEnchantmentVariables();
				EnchantmentDialogue.setIngredient(itemToEnchant);
			}

		} else if(primaryModifier != null) {
			EnchantmentDialogue.setPrimaryMod(primaryModifier);

		} else if(secondaryModifier != null) {
			EnchantmentDialogue.setSecondaryMod(secondaryModifier);

		} else if(potency != null) {
			EnchantmentDialogue.setPotency(potency);

		} else if(effect) {
			EnchantmentDialogue.removeEffect(effectIndex);

		} else if(limit != EnchantmentDialogue.getLimit()) {
			EnchantmentDialogue.setLimit(limit);
		}

		if(!EnchantmentDialogue.getIngredient().getEnchantmentEffect().getPrimaryModifiers().contains(EnchantmentDialogue.getPrimaryMod())) {
			EnchantmentDialogue.setPrimaryMod(EnchantmentDialogue.getIngredient().getEnchantmentEffect().getPrimaryModifiers().get(0));
		}
		if(!EnchantmentDialogue.getIngredient().getEnchantmentEffect().getSecondaryModifiers(EnchantmentDialogue.getPrimaryMod()).contains(EnchantmentDialogue.getSecondaryMod())) {
			EnchantmentDialogue.setSecondaryMod(EnchantmentDialogue.getIngredient().getEnchantmentEffect().getSecondaryModifiers(EnchantmentDialogue.getPrimaryMod()).get(0));
		}
		if(!EnchantmentDialogue.getIngredient().getEnchantmentEffect().getPotencyModifiers(EnchantmentDialogue.getPrimaryMod(), EnchantmentDialogue.getSecondaryMod()).contains(EnchantmentDialogue.getPotency())) {
			EnchantmentDialogue.setPotency(TFPotency.MINOR_BOOST);
		}
		if(EnchantmentDialogue.getLimit() > EnchantmentDialogue.getIngredient().getEnchantmentEffect().getLimits(EnchantmentDialogue.getPrimaryMod(), EnchantmentDialogue.getSecondaryMod())) {
			EnchantmentDialogue.setLimit(EnchantmentDialogue.getIngredient().getEnchantmentEffect().getLimits(EnchantmentDialogue.getPrimaryMod(), EnchantmentDialogue.getSecondaryMod()));
		}

		Main.game.setContent(new Response("Enchanting", "Start enchanting.", EnchantmentDialogue.ENCHANTMENT_MENU));
	}

	public EnchantmentEventListener setItemToEnchant(AbstractCoreItem itemToEnchant) {
		resetVariables();
		this.itemToEnchant = itemToEnchant;

		return this;
	}

	public EnchantmentEventListener setPrimaryModifier(TFModifier primaryModifier) {
		resetVariables();
		this.primaryModifier = primaryModifier;

		return this;
	}

	public EnchantmentEventListener setSecondaryModifier(TFModifier secondaryModifier) {
		resetVariables();
		this.secondaryModifier = secondaryModifier;

		return this;
	}

	public EnchantmentEventListener setPotency(TFPotency potency) {
		resetVariables();
		this.potency = potency;

		return this;
	}

	public EnchantmentEventListener removeEffect(int effectIndex) {
		resetVariables();
		effect = true;
		this.effectIndex = effectIndex;

		return this;
	}

	public EnchantmentEventListener setLimit(int limit) {
		resetVariables();
		this.limit = limit;

		return this;
	}

	private void resetVariables() {
		effect = false;
		effectIndex = 0;
		itemToEnchant = null;
		primaryModifier = null;
		secondaryModifier = null;
		potency = null;
		limit = 0;
	}
}

repo_name: klorpa/Pedo

license: unknown


code:
package com.lilithsthrone.controller.eventListeners;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.controller.TooltipUpdateThread;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.dialogue.DialogueFlagValue;
import com.lilithsthrone.game.dialogue.DialogueNodeType;
import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.game.dialogue.utils.InventoryDialogue;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
import com.lilithsthrone.game.inventory.item.AbstractItem;
import com.lilithsthrone.game.inventory.weapon.AbstractWeapon;
import com.lilithsthrone.main.Main;

/**
 * @since 0.1.0
 * @version 0.3.4.5
 * @author Innoxia
 */
public class InventorySelectedItemEventListener implements EventListener {
	private AbstractItem item;

	private AbstractClothing clothing;
	private AbstractClothing clothingEquipped;

	private AbstractWeapon weapon;
	private AbstractWeapon weaponEquipped;

	private InventorySlot weaponSlot;

	private GameCharacter owner;

	private int buyBackIndex;

	@Override
	public void handleEvent(Event event) {

//		if (Main.game.getCurrentDialogueNode().getDialogueNodeType() == DialogueNodeType.CHARACTERS_PRESENT
//				|| Main.game.getCurrentDialogueNode().getDialogueNodeType() == DialogueNodeType.PHONE
//				|| Main.game.getCurrentDialogueNode().getDialogueNodeType() == DialogueNodeType.OCCUPANT_MANAGEMENT) {
		if(Main.game.getCurrentDialogueNode().getDialogueNodeType()!=DialogueNodeType.INVENTORY
				&& (!Main.game.isInSex() || Main.game.getCurrentDialogueNode().isInventoryDisabled())
				&& !Main.game.isInCombat()
				&& (clothingEquipped!=null || weaponEquipped!=null)) {
			return;
		}

		TooltipUpdateThread.cancelThreads=true;

		if (item != null) {
			if(Main.game.getCurrentDialogueNode().getDialogueNodeType()!=DialogueNodeType.INVENTORY) {
				Main.mainController.openInventory();
			}
			if(Main.game.getDialogueFlags().values.contains(DialogueFlagValue.quickTrade)) {
				switch(InventoryDialogue.getNPCInventoryInteraction()) { //TODO
					case COMBAT:
						break;
					case FULL_MANAGEMENT:
						break;
					case SEX:
						break;
					case TRADING:
						break;
					case CHARACTER_CREATION:
						break;
				}

			} else {
				InventoryDialogue.setOwner(owner);
				InventoryDialogue.setItem(item);
				InventoryDialogue.setBuyBackIndex(buyBackIndex);
				Main.game.setResponseTab(1);
				Main.game.setContent(new Response("", "", InventoryDialogue.ITEM_INVENTORY));
			}

		} else if (clothing != null) {
			if(Main.game.getCurrentDialogueNode().getDialogueNodeType()!=DialogueNodeType.INVENTORY) {
				Main.mainController.openInventory();
			}
			InventoryDialogue.setOwner(owner);
			InventoryDialogue.setClothing(clothing);
			InventoryDialogue.setBuyBackIndex(buyBackIndex);
			Main.game.setResponseTab(1);
			Main.game.setContent(new Response("", "", InventoryDialogue.CLOTHING_INVENTORY));

		} else if (weapon != null) {
			if(Main.game.getCurrentDialogueNode().getDialogueNodeType()!=DialogueNodeType.INVENTORY) {
				Main.mainController.openInventory();
			}
			InventoryDialogue.setOwner(owner);
			InventoryDialogue.setWeapon(weaponSlot, weapon);
			InventoryDialogue.setBuyBackIndex(buyBackIndex);
			Main.game.setResponseTab(1);
			Main.game.setContent(new Response("", "", InventoryDialogue.WEAPON_INVENTORY));

		} else if (clothingEquipped != null) {
			if(Main.game.getCurrentDialogueNode().getDialogueNodeType()!=DialogueNodeType.INVENTORY) {
				Main.mainController.openInventory();
			}
			InventoryDialogue.setOwner(owner);
			InventoryDialogue.setClothing(clothingEquipped);
			InventoryDialogue.setBuyBackIndex(buyBackIndex);
			Main.game.setResponseTab(1);
			Main.game.setContent(new Response("", "", InventoryDialogue.CLOTHING_EQUIPPED));

		} else if (weaponEquipped != null) {
			if(Main.game.getCurrentDialogueNode().getDialogueNodeType()!=DialogueNodeType.INVENTORY) {
				Main.mainController.openInventory();
			}
			InventoryDialogue.setOwner(owner);
			InventoryDialogue.setWeapon(weaponSlot, weaponEquipped);
			InventoryDialogue.setBuyBackIndex(buyBackIndex);
			Main.game.setResponseTab(1);
			Main.game.setContent(new Response("", "", InventoryDialogue.WEAPON_EQUIPPED));
		}

		Main.mainController.getTooltip().hide();
	}

	public InventorySelectedItemEventListener setItemInventory(AbstractItem item, GameCharacter owner) {
		return setItemInventory(item, owner, 0);
	}
	public InventorySelectedItemEventListener setItemInventory(AbstractItem item, GameCharacter owner, int buyBackIndex) {
		resetVariables();
		this.item = item;
		this.buyBackIndex=buyBackIndex;
		setOwner(owner);
		return this;
	}

	public InventorySelectedItemEventListener setClothingInventory(AbstractClothing clothing, GameCharacter owner) {
		return setClothingInventory(clothing, owner, 0);
	}
	public InventorySelectedItemEventListener setClothingInventory(AbstractClothing clothing, GameCharacter owner, int buyBackIndex) {
		resetVariables();
		this.clothing = clothing;
		this.buyBackIndex=buyBackIndex;
		setOwner(owner);
		return this;
	}

	public InventorySelectedItemEventListener setWeaponInventory(AbstractWeapon weapon, GameCharacter owner) {
		return setWeaponInventory(weapon, owner, 0);
	}
	public InventorySelectedItemEventListener setWeaponInventory(AbstractWeapon weapon, GameCharacter owner, int buyBackIndex) {
		resetVariables();
		this.weapon = weapon;
		this.buyBackIndex=buyBackIndex;
		setOwner(owner);
		return this;
	}

	public InventorySelectedItemEventListener setWeaponEquipped(GameCharacter owner, InventorySlot invSlot) {
		resetVariables();
		setOwner(owner);
		weaponSlot = invSlot;

		if (owner != null) {
			if (invSlot == InventorySlot.WEAPON_MAIN_1) {
				weaponEquipped = owner.getMainWeapon(0);
			} else if (invSlot == InventorySlot.WEAPON_MAIN_2) {
				weaponEquipped = owner.getMainWeapon(1);
			} else if (invSlot == InventorySlot.WEAPON_MAIN_3) {
				weaponEquipped = owner.getMainWeapon(2);
			} else if (invSlot == InventorySlot.WEAPON_OFFHAND_1) {
				weaponEquipped = owner.getOffhandWeapon(0);
			} else if (invSlot == InventorySlot.WEAPON_OFFHAND_2) {
				weaponEquipped = owner.getOffhandWeapon(1);
			} else if (invSlot == InventorySlot.WEAPON_OFFHAND_3) {
				weaponEquipped = owner.getOffhandWeapon(2);
			}
		} else {
			weaponEquipped = null;
		}

		return this;
	}

	public InventorySelectedItemEventListener setClothingEquipped(GameCharacter owner, InventorySlot invSlot) {
		resetVariables();

		setOwner(owner);

		if (owner != null) {
			clothingEquipped = owner.getClothingInSlot(invSlot);
		} else {
			clothingEquipped = null;
		}

		return this;
	}

	private void setOwner(GameCharacter owner) {
		this.owner = owner;
	}

	private void resetVariables() {
		item = null;
		clothing = null;
		clothingEquipped = null;
		weapon = null;
		weaponEquipped = null;
		weaponSlot = null;
		owner = null;
		buyBackIndex = 0;
	}
}


code:
package com.lilithsthrone.controller.eventListeners;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.controller.MainController;
import com.lilithsthrone.main.Main;

/**
 * Sets the MainController's content.
 *
 * @since 0.1.0
 * @version 0.1.7
 * @author Innoxia
 */
public class SetContentEventListener implements EventListener {
	private int index;
	private boolean nextPage = false, previousPage = false;

	@Override
	public void handleEvent(Event event) {
		if (nextPage) {
			if (Main.game.isHasNextResponsePage()) {
				Main.game.setResponsePage(Main.game.getResponsePage() + 1);
				Main.game.updateResponses();
//				Main.game.setResponses(Main.game.getCurrentDialogueNode());
			}
		} else if (previousPage) {
			if (Main.game.getResponsePage() != 0) {
				Main.game.setResponsePage(Main.game.getResponsePage() - 1);
				Main.game.updateResponses();
//				Main.game.setResponses(Main.game.getCurrentDialogueNode());
			}
		} else {
			if (Main.game.getResponsePage() == 0)
				Main.game.setContent(Main.game.getResponsePage() * MainController.RESPONSE_COUNT + index);
			else {
				if (index != 0)
					Main.game.setContent(Main.game.getResponsePage() * MainController.RESPONSE_COUNT + index - 1);
				else
					Main.game.setContent(Main.game.getResponsePage() * MainController.RESPONSE_COUNT + (MainController.RESPONSE_COUNT-1));
			}

		}
	}

	public SetContentEventListener setIndex(int index) {
		this.index = index;

		nextPage = false;
		previousPage = false;
		return this;
	}

	public SetContentEventListener nextPage() {
		nextPage = true;
		previousPage = false;

		return this;
	}

	public SetContentEventListener previousPage() {
		nextPage = false;
		previousPage = true;

		return this;
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonCharactersEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.openCharactersPresent();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonCopyDialogueEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		StringSelection selection = new StringSelection(Main.game.getContentForClipboard());
	    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
	    clipboard.setContents(selection, selection);
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonInventoryEventHandler implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.openInventory();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonJournalEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.openPhone();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonMainMenuEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.openOptions();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonMoveEastEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.moveEast();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonMoveNorthEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.moveNorth();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonMoveSouthEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.moveSouth();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonMoveWestEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.moveWest();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.buttons;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.main.Main;

/**
 * @since 0.1.69.9
 * @version 0.1.69.9
 * @author Innoxia
 */
public class ButtonZoomEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.zoomMap();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.tooltips;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.controller.TooltipUpdateThread;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Colour;

/**
 * @since 0.1.69.9
 * @version 0.3.1
 * @author Innoxia
 */
public class TooltipCopyInfoEventListener implements EventListener {

	@Override
	public void handleEvent(Event event) {
		Main.mainController.setTooltipSize(360, 170);

		Main.mainController.setTooltipContent(
				"
"
				+(Main.game.getCurrentDialogueNode().getLabel() == "" || Main.game.getCurrentDialogueNode().getLabel() == null ? "-" : Main.game.getCurrentDialogueNode().getLabel())
				+ "
"
				+ "
"
				+ "Click to copy the currently displayed dialogue to your clipboard.

"
				+ "This scene was written by "
				+ Main.game.getCurrentDialogueNode().getAuthor()
				+ "
");

		TooltipUpdateThread.updateToolTip(-1,-1);
	}
}


code:
package com.lilithsthrone.controller.eventListeners.tooltips;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.controller.TooltipUpdateThread;
import com.lilithsthrone.main.Main;

/**
 * Hides the tooltip.
 *
 * @since 0.1.0
 * @version 0.1.3
 * @author Innoxia
 */
public class TooltipHideEventListener implements EventListener {
	@Override
	public void handleEvent(Event event) {
		TooltipUpdateThread.cancelThreads = true;
		Main.mainController.getTooltip().hide();
	}
}


code:
package com.lilithsthrone.controller.eventListeners.tooltips;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;

import com.lilithsthrone.controller.TooltipUpdateThread;
import com.lilithsthrone.game.PropertyValue;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.attributes.ArousalLevel;
import com.lilithsthrone.game.character.attributes.Attribute;
import com.lilithsthrone.game.character.attributes.CorruptionLevel;
import com.lilithsthrone.game.character.attributes.IntelligenceLevel;
import com.lilithsthrone.game.character.attributes.LustLevel;
import com.lilithsthrone.game.character.attributes.PhysiqueLevel;
import com.lilithsthrone.game.character.body.CoverableArea;
import com.lilithsthrone.game.character.body.Covering;
import com.lilithsthrone.game.character.body.types.AntennaType;
import com.lilithsthrone.game.character.body.types.BodyCoveringType;
import com.lilithsthrone.game.character.body.types.HornType;
import com.lilithsthrone.game.character.body.types.TailType;
import com.lilithsthrone.game.character.body.types.VaginaType;
import com.lilithsthrone.game.character.body.types.WingType;
import com.lilithsthrone.game.character.body.valueEnums.BreastShape;
import com.lilithsthrone.game.character.body.valueEnums.CoveringPattern;
import com.lilithsthrone.game.character.body.valueEnums.Femininity;
import com.lilithsthrone.game.character.effects.AbstractPerk;
import com.lilithsthrone.game.character.effects.PerkCategory;
import com.lilithsthrone.game.character.effects.PerkManager;
import com.lilithsthrone.game.character.effects.StatusEffect;
import com.lilithsthrone.game.character.fetishes.Fetish;
import com.lilithsthrone.game.character.fetishes.FetishDesire;
import com.lilithsthrone.game.character.fetishes.FetishLevel;
import com.lilithsthrone.game.character.npc.NPC;
import com.lilithsthrone.game.character.npc.misc.Elemental;
import com.lilithsthrone.game.character.race.Race;
import com.lilithsthrone.game.combat.Attack;
import com.lilithsthrone.game.combat.CombatMove;
import com.lilithsthrone.game.combat.Spell;
import com.lilithsthrone.game.combat.SpellUpgrade;
import com.lilithsthrone.game.dialogue.places.dominion.lilayashome.Library;
import com.lilithsthrone.game.dialogue.utils.InventoryDialogue;
import com.lilithsthrone.game.dialogue.utils.InventoryInteraction;
import com.lilithsthrone.game.dialogue.utils.UtilText;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
import com.lilithsthrone.game.inventory.enchanting.ItemEffect;
import com.lilithsthrone.game.inventory.enchanting.LoadedEnchantment;
import com.lilithsthrone.game.inventory.enchanting.TFEssence;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.rendering.CachedImage;
import com.lilithsthrone.rendering.ImageCache;
import com.lilithsthrone.rendering.RenderingEngine;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Units;
import com.lilithsthrone.utils.Util;
import com.lilithsthrone.utils.Util.Value;
import com.lilithsthrone.world.Cell;
import com.lilithsthrone.world.WorldType;

/**
 * @since 0.1.0
 * @version 0.3.4
 * @author Innoxia
 */
public class TooltipInformationEventListener implements EventListener {
	private String title;
	private String description;

	private boolean extraAttributes = false;
	private boolean weather = false;
	private boolean protection = false;
	private boolean copyInformation = false;
	private boolean availableForSelection = false;

	private GameCharacter owner;
	private StatusEffect statusEffect;
	private AbstractPerk perk;
	private AbstractPerk levelUpPerk;
	private int perkRow;
	private Fetish fetish;
	private boolean fetishExperience = false;
	private FetishDesire desire;
	private Spell spell;
	private SpellUpgrade spellUpgrade;
	private Attribute attribute;
	private InventorySlot concealedSlot;
	private LoadedEnchantment loadedEnchantment;
	private CombatMove move;
	private Cell cell;
	private GameCharacter moneyTransferTarget;
	private int moneyTransferPercentage;

	private static StringBuilder tooltipSB  = new StringBuilder();

	private int descriptionHeightOverride;

	private static final int LINE_HEIGHT= 16;

	@Override
	public void handleEvent(Event event) {
		Main.mainController.setTooltipSize(360, 180);
		Main.mainController.setTooltipContent("");

		if (statusEffect != null) {

			// I hate this. If only JavaFX's height detection and resizing methods actually worked...
			int size = statusEffect.getModifiersAsStringList(owner).size();
			int yIncrease = (size > 4 ? size - 4 : 0)
								+ (owner.hasStatusEffect(statusEffect)?(owner.getStatusEffectDuration(statusEffect) == -1 ? 0 : 2):0);

			if(statusEffect.getAdditionalDescription(owner)!=null && !statusEffect.getAdditionalDescription(owner).isEmpty()) {
				yIncrease += 7;
			}

			Main.mainController.setTooltipSize(360, 284 + (yIncrease * LINE_HEIGHT));

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append(""
					+ "
" + Util.capitaliseSentence(statusEffect.getName(owner)) + "
");

			// Attribute modifiers:
			tooltipSB.append("
");// style='white-space: nowrap'>");
				if (!statusEffect.getModifiersAsStringList(owner).isEmpty()) {
					int i=0;
					for (String s : statusEffect.getModifiersAsStringList(owner)) {
						tooltipSB.append((i!=0?"
":"") + s);
						i++;
					}

				} else {
					tooltipSB.append("No bonuses");
				}
			tooltipSB.append("
");

			// Picture:
			tooltipSB.append("
"
								+ statusEffect.getSVGString(owner)
							+ "
"
							+ "
"
								+ statusEffect.getDescription(owner)
							+ "
");

			if(statusEffect.getAdditionalDescription(owner)!=null && !statusEffect.getAdditionalDescription(owner).isEmpty()) {
				tooltipSB.append("
"
						+ statusEffect.getAdditionalDescription(owner)
					+ "
");
			}

			if(owner.hasStatusEffect(statusEffect)) {
				if (owner.getStatusEffectDuration(statusEffect) != -1) {
					if (statusEffect.isCombatEffect()) {
						tooltipSB.append("
Turns remaining: " + owner.getStatusEffectDuration(statusEffect) + "
");
					} else {
						int timerHeight = (int) ((owner.getStatusEffectDuration(statusEffect)/(60*60*6f))*100);

						Colour timerColour = Colour.STATUS_EFFECT_TIME_HIGH;

						if(timerHeight>100) {
							timerHeight=100;
							timerColour = Colour.STATUS_EFFECT_TIME_OVERFLOW;
						} else if(timerHeight<15) {
							timerColour = Colour.STATUS_EFFECT_TIME_LOW;
						} else if (timerHeight<50) {
							timerColour = Colour.STATUS_EFFECT_TIME_MEDIUM;
						}
						int minutes = Math.max(1, owner.getStatusEffectDuration(statusEffect)/60);
						int hours = minutes/60;
						int days = hours/24;

						tooltipSB.append("
Time remaining: "
								+ ""
								+(days>0
									?days+" day"+(days>1?"s":"")
										+(hours%24>0
												?" "+(hours%24)+" hour"+((hours%24)>1?"s":"")
														+ (minutes%60>0
																?" "+(minutes%60)+" minute"+((minutes%60)>1?"s":"")
																		:"")
												:(minutes%60>0
														?" "+(minutes%60)+" minute"+((minutes%60)>1?"s":"")
																:""))
									:(hours>0
											?" "+(hours)+" hour"+((hours)>1?"s":"")
													+ (minutes%60>0
															?" "+(minutes%60)+" minute"+((minutes%60)>1?"s":"")
																	:"")
											:(minutes)+" minute"+((minutes)>1?"s":"")))
								+ ""
								+ "
");
						//STATUS_EFFECT_TIME_OVERFLOW
					}
				}
			}

			tooltipSB.append("");

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

			// Wasted more time trying to get JavaFX to return sensible height values.
//			int height = Integer.valueOf(((String) Main.mainController.getWebEngineTooltip().executeScript("window.getComputedStyle(document.body, null).getPropertyValue('height')")).replace("px", ""));
////					"Math.max( document.body.scrollHeight, document.body.offsetHeight );");
//
//			System.out.println(height);
//
//			Main.mainController.setTooltipSize(360, height+8);

		} else if (perk != null) { // Perks:

			int yIncrease = (perk.getModifiersAsStringList(owner).size() > 4 ? perk.getModifiersAsStringList(owner).size() - 4 : 0);

			Main.mainController.setTooltipSize(360, 324 + (yIncrease * LINE_HEIGHT));

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append("
" + Util.capitaliseSentence(perk.getName(owner)) + "
");

			if(perk.isEquippableTrait()) {
				tooltipSB.append("
Trait
");
			} else {
				tooltipSB.append("
Perk
");
			}

			// Attribute modifiers:
			tooltipSB.append("
");
			if (!perk.getModifiersAsStringList(owner).isEmpty()) {
				int i=0;
				for (String s : perk.getModifiersAsStringList(owner)) {
					tooltipSB.append((i!=0?"
":"") + s);
					i++;
				}
			} else
				tooltipSB.append("Perk" + "
None");
			tooltipSB.append("
");

			// Picture:
			tooltipSB.append("
" + perk.getSVGString(owner) + "
");

			// Description:
			tooltipSB.append("
" + UtilText.parse(owner, perk.getDescription(owner)) + "
");

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (levelUpPerk != null) { // Level Up Perk (same as Perk, but with requirements at top):

			int yIncrease = (levelUpPerk.getModifiersAsStringList(owner).size() > 4 ? levelUpPerk.getModifiersAsStringList(owner).size() - 4 : 0);

			Main.mainController.setTooltipSize(360, 320 + (availableForSelection?32:0) + (yIncrease * LINE_HEIGHT));

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append("
" + Util.capitaliseSentence(levelUpPerk.getName(owner)) + "
");

			if(levelUpPerk.isEquippableTrait()) {
				if(levelUpPerk.getPerkCategory()==PerkCategory.JOB) {
					tooltipSB.append("
'"+Util.capitaliseSentence(owner.getHistory().getName())+"' Occupation Trait
");
				} else if(levelUpPerk.isHiddenPerk()) {
					tooltipSB.append("
Unique Trait
");
				} else {
					tooltipSB.append("
Trait
");
				}
			} else {
				 if(levelUpPerk.isHiddenPerk()) {
					tooltipSB.append("
Unique Perk
");
				} else {
					tooltipSB.append("
Perk
");
				}
			}

			// Attribute modifiers:
			tooltipSB.append("
");
			if (!levelUpPerk.getModifiersAsStringList(owner).isEmpty()) {
				int i=0;
				for (String s : levelUpPerk.getModifiersAsStringList(owner)) {
					tooltipSB.append((i!=0?"
":"") + s);
					i++;
				}
			} else {
				tooltipSB.append("Perk" + "
None");
			}
			tooltipSB.append("
");

			// Picture:
			tooltipSB.append("
" + levelUpPerk.getSVGString(owner) + "
");

			// Description:
			tooltipSB.append("
" + UtilText.parse(owner, levelUpPerk.getDescription(owner)) + "
");

			if(availableForSelection) {
				if(levelUpPerk.isEquippableTrait()) {
					if(levelUpPerk.getPerkCategory()==PerkCategory.JOB) {
						tooltipSB.append("
Occupation traits cannot be removed.
");

					} else {
						if(!owner.hasPerkInTree(perkRow, levelUpPerk)) {
							if(!PerkManager.MANAGER.isPerkAvailable(owner, perkRow, levelUpPerk)) {
								tooltipSB.append("
Purchasing requires a connecting perk or trait.
");
							} else {
								tooltipSB.append("
Click to purchase trait.
");
							}
						} else {
							if(owner.getTraits().contains(levelUpPerk)) {
								tooltipSB.append("
Click to unequip trait.
");
							} else {
								if(owner.getTraits().size()==GameCharacter.MAX_TRAITS) {
									tooltipSB.append("
Maximum traits activated.
");
								} else {
									tooltipSB.append("
Click to equip trait.
");
								}
							}
						}
					}

				} else {
					if(!owner.hasPerkInTree(perkRow, levelUpPerk) && !levelUpPerk.isHiddenPerk()) {
						if(!PerkManager.MANAGER.isPerkAvailable(owner, perkRow, levelUpPerk)) {
							tooltipSB.append("
Purchasing requires a connecting perk or trait.
");
						} else {
							tooltipSB.append("
Click to purchase perk.
");
						}

					} else {
						tooltipSB.append("
"
											+ UtilText.parse(owner, "[npc.Name] already [npc.verb(own)] this perk!")
										+ "
");
					}
				}
			}

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (move != null) {
			List<String> critReqs = move.getCritRequirements(owner, null, null, null);

			int currentCooldown = owner.getMoveCooldown(move.getIdentifier());

			Main.mainController.setTooltipSize(360,
					352
					+ (critReqs.size()>0?(32+critReqs.size()*16):0)
					+ (currentCooldown>0?32:0));

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append("
" + Util.capitaliseSentence(move.getName(0, owner)) + "
");

			boolean coreMove = owner.getEquippedMoves().contains(move);

			tooltipSB.append("
"+(coreMove?"[style.colourMinorGood(Core)]":"[style.colourMinorBad(Non-core)]")+"
");
			tooltipSB.append("
"+move.getType().getName()+"
");

			if(currentCooldown>0) {
				tooltipSB.append("
On cooldown: "+currentCooldown+(currentCooldown==1?" turn":" turns")+"
");
			}

			// Picture:
			tooltipSB.append("
" + move.getSVGString() + "
");

			// Description:
			tooltipSB.append("
");

			int apCost = move.getAPcost(owner);
			int cooldown = move.getCooldown(owner);

			tooltipSB.append(
					"AP cost: "
						+""
						+(coreMove?apCost:(apCost-1)+"[style.colourBad(+1)]")
						+""
					+ "
Cooldown: "
						+ "<span style='color:"+(cooldown-(coreMove?0:1)<=0?Colour.GENERIC_MINOR_GOOD:Colour.GENERIC_MINOR_BAD).toWebHexString()+";'>"
						+(coreMove?cooldown:(cooldown-1)+"[style.colourBad(+1)]")
						+" turn"+(cooldown==1?"":"s"));

//			tooltipSB.append("AP cost: "+""+apCost+"");
//			tooltipSB.append("
Cooldown: "+""+cooldown+(cooldown==1?" turn":" turns")+"");

			if(move.getStatusEffects(owner, owner, false)!=null) {
				for(Entry<StatusEffect, Integer> entry : move.getStatusEffects(owner, owner, false).entrySet()) {
					tooltipSB.append("
Applies: "+Util.capitaliseSentence(entry.getKey().getName(null))+" for "+entry.getValue()+(entry.getValue()==1?" turn":" turns"));
				}
			}
			tooltipSB.append("
");

			// Description:
			Value<Boolean, String> availableValue = owner.isMoveAvailable(move.getIdentifier());

			tooltipSB.append(
					"
"
						+""+availableValue.getValue()+" "
						+ move.getDescription(owner)
					+ "
");

			tooltipSB.append("
Critically hits when:");
			for(String s : critReqs) {
				tooltipSB.append("
"+s);
			}
			tooltipSB.append("
");

			if(owner.getEquippedMoves().contains(move)) {
				tooltipSB.append("
Click to unequip move.
");
			} else {
				if(owner.getEquippedMoves().size()>=GameCharacter.MAX_COMBAT_MOVES) {
					tooltipSB.append("
Maximum core moves selected.
");
				} else {
					tooltipSB.append("
Click to equip move.
");
				}
			}

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (desire != null) { // Desire:

			Main.mainController.setTooltipSize(360, 264);

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append("
Set Desire: "+Util.capitaliseSentence(desire.getName())+"
");

			// Attribute modifiers:
			tooltipSB.append("
");
			int i=0;
			for (String s : desire.getModifiersAsStringList()) {
				tooltipSB.append((i!=0?"
":"") + s);
				i++;
			}
			tooltipSB.append("
");

			// Picture:
			tooltipSB.append("
" + desire.getSVGImage() + "
");

			// Description:
			if(owner.hasFetish(fetish) && desire!=FetishDesire.FOUR_LOVE) {
				tooltipSB.append("
Your desire is [style.boldBad(locked)] to "+FetishDesire.FOUR_LOVE.getName()+","
						+ " due to owning the related fetish ("+fetish.getName(owner)+").
");
				tooltipSB.append("
Cost: [style.boldDisabled(N/A)]
");
			} else {
				tooltipSB.append("
" + fetish.getFetishDesireDescription(owner, desire) + "
");
				if(owner.getBaseFetishDesire(fetish)==desire) {
					tooltipSB.append("
Cost: [style.boldDisabled(N/A)]
");
				} else {
					tooltipSB.append("
Cost: [style.boldArcane("
							+ (FetishDesire.getCostToChange()==0
								?"Free"
								:Integer.toString(FetishDesire.getCostToChange())+" Arcane Essence"+(FetishDesire.getCostToChange()>1?"s":""))
							+ ")]
");
				}
			}

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (fetish != null) { // Fetishes:

			if(fetishExperience) {

				Main.mainController.setTooltipSize(420, 156);

				tooltipSB.setLength(0);
				tooltipSB.append("
" + Util.capitaliseSentence(fetish.getName(owner)) + " fetish
");
				FetishLevel level = FetishLevel.getFetishLevelFromValue(owner.getFetishExperience(fetish));
				tooltipSB.append("
Level "+level.getNumeral()+": "+Util.capitaliseSentence(level.getName())+""
									+ " | " + owner.getFetishExperience(fetish) +" / "+ level.getMaximumExperience() + " xp" + "
");
				tooltipSB.append("
You earn fetish experience by performing related actions in sex. Each level increases the fetish's bonuses (max level is 5).
");

				Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

			} else {
				int yIncrease = (fetish.getModifiersAsStringList(owner).size() > 4 ? fetish.getModifiersAsStringList(owner).size() - 4 : 0) + fetish.getFetishesForAutomaticUnlock().size();

				Main.mainController.setTooltipSize(360, (fetish.getFetishesForAutomaticUnlock().isEmpty()?350:360) + (yIncrease * LINE_HEIGHT));

				// Title:
				tooltipSB.setLength(0);
				tooltipSB.append("
" + Util.capitaliseSentence(fetish.getName(owner)) + " fetish
");
				FetishLevel level = FetishLevel.getFetishLevelFromValue(owner.getFetishExperience(fetish));
				tooltipSB.append("
Level "+level.getNumeral()+": "+Util.capitaliseSentence(level.getName())+""
						+ " | " + owner.getFetishExperience(fetish) +" / "+ level.getMaximumExperience() + " xp" + "
");

				// Requirements:
				if(!fetish.getFetishesForAutomaticUnlock().isEmpty()) {
					tooltipSB.append("
Requirements");
					for (Fetish f : fetish.getFetishesForAutomaticUnlock())
						tooltipSB.append("
[style.boldArcane(" + Util.capitaliseSentence(f.getName(Main.game.getPlayer()))+")]");
					tooltipSB.append("
");
				}

				// Attribute modifiers:
				tooltipSB.append("
");
				if (!fetish.getModifiersAsStringList(owner).isEmpty()) {
					int i=0;
					for (String s : fetish.getModifiersAsStringList(owner)) {
						tooltipSB.append((i!=0?"
":"") + s);
						i++;
					}
				} else {
					tooltipSB.append("Fetish" + "
None");
				}
				tooltipSB.append("
");

				// Picture:
				tooltipSB.append("
" + fetish.getSVGString(owner) + "
");

				// Description:
				tooltipSB.append("
" + fetish.getDescription(owner) + "
");

				if(fetish.getFetishesForAutomaticUnlock().isEmpty()) {
					if(owner.hasBaseFetish(fetish)) {
						tooltipSB.append("
Cost: [style.boldDisabled(N/A)]
");
					} else {
						tooltipSB.append("
Cost: [style.boldArcane("+fetish.getCost()+" Arcane Essences)]
");
					}
				}

				Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));
			}

		} else if (spell != null) { // Spells:

			int yIncrease = (spell.getModifiersAsStringList().size() > 5 ? spell.getModifiersAsStringList().size() - 5 : 0);

			Main.mainController.setTooltipSize(360, 332 + (yIncrease * LINE_HEIGHT));

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append("
" + Util.capitaliseSentence(spell.getName()) + "
");

			// Attribute modifiers:
			tooltipSB.append("
");

			if(spell.getDamage(Main.game.getPlayer())>0) {
				tooltipSB.append(
						"Base "+spell.getDamage(owner)+" " + Util.capitaliseSentence(spell.getDamageType().getName()) + " Damage
"
						+""
							+ Attack.getMinimumSpellDamage(owner, null, spell.getDamageType(), spell.getDamage(owner), spell.getDamageVariance())
							+ "-"
							+ Attack.getMaximumSpellDamage(owner, null, spell.getDamageType(), spell.getDamage(owner), spell.getDamageVariance())
						+ ""
						+ " " + Util.capitaliseSentence(spell.getDamageType().getName()) + " Damage
");
			}

			if(!spell.getModifiersAsStringList().isEmpty()) {
				for(int i=0; i<spell.getModifiersAsStringList().size(); i++) {
					tooltipSB.append(spell.getModifiersAsStringList().get(i)+(i<spell.getModifiersAsStringList().size()-1?"
":""));
				}
			} else {
				tooltipSB.append("No effects
");
			}
			tooltipSB.append("
");

			// Picture:
			tooltipSB.append("
" + spell.getSVGString() + "
");

			// Description & turns remaining:
			tooltipSB.append(
					"
"
							+ (spell.isForbiddenSpell() && !owner.hasSpell(spell)?"[style.italicsArcane(This is a forbidden spell, and can only be discovered through a special quest!)]
":"")
							+ spell.getDescription(owner)
					+ "
"
					+ "
"
						+ "Costs " + (spell.getModifiedCost(owner)) + " aura"
					+ "
");

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (spellUpgrade != null) { // Spell upgrades:

			int yIncrease = (spellUpgrade.getModifiersAsStringList().size() > 5 ? spellUpgrade.getModifiersAsStringList().size() - 5 : 0);

			Main.mainController.setTooltipSize(360, 316 + (yIncrease * LINE_HEIGHT));

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append("
" + Util.capitaliseSentence(spellUpgrade.getName()) + "
");

			// Attribute modifiers:
			tooltipSB.append("
");

			if(!spellUpgrade.getModifiersAsStringList().isEmpty()) {
				for(int i=0; i<spellUpgrade.getModifiersAsStringList().size(); i++) {
					tooltipSB.append(spellUpgrade.getModifiersAsStringList().get(i)+(i<spellUpgrade.getModifiersAsStringList().size()-1?"
":""));
				}
			} else {
				tooltipSB.append("No effects
");
			}

			tooltipSB.append("
");

			// Picture:
			tooltipSB.append("
" + spellUpgrade.getSVGString() + "
");

			// Description:
			tooltipSB.append(
					"
"
							+ spellUpgrade.getDescription()+" "+spellUpgrade.getUnavailableReason(owner)
					+ "
"
					+ "
"
						+ (owner.hasSpellUpgrade(spellUpgrade)
								?"[style.boldExcellent(Owned)] (Cost "+spellUpgrade.getPointCost()+" Point"+(spellUpgrade.getPointCost()==1?"":"s")+")"
								:(owner.getSpellUpgradePoints(spellUpgrade.getSpellSchool()) >= spellUpgrade.getPointCost()
										?"Costs "+spellUpgrade.getPointCost()+" Point"+(spellUpgrade.getPointCost()==1?"":"s")+" - [style.colourGood(Can afford!)]"
										:"Costs "+spellUpgrade.getPointCost()+" Point"+(spellUpgrade.getPointCost()==1?"":"s")+" - [style.colourBad(Cannot afford!)]"))
					+ "
");

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (attribute != null) {

			if (attribute == Attribute.MAJOR_PHYSIQUE
					|| attribute == Attribute.MAJOR_ARCANE
					|| attribute == Attribute.MAJOR_CORRUPTION
					|| attribute == Attribute.AROUSAL
					|| attribute == Attribute.LUST) {
				StatusEffect currentAttributeStatusEffect=null;
				int minimumLevelValue=0, maximumLevelValue=0;

				if(attribute == Attribute.MAJOR_PHYSIQUE) {
					currentAttributeStatusEffect = PhysiqueLevel.getPhysiqueLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_PHYSIQUE)).getRelatedStatusEffect();
					minimumLevelValue = PhysiqueLevel.getPhysiqueLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_PHYSIQUE)).getMinimumValue();
					maximumLevelValue = PhysiqueLevel.getPhysiqueLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_PHYSIQUE)).getMaximumValue();

				} else if(attribute == Attribute.MAJOR_ARCANE) {
					currentAttributeStatusEffect = IntelligenceLevel.getIntelligenceLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_ARCANE)).getRelatedStatusEffect();
					minimumLevelValue = IntelligenceLevel.getIntelligenceLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_ARCANE)).getMinimumValue();
					maximumLevelValue = IntelligenceLevel.getIntelligenceLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_ARCANE)).getMaximumValue();

				} else if(attribute == Attribute.MAJOR_CORRUPTION) {
					currentAttributeStatusEffect = CorruptionLevel.getCorruptionLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_CORRUPTION)).getRelatedStatusEffect();
					minimumLevelValue = CorruptionLevel.getCorruptionLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_CORRUPTION)).getMinimumValue();
					maximumLevelValue = CorruptionLevel.getCorruptionLevelFromValue(owner.getAttributeValue(Attribute.MAJOR_CORRUPTION)).getMaximumValue();

				} else if(attribute == Attribute.AROUSAL) {
					currentAttributeStatusEffect = ArousalLevel.getArousalLevelFromValue(owner.getAttributeValue(Attribute.AROUSAL)).getRelatedStatusEffect();
					minimumLevelValue = ArousalLevel.getArousalLevelFromValue(owner.getAttributeValue(Attribute.AROUSAL)).getMinimumValue();
					maximumLevelValue = ArousalLevel.getArousalLevelFromValue(owner.getAttributeValue(Attribute.AROUSAL)).getMaximumValue();

				} else if(attribute == Attribute.LUST) {
					currentAttributeStatusEffect = LustLevel.getLustLevelFromValue(owner.getAttributeValue(Attribute.LUST)).getRelatedStatusEffect();
					minimumLevelValue = LustLevel.getLustLevelFromValue(owner.getAttributeValue(Attribute.LUST)).getMinimumValue();
					maximumLevelValue = LustLevel.getLustLevelFromValue(owner.getAttributeValue(Attribute.LUST)).getMaximumValue();
				}

				int yIncrease = (currentAttributeStatusEffect.getModifiersAsStringList(owner).size() > 4 ? currentAttributeStatusEffect.getModifiersAsStringList(owner).size() - 4 : 0)
						+ (owner.hasStatusEffect(currentAttributeStatusEffect)?(owner.getStatusEffectDuration(currentAttributeStatusEffect) == -1 ? 0 : 2):0);

				Main.mainController.setTooltipSize(360, 460 + (yIncrease * LINE_HEIGHT));

				tooltipSB.setLength(0);
				tooltipSB.append("
" + Util.capitaliseSentence(attribute.getName()) + "
"

						+ "
" + "Core
"
						+ (owner.getBaseAttributeValue(attribute) > 0 ? "" : "")
							+ Units.number(owner.getBaseAttributeValue(attribute), 1, 1)
						+ "" + "
"

						+ "
" + "Bonus
"
						+ ((owner.getBonusAttributeValue(attribute)) > 0 ? ""
								: ((owner.getBonusAttributeValue(attribute)) == 0 ? "" : ""))
						+ Units.number(owner.getBonusAttributeValue(attribute), 1, 1)+ "" + "
"

						+ "
" + "Total
" + Units.number(owner.getAttributeValue(attribute), 1, 1)
						+ "" + "
");

				tooltipSB.append("
" + attribute.getDescription(owner) + "
");

				// Related status effect:
				tooltipSB.append("
"
												+ ""
												+ currentAttributeStatusEffect.getName(owner)
												+" ("+minimumLevelValue
												+"-"
												+ maximumLevelValue
												+")"
												+ "
");

				// Attribute modifiers:
				tooltipSB.append("
");
				if (!currentAttributeStatusEffect.getModifiersAsStringList(owner).isEmpty()) {
					int i=0;
					for (String s : currentAttributeStatusEffect.getModifiersAsStringList(owner)) {
						if(i!=0) {
							tooltipSB.append("
");
						}
						tooltipSB.append(s);
						i++;
					}
				} else {
					tooltipSB.append("No bonuses");
				}
				tooltipSB.append("
");

				// Picture:
				tooltipSB.append("
" + currentAttributeStatusEffect.getSVGString(owner) + "
");

				// Description & turns remaining:
				tooltipSB.append("
" + currentAttributeStatusEffect.getDescription(owner) + "
");

				Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

			} else if (attribute == Attribute.EXPERIENCE) {
				// Special tooltip for experience/transformation combo:

				if(owner.isRaceConcealed()) {
					Main.mainController.setTooltipSize(420, 64);

					tooltipSB.setLength(0);
					tooltipSB.append("
"
							+ "Unknown Race!"
							+ "
");

				} else {
					CachedImage image = null;
					boolean displayImage = Main.getProperties().hasValue(PropertyValue.thumbnail)
							&& Main.getProperties().hasValue(PropertyValue.artwork);
					if (displayImage) {
						if (owner.hasArtwork()) {
							image = ImageCache.INSTANCE.requestImage(owner.getCurrentArtwork().getCurrentImage());
						}
						displayImage = image != null;
					}

					boolean crotchBreasts = owner.hasBreastsCrotch()
							&& Main.getProperties().udders>0
							&& (owner.isBreastsCrotchVisibleThroughClothing()||owner.isAreaKnownByCharacter(CoverableArea.NIPPLES_CROTCH, Main.game.getPlayer()));
					int crotchBreastAddition = crotchBreasts?24:0;

					int[] dimensions = new int[]{419, 508+crotchBreastAddition};
					int imagePadding = 0;
					int imageWidth = 0;
					if (displayImage) {
						// Add the scaled width to the tooltip dimensions
						int[] scaledSize = image.getAdjustedSize(300, 445);
						imageWidth = scaledSize[0];
						dimensions[0] += scaledSize[0];
						// ... and place it in the bottom right corner of the tooltip
						imagePadding = Math.max(0, 455 - scaledSize[1]);
					}

					Main.mainController.setTooltipSize(dimensions[0], dimensions[1]);

					tooltipSB.setLength(0);
					tooltipSB.append("
"
							+(owner.getRaceStage().getName()!=""?"" + Util.capitaliseSentence(owner.getRaceStage().getName())+" ":"")
							+ ""
							+ (owner.isFeminine()?Util.capitaliseSentence(owner.getSubspecies().getSingularFemaleName(owner)):Util.capitaliseSentence(owner.getSubspecies().getSingularMaleName(owner)))
							+ ""
							+ "
");

					if (displayImage) {
						tooltipSB.append("
");
					}

					// GREATER:
					tooltipSB.append(getBodyPartDivFace(owner, "Face", owner.getFaceRace(), owner.getFaceCovering(), owner.isFaceBestial()));
					tooltipSB.append(getBodyPartDiv(owner, "Torso", owner.getSkinRace(), owner.getSkinCovering(), owner.isSkinBestial(),
							(owner.isSizeDifferenceShorterThan(Main.game.getPlayer())
							?""
							:(owner.isSizeDifferenceTallerThan(Main.game.getPlayer())
								?""
								:""))
							+"Height: [unit.sizeShort(" + owner.getHeightValue()+ ")]"));

					// LESSER:
					tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(Util.intToString(owner.getArmRows()*2))+" arms", owner.getArmRace(), owner.getArmCovering(), owner.isArmBestial()));
					tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(Util.intToString(owner.getLegCount()))+" "+owner.getFootStructure().getName()+" legs", owner.getLegRace(), owner.getLegCovering(), owner.isLegBestial()));

					// PARTIAL:
					tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(owner.getHairLength().getDescriptor())+" "+owner.getHairStyle().getName()+" "+owner.getHairName(), owner.getHairRace(), owner.getHairCovering(), owner.isHairBestial()));
					tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(Util.intToString(owner.getEyePairs()*2))+" eyes", owner.getEyeRace(), owner.getEyeCovering(), owner.isEyeBestial()));
					tooltipSB.append(getBodyPartDiv(owner, "Ears", owner.getEarRace(), owner.getEarCovering(), owner.isEarBestial()));
					tooltipSB.append(getBodyPartDiv(owner, "Tongue", owner.getTongueRace(), owner.getTongueCovering(), owner.isTongueBestial()));
					if (owner.getHornType() != HornType.NONE) {
						tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(Util.intToString(owner.getTotalHorns()))+" "+(owner.getTotalHorns()==1?owner.getHornNameSingular():owner.getHornName()),
								owner.getHornRace(), owner.getHornCovering(), owner.isHornBestial()));
					} else {
						tooltipSB.append(getEmptyBodyPartDiv("Horns", "None"));
					}
					if (owner.getAntennaType() != AntennaType.NONE) {
						//TODO might need changing if made like horn count:
						tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(Util.intToString(owner.getAntennaRows()*2))+" antennae", owner.getAntennaRace(), owner.getAntennaCovering(), owner.isAntennaBestial()));
					} else {
						tooltipSB.append(getEmptyBodyPartDiv("Antennae", "None"));
					}
					if (owner.getWingType() != WingType.NONE) {
						tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(owner.getWingSize().getName())+" wings", owner.getWingRace(), owner.getWingCovering(), owner.isWingBestial()));
					} else {
						tooltipSB.append(getEmptyBodyPartDiv("Wings", "None"));
					}
					if (owner.getTailType() != TailType.NONE) {
						tooltipSB.append(getBodyPartDiv(owner, Util.capitaliseSentence(Util.intToString(owner.getTailCount()))+" tail"+(owner.getTailCount()!=1?"s":""), owner.getTailRace(), owner.getTailCovering(), owner.isTailBestial()));
					} else {
						tooltipSB.append(getEmptyBodyPartDiv("Tail", "None"));
					}

					// SEXUAL:
					if(!owner.isPlayer() && !owner.isAreaKnownByCharacter(CoverableArea.VAGINA, Main.game.getPlayer())) {
						tooltipSB.append(getEmptyBodyPartDiv("Vagina", "Unknown!"));
					} else {
						if (owner.getVaginaType() != VaginaType.NONE) {
							tooltipSB.append(getBodyPartDiv(owner, "Vagina", owner.getVaginaRace(), owner.getVaginaCovering(), owner.isVaginaBestial()));
						} else {
							tooltipSB.append(getEmptyBodyPartDiv("Vagina", "None"));
						}
					}

					if(!owner.isPlayer() && !owner.isAreaKnownByCharacter(CoverableArea.PENIS, Main.game.getPlayer())) {
						tooltipSB.append(getEmptyBodyPartDiv("Penis", "Unknown!"));
					} else {
						if (owner.hasPenisIgnoreDildo()) {
							tooltipSB.append(getBodyPartDiv(owner, "Penis", owner.getPenisRace(), owner.getPenisCovering(), owner.isPenisBestial(), "[unit.sizes(" + owner.getPenisRawSizeValue()+ ")]"));
						} else if (owner.hasPenis()) {
							tooltipSB.append(getBodyPartDiv(owner, "Penis", owner.getPenisRace(), owner.getPenisCovering(), owner.isPenisBestial(), "[unit.sizes(" + owner.getPenisRawSizeValue()+ ")]"));
						} else {
							tooltipSB.append(getEmptyBodyPartDiv("Penis", "None"));
						}
					}

					if(!owner.isPlayer() && !owner.isAreaKnownByCharacter(CoverableArea.ANUS, Main.game.getPlayer())) {
						tooltipSB.append(getEmptyBodyPartDiv("Anus", "Unknown!"));
					} else {
						tooltipSB.append(getBodyPartDiv(owner, "Anus", owner.getAssRace(), owner.getAssType().getAnusType().getBodyCoveringType(owner), owner.isAnusBestial()));
					}

					if(!owner.isPlayer() && !owner.isAreaKnownByCharacter(CoverableArea.NIPPLES, Main.game.getPlayer())) {
						tooltipSB.append(getEmptyBodyPartDiv("Nipples",
								"Unknown!",
								Util.capitaliseSentence(Util.intToString(owner.getBreastRows()*2))+" "+(owner.getBreastRawSizeValue()>0?(owner.getBreastSize().getCupSizeName() + "-cup breasts"):(owner.isFeminine()?"flat breasts":"pecs"))));
					} else {
						tooltipSB.append(getBodyPartDiv(owner, "Nipples",
								owner.getBreastRace(),
								owner.getBreastType().getNippleType().getBodyCoveringType(owner),
								owner.isNippleBestial(),
								Util.capitaliseSentence(Util.intToString(owner.getBreastRows()*2))+" "+(owner.getBreastRawSizeValue()>0?(owner.getBreastSize().getCupSizeName() + "-cup breasts"):(owner.isFeminine()?"flat breasts":"pecs"))));
					}

					if(crotchBreasts) {
						if(!owner.isAreaKnownByCharacter(CoverableArea.NIPPLES_CROTCH, Main.game.getPlayer())) {
							tooltipSB.append(getEmptyBodyPartDiv("Nipples",
									"Unknown!",
									Util.capitaliseSentence(Util.intToString(owner.getBreastCrotchRows()*2))+" "
											+(owner.getBreastRawSizeValue()>0?(owner.getBreastCrotchSize().getCupSizeName() + "-cup "):"flat ")+(owner.getBreastCrotchShape()==BreastShape.UDDERS?"udders":"crotch-boobs")));
						} else {
							tooltipSB.append(getBodyPartDiv(owner, "Nipples",
									owner.getBreastCrotchRace(),
									owner.getNippleCrotchCovering(),
									owner.isNippleCrotchBestial(),
									Util.capitaliseSentence(Util.intToString(owner.getBreastCrotchRows()*2))+" "
											+(owner.getBreastRawSizeValue()>0?(owner.getBreastCrotchSize().getCupSizeName() + "-cup "):"flat ")+(owner.getBreastCrotchShape()==BreastShape.UDDERS?"udders":"crotch-boobs")));
						}
					}

					if (displayImage) {
						boolean revealed = owner.isImageRevealed();
						tooltipSB.append("
"
								+ "
"
									+ ""
										+(revealed?"":"
Unlocked through sex!
")
								+ "
");
					}
				}

				Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

			} else {
				Main.mainController.setTooltipSize(360, 234);

				Main.mainController.setTooltipContent(UtilText.parse(
						"
" + Util.capitaliseSentence(attribute.getName()) + "
"

						+ "
"
						+ "Core
"
						+ (owner.getBaseAttributeValue(attribute) > 0 ? "" : "")
							+ Units.number(owner.getBaseAttributeValue(attribute), 1, 1)
						+ ""
						+ "
"
						+ "
"
						+ "Bonus
"
						+ ((owner.getBonusAttributeValue(attribute)) > 0 ? ""
								: ((owner.getBonusAttributeValue(attribute)) == 0 ? ""
										: ""))
						+ Units.number(owner.getBonusAttributeValue(attribute), 1, 1)
						+ ""
						+ "
"
						+ "
"
						+ "Total
" + Units.number(owner.getAttributeValue(attribute), 1, 1) + ""
						+ "
"

						+ "
" + attribute.getDescription(owner) + "
"));

			}

		} else if (extraAttributes) {

//			if(owner.isRaceConcealed()) {
//				Main.mainController.setTooltipSize(420, 64);
//
//				tooltipSB.setLength(0);
//				tooltipSB.append("
"
//						+ "Unknown Stats!"
//						+ "
");
//
//			} else {
				Main.mainController.setTooltipSize(400, 516+(Main.game.isEnchantmentCapacityEnabled()?46:32));

				int enchantmentPointsUsed = owner.getEnchantmentPointsUsedTotal();
				tooltipSB.setLength(0);
				tooltipSB.append(UtilText.parse(owner,
						"
"
								+ (owner.getName(true).length() == 0
									? "[npc.Race]"
									: (owner.isPlayer()
											?"[pc.Name]"
											:"[npc.Name]"))
							+ "
"

						+"
Level " + owner.getLevel() + " | "
							+ (owner instanceof Elemental
									?"Elementals share their summoner's level"
									:""+owner.getExperience() + " / "+ (10 * owner.getLevel()) + " xp")
						+ "
"

						+ (Main.game.isEnchantmentCapacityEnabled()
								?"
"
										+ "[style.colourEnchantment("+Util.capitaliseSentence(Attribute.ENCHANTMENT_LIMIT.getName())+")]
"
										+ (enchantmentPointsUsed>owner.getAttributeValue(Attribute.ENCHANTMENT_LIMIT)
												?"[style.colourBad("
												:(enchantmentPointsUsed==owner.getAttributeValue(Attribute.ENCHANTMENT_LIMIT)
														?"[style.colourGood("
														:"[style.colourMinorGood("))
										+ enchantmentPointsUsed + ")]" + "/" + Math.round(owner.getAttributeValue(Attribute.ENCHANTMENT_LIMIT))
									+ "
"
									+"
"
								:"
")
							+ "[style.colourArcane(Essences)]"+(Main.game.isEnchantmentCapacityEnabled()?"
":": ")
							+ owner.getEssenceCount(TFEssence.ARCANE)
						+ "
"

						+ extraAttributeBonus(owner, Attribute.MAJOR_PHYSIQUE)
						+ extraAttributeBonus(owner, Attribute.MAJOR_ARCANE)
						+ extraAttributeBonus(owner, Attribute.MAJOR_CORRUPTION)
						+ extraAttributeBonus(owner, Attribute.CRITICAL_DAMAGE)

						+ extraAttributeBonus(owner, Attribute.SPELL_COST_MODIFIER)
						+ extraAttributeBonus(owner, Attribute.DAMAGE_SPELLS)

						+ extraAttributeBonus(owner, Attribute.DAMAGE_UNARMED)
						+ extraAttributeBonus(owner, Attribute.DAMAGE_MELEE_WEAPON)
						+ extraAttributeBonus(owner, Attribute.DAMAGE_RANGED_WEAPON)

						+ extraAttributeBonus(owner, Attribute.ENERGY_SHIELDING)

						// Header:
						+ "
"
							+ "Type"
						+ "
"
							+ "
"
						+ "Damage"
							+ "
"
						+ "
"
							+ "Shielding"
						+ "
"

						// Values:
						+ extraAttributeTableRow(owner, "Physical", Attribute.DAMAGE_PHYSICAL, Attribute.RESISTANCE_PHYSICAL)
						+ extraAttributeTableRow(owner, "Fire", Attribute.DAMAGE_FIRE, Attribute.RESISTANCE_FIRE)
						+ extraAttributeTableRow(owner, "Cold", Attribute.DAMAGE_ICE, Attribute.RESISTANCE_ICE)
						+ extraAttributeTableRow(owner, "Poison", Attribute.DAMAGE_POISON, Attribute.RESISTANCE_POISON)
						+ extraAttributeTableRow(owner, "Seduction", Attribute.DAMAGE_LUST, Attribute.RESISTANCE_LUST)

						+ extraAttributeBonus(owner, Attribute.FERTILITY)
						+ extraAttributeBonus(owner, Attribute.VIRILITY)));
//			}

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (weather) {

			Main.mainController.setTooltipSize(360, 100);

			tooltipSB.setLength(0);
			int minutes = Math.max(1, Main.game.getWeatherTimeRemainingInSeconds()/60);
			int hours = minutes/60;
			tooltipSB.append(
				"
"
					+ "" + Util.capitaliseSentence(Main.game.getCurrentWeather().getName()) + ""
				+ "
"
				+ "
"
					+ (hours>0
							?hours+" hour"+(hours>1?"s ":" ")
							:"")
					+ (minutes%60>0
							?minutes+" minute"+(minutes>1?"s ":" ")
							:"")
					+"remaining"
				+ "
");

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (protection) {

			Main.mainController.setTooltipSize(360, 100);

			tooltipSB.setLength(0);
			tooltipSB.append("
Protection
"
					+ "
"
					+ (owner.isWearingCondom()?"Wearing Condom":"No Condom")
					+"
");

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if (copyInformation) {

			Main.mainController.setTooltipSize(360, 170);

			tooltipSB.setLength(0);
			tooltipSB.append(
					"
"
					+(Main.game.getCurrentDialogueNode().getLabel() == "" || Main.game.getCurrentDialogueNode().getLabel() == null ? "-" : Main.game.getCurrentDialogueNode().getLabel())
					+ "
"
					+ "
"
					+ "Click to copy the currently displayed dialogue to your clipboard.

"
					+ "This scene was written by "
					+ Main.game.getCurrentDialogueNode().getAuthor()
					+ "
");

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if(concealedSlot!=null) {

			Map<InventorySlot, List<AbstractClothing>> concealedSlots = RenderingEngine.getCharacterToRender().getInventorySlotsConcealed();

			List<AbstractClothing> clothingVisible = concealedSlots.get(concealedSlot).stream().filter(clothing -> !concealedSlots.containsKey(clothing.getSlotEquippedTo())).collect(Collectors.toList());

			Main.mainController.setTooltipSize(360, 175);

			Main.mainController.setTooltipContent(UtilText.parse(
					"
"+Util.capitaliseSentence(concealedSlot.getName())+" - [style.boldBad(Concealed!)]
"
					+ "
"
						+ UtilText.parse(RenderingEngine.getCharacterToRender(),
							(concealedSlots.get(concealedSlot).isEmpty()
								?"Due to [npc.namePos] position, this slot is currently hidden from view!"
								:(clothingVisible.isEmpty()
										?"This slot is currently hidden from view by items of [npc.namePos] clothing that you cannot see!"
										:"This slot is currently hidden from view by [npc.namePos] "+Util.clothesToStringList(clothingVisible, false)+".")))
					+ "
"));

		} else if(loadedEnchantment!=null) {
			//TODO

			int yIncrease = 0;

			// Title:
			tooltipSB.setLength(0);
			tooltipSB.append("
" + Util.capitaliseSentence(loadedEnchantment.getName()) + "
");

			if(loadedEnchantment.isSuitableItemAvailable()) {
				tooltipSB.append("
Suitable item in inventory
");
			} else {
				tooltipSB.append("
No suitable item in inventory
");
			}

			// Attribute modifiers:
			tooltipSB.append("
");
			int i=0;
			for (ItemEffect ie : loadedEnchantment.getEffects()) {
				for(String s : ie.getEffectsDescription(Main.game.getPlayer(), Main.game.getPlayer())) {
					tooltipSB.append((i!=0?"
":"") + s);
					yIncrease++;
					if(UtilText.parse(s).replaceAll("<.*?>", "").length()>32) { // Yes, this is terrible...
						yIncrease++;
					}
				}
				i++;
			}
			if(yIncrease>=5) {
				yIncrease-=5;
			} else {
				yIncrease=0;
			}
			tooltipSB.append("
");

			// Picture:
			tooltipSB.append("
" + loadedEnchantment.getSVGString() + "
");

			Main.mainController.setTooltipSize(360, 208 + (yIncrease>0?4:0) + (yIncrease * LINE_HEIGHT));

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else if(cell!=null) {

			Set<NPC> charactersPresent = new HashSet<>(Main.game.getCharactersPresent(cell));
			if(!cell.equals(Main.game.getWorlds().get(WorldType.DOMINION).getCell(0, 0))) { // Override as NPCs had their home placed here... Add a version catch?
				charactersPresent.addAll(Main.game.getCharactersTreatingCellAsHome(cell));
			}

			boolean teleport = Main.game.getPlayer().hasSpell(Spell.TELEPORT);

			int yIncrease = 0;
			StringBuilder charactersPresentDescription = new StringBuilder();
			StringBuilder teleportingDescription = new StringBuilder();
			if(Main.game.getCurrentDialogueNode() != Library.DOMINION_MAP) {
				if(!charactersPresent.isEmpty()) {
					for(NPC character : charactersPresent) {
						yIncrease++;
						charactersPresentDescription.append(
								(Main.game.getCharactersPresent(cell).contains(character)
										?character.getName("The")
										:"[style.colourDisabled("+character.getName("The")+")]")
								+": "+(character.isRaceConcealed()?"[style.colourDisabled(Unknown race!)]":UtilText.parse(character, "[npc.FullRace(true)]"))
								+"
");
					}
				}
				if(teleport) {
					if(cell.getType().getTeleportPermissions().isIncoming() && cell.getPlace().getPlaceType().getTeleportPermissions().isIncoming()) {
						teleportingDescription.append("It [style.colourGood(is possible)] to [style.colourArcane(teleport)] into this tile!");
					} else {
						teleportingDescription.append("It [style.colourBad(is not possible)] to [style.colourArcane(teleport)] into this tile!");
					}
					if(cell.getType().getTeleportPermissions().isOutgoing() && cell.getPlace().getPlaceType().getTeleportPermissions().isOutgoing()) {
						teleportingDescription.append("
It [style.colourGood(is possible)] to [style.colourArcane(teleport)] out of this tile!");
					} else {
						teleportingDescription.append("
It [style.colourBad(is not possible)] to [style.colourArcane(teleport)] out of this tile!");
					}
				}
			}

			Main.mainController.setTooltipSize(360, 175+(yIncrease>0?32:0)+(teleport?8+48:0)+(yIncrease * LINE_HEIGHT));

			String tooltipDesc = cell.getPlace().getPlaceType().getTooltipDescription();

			Main.mainController.setTooltipContent(UtilText.parse(
					"
"+Util.capitaliseSentence(cell.getPlaceName())+"
"
					+ "
"
						+ (tooltipDesc==null || tooltipDesc.isEmpty()
							?""
							:tooltipDesc+"
")
						+(cell.getPlace().getPlaceType().isDangerous()
							?"This is a [style.italicsBad(dangerous)] area!"
							:"This is a [style.italicsGood(safe)] area.")
					+ "
"
					+ (yIncrease>0
							?"
"+charactersPresentDescription.toString()+"
"
							:"")
					+ (teleport
							?"
"+teleportingDescription.toString()+"
"
							:"")));

		} else if(moneyTransferPercentage>0) {

			if(InventoryDialogue.getNPCInventoryInteraction()==InventoryInteraction.FULL_MANAGEMENT
					&& owner!=null?owner.getMoney()>0:Main.game.getPlayerCell().getInventory().getMoney()>0) {
				Main.mainController.setTooltipSize(360, 112);
			} else {
				Main.mainController.setTooltipSize(360, 96);
			}
			tooltipSB.setLength(0);

			String percentageTransfer;
			int transferAmount;

			if(this.moneyTransferPercentage==1) {
				tooltipSB.append("
[style.colourMinorGood(Small Flames Transfer)]
");
				percentageTransfer = "[style.colourMinorGood("+moneyTransferPercentage+"%)]";
			} else if(this.moneyTransferPercentage==10) {
				tooltipSB.append("
[style.colourGood(Flames Transfer)]
");
				percentageTransfer = "[style.colourGood("+moneyTransferPercentage+"%)]";
			} else {
				tooltipSB.append("
[style.colourExcellent(Total Flames Transfer)]
");
				percentageTransfer = "[style.colourExcellent("+moneyTransferPercentage+"%)]";
			}

			if(InventoryDialogue.getNPCInventoryInteraction()!=InventoryInteraction.FULL_MANAGEMENT) {
				tooltipSB.append("
"
						+ "[style.italicsBad(Flame transfer not available in this interaction!)]"
						+ "
");

			} else if(owner==null) {
				transferAmount = (int) Math.max(1, Main.game.getPlayerCell().getInventory().getMoney()*(moneyTransferPercentage/100f));
				tooltipSB.append("
"
						+ (Main.game.getPlayerCell().getInventory().getMoney()==0
								?"[style.italicsBad(There are no flames in this area...)]"
								:UtilText.parse(moneyTransferTarget,
									"Pick up "+percentageTransfer+" of the flames in this area:
 ")
									+ UtilText.formatAsMoney(transferAmount, "i"))
						+"
");

			} else if(owner.isPlayer()) {
				transferAmount = (int) Math.max(1, owner.getMoney()*(moneyTransferPercentage/100f));
				tooltipSB.append("
"
						+ (owner.getMoney()==0
								?"[style.italicsBad(You do not have any flames, so cannot transfer any money...)]"
								:((moneyTransferTarget==null
									?(Main.game.getPlayerCell().getPlace().isItemsDisappear()
											?"[style.colourBad(Abandon)] "+percentageTransfer+" of your flames in this area:
 "
											:"[style.colourGood(Safely store)] "+percentageTransfer+" of your flames in this area:
 ")
									:UtilText.parse(moneyTransferTarget,
											"Transfer "+percentageTransfer+" of your flames to [npc.name]:
 "))
									+UtilText.formatAsMoney(transferAmount, "i")))
						+"
");

			} else {
				transferAmount = (int) Math.max(1, owner.getMoney()*(moneyTransferPercentage/100f));
				tooltipSB.append("
"
						+ UtilText.parse(owner,
								(owner.getMoney()==0
									?"[style.italicsBad([npc.Name] does not have any flames...)]"
									:"Take "+percentageTransfer+" of [npc.namePos] flames:
 "
										+ UtilText.formatAsMoney(transferAmount, "i")))
						+"
");
			}

			Main.mainController.setTooltipContent(UtilText.parse(tooltipSB.toString()));

		} else { // Standard information:
			if(description==null || description.isEmpty()) {
				Main.mainController.setTooltipSize(360, 64);

				Main.mainController.setTooltipContent(UtilText.parse(
						"
"+title+"
"));

			} else if(title==null || title.isEmpty()) {
				Main.mainController.setTooltipSize(360, descriptionHeightOverride>0?descriptionHeightOverride+64+32:200);

				Main.mainController.setTooltipContent(UtilText.parse(
						"
0?(descriptionHeightOverride+26):"176")+"px;'>"+description+"
"));

			} else {
				Main.mainController.setTooltipSize(360, descriptionHeightOverride>0?descriptionHeightOverride+64+32:175);

				Main.mainController.setTooltipContent(UtilText.parse(
						"
"+title+"
"
						+ "
0?"style='height:"+(descriptionHeightOverride+26)+"px;'":"")+">" + description + "
"));
			}
		}

		TooltipUpdateThread.updateToolTip(-1,-1);
	}

	private String getBodyPartDiv(GameCharacter character, String name, Race race, BodyCoveringType covering, boolean bestial) {
		return getBodyPartDiv(character, name, race, covering, bestial, null);
	}

	private String getBodyPartDiv(GameCharacter character, String name, Race race, BodyCoveringType covering, boolean bestial, String size) {
		String raceName;
		raceName = race.getName(character, bestial);

		if(raceName.equals("wolf-morph") && Main.getProperties().hasValue(PropertyValue.sillyMode)){
			raceName = "awoo-morph";
		}
		if(raceName.equals("cat-morph") && Main.getProperties().hasValue(PropertyValue.sillyMode)){
			raceName = "catte-morph";
		}
		if(raceName.equals("harpy") && Main.getProperties().hasValue(PropertyValue.sillyMode)){
			raceName = "birb";
		}

		return "
"
					+ name +(size!=null?" ("+size+"): ":": ")
					+ (bestial&&race!=Race.NONE?"[style.colourBestial(Feral )]":"")
					+(covering!=BodyCoveringType.DILDO
						?""
							+Util.capitaliseSentence(raceName)
						:""
								+"Dildo")
					+ " - "
					+ owner.getCovering(covering).getColourDescriptor(owner, true, true) + " " + owner.getCovering(covering).getName(owner)
				+"
";
	}

	private String getBodyPartDivFace(GameCharacter character, String name, Race race, BodyCoveringType covering, boolean bestial) {
		String raceName;
		raceName = race.getName(character, bestial);

		if(raceName.equals("wolf-morph") && Main.getProperties().hasValue(PropertyValue.sillyMode)){
			raceName = "awoo-morph";
		}
		if(raceName.equals("cat-morph") && Main.getProperties().hasValue(PropertyValue.sillyMode)){
			raceName = "catte-morph";
		}
		if(raceName.equals("harpy") && Main.getProperties().hasValue(PropertyValue.sillyMode)){
			raceName = "birb";
		}

		Covering coveringHandledForFreckles = character.getCovering(covering);

		if(character.getCovering(covering).getPattern()==CoveringPattern.FRECKLED_FACE) {
			coveringHandledForFreckles = new Covering(covering,
					CoveringPattern.FRECKLED,
					character.getCovering(covering).getModifier(),
					character.getCovering(covering).getPrimaryColour(),
					character.getCovering(covering).isPrimaryGlowing(),
					character.getCovering(covering).getSecondaryColour(),
					character.getCovering(covering).isSecondaryGlowing());
		}

		return "
"
					+ name+": "
					+ (bestial&&race!=Race.NONE?"[style.colourBestial(Feral )]":"")
					+(covering!=BodyCoveringType.DILDO
						?""
							+Util.capitaliseSentence(raceName)
						:""
								+"Dildo")
					+ " - "
					+ coveringHandledForFreckles.getColourDescriptor(owner, true, true) + " " + coveringHandledForFreckles.getName(owner)
				+"
";

	}

	private String getEmptyBodyPartDiv(String name, String description) {
		return getEmptyBodyPartDiv(name, description, null);
	}

	private String getEmptyBodyPartDiv(String name, String description, String size) {
		return "
"
					+ name +(size!=null?" ("+size+"): ":": ")+ ""+description+""
			+ "
";
	}

	private String extraAttributeTableRow(GameCharacter owner, String type, Attribute damage, Attribute resist) {
		return "
"
				+ "" + type + ""
				+ "
"
				+ "
"
					+ (owner.getAttributeValue(damage) > damage.getBaseValue()
										? ""
										: (owner.getAttributeValue(damage) < damage.getBaseValue()
												? ""
												: ""))
						+ owner.getAttributeValue(damage)
					+ ""
				+ "
"
				+ "
"
					+ (resist == null
							? "0.0"
							: (owner.getAttributeValue(resist) > 0
									? ""
									: (owner.getAttributeValue(resist) < 0
											? ""
											: ""))
						+ owner.getAttributeValue(resist)
					+ "")
				+ "
";
	}

	private String extraAttributeBonus(GameCharacter owner, Attribute bonus) {
		return "
"
				+ ""
					+ Util.capitaliseSentence(bonus.getName())
				+ "
" + (owner.getAttributeValue(bonus) > bonus.getBaseValue()
						? "" : (owner.getAttributeValue(bonus) < bonus.getBaseValue() ? "" : ""))
				+ owner.getAttributeValue(bonus) + "" + "
";
	}

	public TooltipInformationEventListener setInformation(String title, String description) {
		resetFields();
		this.title = title;
		this.description = description;

		return this;
	}

	public TooltipInformationEventListener setInformation(String title, String description, int descriptionHeightOverride) {
		setInformation(title, description);
		this.descriptionHeightOverride = descriptionHeightOverride;

		return this;
	}

	public TooltipInformationEventListener setWeather() {
		resetFields();
		weather = true;

		return this;
	}

	public TooltipInformationEventListener setExtraAttributes(GameCharacter owner) {
		resetFields();
		extraAttributes = true;
		this.owner = owner;

		return this;
	}
	public TooltipInformationEventListener setStatusEffect(StatusEffect statusEffect, GameCharacter owner) {
		resetFields();
		this.statusEffect = statusEffect;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setPerk(AbstractPerk perk, GameCharacter owner) {
		resetFields();
		this.perk = perk;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setFetish(Fetish fetish, GameCharacter owner) {
		resetFields();
		this.fetish = fetish;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setFetishExperience(Fetish fetish, GameCharacter owner) {
		resetFields();
		fetishExperience = true;
		this.fetish = fetish;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setFetishDesire(Fetish fetish, FetishDesire desire, GameCharacter owner) {
		resetFields();
		this.desire = desire;
		this.fetish = fetish;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setLevelUpPerk(int perkRow, AbstractPerk levelUpPerk, GameCharacter owner, boolean availableForSelection) {
		resetFields();
		this.levelUpPerk = levelUpPerk;
		this.perkRow = perkRow;
		this.owner = owner;
		this.availableForSelection = availableForSelection;

		return this;
	}

	public TooltipInformationEventListener setSpell(Spell spell, GameCharacter owner) {
		resetFields();
		this.spell = spell;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setSpellUpgrade(SpellUpgrade spellUpgrade, GameCharacter owner) {
		resetFields();
		this.spellUpgrade = spellUpgrade;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setAttribute(Attribute attribute, GameCharacter owner) {
		resetFields();
		this.attribute = attribute;
		this.owner = owner;

		return this;
	}

	public TooltipInformationEventListener setProtection(GameCharacter owner) {
		resetFields();
		this.owner = owner;
		protection=true;

		return this;
	}

	public TooltipInformationEventListener setCopyInformation() {
		resetFields();
		copyInformation = true;

		return this;
	}

	public TooltipInformationEventListener setConcealedSlot(InventorySlot concealedSlot) {
		resetFields();
		this.concealedSlot = concealedSlot;

		return this;
	}

	public TooltipInformationEventListener setLoadedEnchantment(LoadedEnchantment loadedEnchantment) {
		resetFields();
		this.loadedEnchantment = loadedEnchantment;

		return this;
	}

	public TooltipInformationEventListener setCombatMove(CombatMove move, GameCharacter owner) {
		resetFields();
		this.owner = owner;
		this.move = move;

		return this;
	}

	public TooltipInformationEventListener setCell(Cell cell) {
		resetFields();
		this.cell = cell;

		return this;
	}

	public TooltipInformationEventListener setMoneyTransferTarget(GameCharacter from, GameCharacter to, int moneyTransferPercentage) {
		resetFields();
		this.owner = from;
		this.moneyTransferTarget = to;
		this.moneyTransferPercentage = moneyTransferPercentage;
		return this;
	}

	private void resetFields() {
		extraAttributes = false;
		weather = false;
		owner = null;
		statusEffect = null;
		perk = null;
		fetish = null;
		fetishExperience = false;
		desire = null;
		levelUpPerk = null;
		availableForSelection = false;
		perkRow = 0;
		spell = null;
		spellUpgrade = null;
		attribute = null;
		protection=false;
		copyInformation=false;
		concealedSlot=null;
		loadedEnchantment=null;
		move=null;
		descriptionHeightOverride = 0;
		cell = null;
		moneyTransferTarget = null;
		moneyTransferPercentage = 0;
	}
}