code:
package com.lilithsthrone.game.dialogue.utils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.effects.PerkManager;
import com.lilithsthrone.game.character.npc.NPC;
import com.lilithsthrone.game.character.npc.misc.Elemental;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.DialogueNodeType;
import com.lilithsthrone.game.dialogue.companions.CompanionManagement;
import com.lilithsthrone.game.dialogue.companions.OccupantDialogue;
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.sex.Sex;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.3
 * @version 0.3.5.1
 * @author Innoxia
 */
public class CharactersPresentDialogue {

	public static String menuContent;
	public static String menuTitle;
	public static NPC characterViewed = null;

	private static NPC targetedCharacterForSex;
	private static NPC companionCharacter;

	public static void resetContent(GameCharacter characterViewed) {
		if(characterViewed==null) {
			CharactersPresentDialogue.characterViewed = Main.game.getCharactersPresent().get(0);
		} else {
			CharactersPresentDialogue.characterViewed = (NPC) characterViewed;
		}
		menuTitle = "Characters Present ("+Util.capitaliseSentence(CharactersPresentDialogue.characterViewed.getName(true))+")";
		menuContent = ((NPC) CharactersPresentDialogue.characterViewed).getCharacterInformationScreen(true);

		if(Main.game.getPlayer().hasCompanion(CharactersPresentDialogue.characterViewed)) {
			if(CharactersPresentDialogue.characterViewed.isSlave() && CharactersPresentDialogue.characterViewed.getOwner().isPlayer()) {
				SlaveDialogue.initDialogue((NPC) CharactersPresentDialogue.characterViewed, true);
			} else {
				OccupantDialogue.initDialogue((NPC) CharactersPresentDialogue.characterViewed, false, true);
			}
			CompanionManagement.initManagement(MENU, 2, CharactersPresentDialogue.characterViewed);
		}

//		Main.game.setActiveNPC(characterViewed);
		targetedCharacterForSex = (NPC) CharactersPresentDialogue.characterViewed;

		if(Main.game.getPlayer().getCompanions().size()>1) {
			companionCharacter = (NPC) Main.game.getPlayer().getMainCompanion();
			if(Objects.equals(getCompanionCharacter(), targetedCharacterForSex)) {
				companionCharacter = (NPC) Main.game.getPlayer().getCompanions().stream().filter((npc) -> !npc.equals(getCharacterViewed())).findFirst().get();
			}
		} else {
			companionCharacter = null;
		}
	}

	private static NPC getCharacterViewed() {
		return characterViewed;
	}

	private static NPC getTargetedCharacterForSex() {
		if(!Main.game.getCharactersPresent().contains(targetedCharacterForSex)) {
			targetedCharacterForSex = (NPC) CharactersPresentDialogue.characterViewed;
			if(Objects.equals(getCompanionCharacter(), targetedCharacterForSex)) {
				if(Main.game.getPlayer().getCompanions().size()>1) {
					companionCharacter = (NPC) Main.game.getPlayer().getCompanions().stream().filter((npc) -> !npc.equals(getCharacterViewed())).findFirst().get();
				}
			}
		}
		return targetedCharacterForSex;
	}

	private static NPC getCompanionCharacter() {
		if(!Main.game.getCharactersPresent().contains(companionCharacter)) {
			if(Main.game.getPlayer().getCompanions().size()>1) {
				companionCharacter = (NPC) Main.game.getPlayer().getMainCompanion();
				if(Objects.equals(companionCharacter, targetedCharacterForSex)) {
					companionCharacter = (NPC) Main.game.getPlayer().getCompanions().stream().filter((npc) -> !npc.equals(getCharacterViewed())).findFirst().get();
				}
			} else {
				companionCharacter = null;
			}
		}
		return companionCharacter;
	}

	private static String getTextFilePath() {
		if(targetedCharacterForSex.isRelatedTo(Main.game.getPlayer())) {
			return "characters/offspring/slave";
		} else {
			return "misc/slaveDialogue";
		}
	}

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

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

		@Override
		public String getLabel() {
			return menuTitle;
		}

		@Override
		public String getContent() {
			return menuContent;
		}

		@Override
		public String getResponseTabTitle(int index) {
			if(Main.game.getPlayer().hasCompanion(characterViewed)) {
				if(index == 0) {
					return "Characters";
				} else if(index == 1) {
					return UtilText.parse("[style.colourSex(Sex)]");
				} else if(index == 2) {
					return UtilText.parse("[style.colourCompanion(Manage)]");
				}

			} else {
				if(index == 0) {
					return "Characters";
				} else if(index == 1) {
					return "Sex";
				} else if(index == 2) {
					return "Manage";
				}
			}
			return null;
		}

		@Override
		public Response getResponse(int responseTab, int index) {

			List<NPC> charactersPresent = Main.game.getCharactersPresent();
			Collections.sort(charactersPresent, (c1, c2) -> Main.game.getPlayer().hasCompanion(c1)?1:0);

			if(responseTab==0) {
				if (index == 0) {
					return new ResponseEffectsOnly("Back", "Stop viewing the characters present and return to the main game."){
						@Override
						public void effects() {
							Main.game.getDialogueFlags().setManagementCompanion(null);
							Main.mainController.openCharactersPresent();
						}
					};

				} else if (index <= charactersPresent.size()) {
					String title = "[npc.Name]";
					String description = "Take a detailed look at [npc.name].";

					if(charactersPresent.get(index - 1).equals(characterViewed)) {
						if(!charactersPresent.get(index - 1).isRaceConcealed() || charactersPresent.get(index - 1).isPlayerKnowsName()) {
							title = "[style.colourDisabled([npc.Name])]";
							description = "You are already looking at [npc.name]!";
						}else {
							title = "[style.colourDisabled(Unknown person)]";
							description = "You don't know what this person looks like!";
						}

					} else if(Main.game.getPlayer().hasCompanion(charactersPresent.get(index - 1))) {
						title = "[style.colourCompanion([npc.Name])]";
						description = "Take a detailed look at your [style.colourCompanion(companion)], [npc.name].";
					}

					return new Response(
							UtilText.parse(charactersPresent.get(index - 1), title),
							UtilText.parse(charactersPresent.get(index - 1), description),
							charactersPresent.get(index - 1).equals(characterViewed)?null:MENU) {
						@Override
						public void effects() {
							characterViewed = charactersPresent.get(index-1);
							menuTitle = "Characters Present ("+Util.capitaliseSentence(charactersPresent.get(index - 1).getName(true))+")";
							menuContent = ((NPC) charactersPresent.get(index - 1)).getCharacterInformationScreen(true);
						}
					};

				} else {
					return null;
				}

			} else if (responseTab==1 && Main.game.getPlayer().hasCompanion(characterViewed)) {
				if (index == 0) {
					return new ResponseEffectsOnly("Back", "Stop viewing the characters present and return to the main game."){
						@Override
						public void effects() {
							Main.game.getDialogueFlags().setManagementCompanion(null);
							Main.mainController.openCharactersPresent();
						}
					};
				}
				if(index>0 && index<5 && !characterViewed.isCompanionAvailableForSex(false)) {
					if(index==1) {
						return new Response("Sex", characterViewed.getCompanionSexRejectionReason(false), null);
					}
					return null;
				}
				if(index>5 && index<10 && !characterViewed.isCompanionAvailableForSex(true)) {
					if(index==6) {
						return new Response("Submissive Sex", characterViewed.getCompanionSexRejectionReason(true), null);
					}
					return null;
				}
				if(characterViewed.isSlave() && characterViewed.getOwner().isPlayer()) {
					return SlaveDialogue.SLAVE_START.getResponse(responseTab, index);
				} else {
					return OccupantDialogue.OCCUPANT_START.getResponse(responseTab, index);
				}

			} else if(responseTab==2 && Main.game.getPlayer().hasCompanion(characterViewed)) {
				return CompanionManagement.getManagementResponses(index);
			}

			return null;
		}
	};

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

		@Override
		public String getDescription(){
			return "Now that you've had your fun, you can step back and leave [npc.name] to recover.";
		}

		@Override
		public String getContent() {
			if(Sex.getAllParticipants().size()>2) {
				List<GameCharacter> parsingCharacters = new ArrayList<>(Sex.getAllParticipants());
				parsingCharacters.remove(Main.game.getPlayer());
				return UtilText.parseFromXMLFile(getTextFilePath(), "AFTER_SEX_THREESOME", parsingCharacters);

			} else if(Sex.getNumberOfOrgasms(getCharacterViewed()) >= getCharacterViewed().getOrgasmsBeforeSatisfied()) {
				return UtilText.parseFromXMLFile(getTextFilePath(), "AFTER_SEX", getTargetedCharacterForSex());

			} else {
				return UtilText.parseFromXMLFile(getTextFilePath(), "AFTER_SEX_NO_ORGASM", getTargetedCharacterForSex());
			}
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if (index == 1) {
				return new Response("Continue", "Decide what to do next.", AFTER_SEX) {
					@Override
					public DialogueNode getNextDialogue() {
						return Main.game.getDefaultDialogueNoEncounter();
					}
					@Override
					public void effects() {
						Main.game.setActiveNPC(null);
					}
				};

			} else {
				return null;
			}
		}
	};

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

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

		@Override
		public String getLabel() {
			return UtilText.parse(getCharacterViewed(), "[npc.NamePos] Perk Tree");
		}

		@Override
		public String getContent() {
			UtilText.nodeContentSB.setLength(0);

			UtilText.nodeContentSB.append(UtilText.parse(characterViewed,
					""
							+ "[style.boldPerk(Perk & Trait Information)]"
							+ "[style.colourPerk(Perks)] (circular icons) apply permanent boosts to [npc.namePos] attributes.
"
							+ "[style.colourPerk(Traits)] (square icons) provide unique effects for [npc.name]."
								+ " Unlike perks, traits will have no effect on [npc.name] until they're slotted into [npc.her] 'Active Traits' bar.
"
							+ "Perks require perk points to unlock. [npc.Name] earns one perk point each time [npc.she] levels up, and earns an extra two perk points every five levels.

"
							+ "In addition to the perks that can be purchased via perk points, there are also several special, hidden perks that are unlocked via special events."
					+ ""));

			UtilText.nodeContentSB.append(PerkManager.MANAGER.getPerkTreeDisplay(characterViewed, true));

			UtilText.nodeContentSB.append("
");

			if(!(characterViewed instanceof Elemental)) {
				UtilText.nodeContentSB.append("
"
							+ "Please note that this perk tree is a work-in-progress. This is not the final version, and is just a proof of concept!"
						+ "
");
			}

			return UtilText.nodeContentSB.toString();
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if (index == 6) {
				return new Response("Perk Tree", UtilText.parse(characterViewed, "You are already assigning [npc.namePos] perk points."), null);

			} else if(index==7) {
				return new Response("Reset perks", "Reset all of [npc.namePos] perks and traits, refunding all points spent. (This is a temporary action while the perk tree is still under development.)", PERKS) {
					@Override
					public void effects() {
						characterViewed.resetPerksMap(false, false);
					}
				};
			}

			return MENU.getResponse(responseTab, index);
		}
	};
}


code:
package com.lilithsthrone.game.dialogue.utils;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.combat.CombatMove;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.DialogueNodeType;
import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.main.Main;

/**
 * @since 0.3.4
 * @version 0.3.4
 * @author Irbynx
 */
public class CombatMovesSetup {

    private static GameCharacter target;
    private static DialogueNode dialogueReturn;

    public static GameCharacter getTarget() {
        if(target==null) {
            return Main.game.getPlayer();
        }
        return target;
    }

    public static void setTarget(GameCharacter target, DialogueNode dialogueReturn) {
        CombatMovesSetup.target = target;
        CombatMovesSetup.dialogueReturn = dialogueReturn;
    }

    public static final DialogueNode COMBAT_MOVES_CORE = new DialogueNode("Combat Moves", "", true) {
        @Override
        public String getHeaderContent() {
            UtilText.nodeContentSB.setLength(0);

            UtilText.nodeContentSB.append(
                    "
"
                            + "While there is no limit on the number of moves which a character can know and use in combat,"
                            	+ " it's only possible for "+String.valueOf(GameCharacter.MAX_COMBAT_MOVES)+" [style.colourMinorGood(core combat moves)] to be selected at a time."
                            + " Any [style.colourMinorBad(non-core moves)] cost [style.colourBad(+1 action point)] to use in combat, and will suffer a [style.colourBad(+1 turn cooldown)].
"
                            + UtilText.parse(getTarget(),
                            		"(You can click on the icons below to add and remove them from [npc.namePos] core combat moves.)"
                            		+(getTarget().isPlayer()
                            				?""
                            				:"([npc.Name] will only choose to use [npc.her] core moves when in combat, unless [npc.she] has none available, in which case [npc.she] will choose from [npc.her] non-core moves.)"))
                            + "
"
                            + "
"
                            + "
[style.colourMinorGood(Core Combat Moves)]
");

            for(int i=0;i<GameCharacter.MAX_COMBAT_MOVES;i++) {
                CombatMove mv = null;
                if(i<target.getEquippedMoves().size()) {
                    mv = target.getEquippedMoves().get(i);
                }
                if(mv!=null) {
                    UtilText.nodeContentSB.append("
"
                            + "
"+mv.getSVGString()+"
"
                            + "
");

                } else {
                    UtilText.nodeContentSB.append("

");

                }
            }
            UtilText.nodeContentSB.append("
");

            UtilText.nodeContentSB.append(
                            "
"
                            + "
[style.colourMinorBad(Non-Core Combat Moves)]
");

            for(int i=0;i<target.getAvailableMoves().size();i++) {
                CombatMove mv = target.getAvailableMoves().get(i);
                if(!target.getEquippedMoves().contains(target.getAvailableMoves().get(i))) {
                    UtilText.nodeContentSB.append("
"
                            + "
" + mv.getSVGString() + "
"
                            + "
");
                }
            }
            UtilText.nodeContentSB.append("
");

            return UtilText.nodeContentSB.toString();
        }

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

		@Override
		public String getResponseTabTitle(int index) {
			return dialogueReturn.getResponseTabTitle(index);
		}

        @Override
        public Response getResponse(int responseTab, int index) {
        	return dialogueReturn.getResponse(responseTab, index);
//            if (index == 0) {
//                return new Response("Back", "Return to the previous menu.", dialogueReturn);
//            } else {
//                return null;
//            }
        }

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

repo_name: klorpa/Pedo

license: unknown


code:
package com.lilithsthrone.game.dialogue.utils;

import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

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

import com.lilithsthrone.game.PropertyValue;
import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.character.attributes.Attribute;
import com.lilithsthrone.game.character.markings.Tattoo;
import com.lilithsthrone.game.character.markings.TattooType;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.DialogueNodeType;
import com.lilithsthrone.game.dialogue.companions.CompanionManagement;
import com.lilithsthrone.game.dialogue.eventLog.EventLogEntry;
import com.lilithsthrone.game.dialogue.places.dominion.shoppingArcade.SuccubisSecrets;
import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.game.dialogue.responses.ResponseEffectsOnly;
import com.lilithsthrone.game.inventory.AbstractCoreItem;
import com.lilithsthrone.game.inventory.InventorySlot;
import com.lilithsthrone.game.inventory.clothing.AbstractClothing;
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.ItemEffectType;
import com.lilithsthrone.game.inventory.enchanting.LoadedEnchantment;
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.ItemType;
import com.lilithsthrone.game.inventory.weapon.AbstractWeapon;
import com.lilithsthrone.game.inventory.weapon.WeaponType;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.rendering.RenderingEngine;
import com.lilithsthrone.rendering.SVGImages;
import com.lilithsthrone.utils.Colour;
import com.lilithsthrone.utils.Util;

/**
 * @since 0.1.7
 * @version 0.3.5.1
 * @author Innoxia
 */
public class EnchantmentDialogue {

	private static StringBuilder inventorySB = new StringBuilder("");

	private static AbstractCoreItem ingredient = null;
	private static AbstractCoreItem previousIngredient = null;

	private static List<ItemEffect> effects = new ArrayList<>();
	private static List<ItemEffect> previousEffects = new ArrayList<>();

	private static InventorySlot tattooSlot;
	private static GameCharacter tattooBearer;

	private static TFModifier primaryMod = TFModifier.NONE;
	private static TFModifier secondaryMod = TFModifier.NONE;
	public static TFModifier previousPrimaryMod = TFModifier.NONE;
	public static TFModifier previousSecondaryMod = TFModifier.NONE;

	private static TFPotency potency = TFPotency.MINOR_BOOST;

	private static int limit = 0;

	private static Map<String, LoadedEnchantment> loadedEnchantmentsMap;

	private static String outputName = "";

	private static boolean isEquipped = false;
	private static GameCharacter isEquippedTo = null;
	private static InventorySlot isEquippedIn = null;

	private static String inventoryView() {
		inventorySB.setLength(0);

		ItemEffect effect = new ItemEffect(ingredient.getEnchantmentEffect(), primaryMod, secondaryMod, potency, limit);

		// Primary mods:
		inventorySB.append("
");
		for (TFModifier tfMod : ingredient.getEnchantmentEffect().getPrimaryModifiers()) {
			inventorySB.append("
"
					+ "
"+tfMod.getSVGString()+"
"
					+ "

"
					+ "
");
		}
		for (int i = 32; i > ingredient.getEnchantmentEffect().getPrimaryModifiers().size(); i--) {
			inventorySB.append("

");
		}

		inventorySB.append("
"
				+ "
"
				+ "
Primary Modifier
"
				+ "
"
				+ "
");
		if(primaryMod != null) {
			inventorySB.append("
"
					+ "
"+primaryMod.getSVGString()+"
"
					+ "

"
					+ "
");

		} else {
			inventorySB.append("
"
					+ "

"
					+ "
");
		}
		inventorySB.append("

");

		inventorySB.append("
");

		// Secondary mods:
		inventorySB.append("
");
		for (TFModifier tfMod : ingredient.getEnchantmentEffect().getSecondaryModifiers(primaryMod)) {
			inventorySB.append("
"
					+ "
"+tfMod.getSVGString()+"
"
					+ "

"
					+ "
");
		}
		for (int i = 32; i > ingredient.getEnchantmentEffect().getSecondaryModifiers(primaryMod).size(); i--) {
			inventorySB.append("

");
		}

		inventorySB.append("
"
				+ "
");
		if(secondaryMod != null) {
			inventorySB.append("
"
					+ "
"+secondaryMod.getSVGString()+"
"
					+ "

"
					+ "
");

		} else {
			inventorySB.append("
"
					+ "

"
					+ "
");
		}
		inventorySB.append("
"
				+ "
"
					+ "
Secondary Modifier
"
				+ "
"
				+ "
");

		inventorySB.append("
");

		// Potency:
		inventorySB.append("
");

		for(TFPotency potency : TFPotency.values()) {
			inventorySB.append("
"+potency.getName()+"
");
		}

		inventorySB.append("
");

		// Limits:
		int ingredientLimit = ingredient.getEnchantmentEffect().getLimits(primaryMod, secondaryMod);
		if(ingredientLimit!=0) {
			inventorySB.append(
					"
"
						+ "
"
							+ "
Limit Min.
"
						+ "
"
						+ "
"
							+ "
Limit--
"
						+ "
"
						+ "
"
							+ "
Limit-
"
						+ "
"
						+ "
"
							+ "
Limit+
"
						+ "
"
						+ "
"
							+ "
Limit++
"
						+ "
"
						+ "
"
							+ "
Limit Max.
"
						+ "
"
					+ "
");
		}

		// Effect:
		inventorySB.append("
");

			inventorySB.append("
"
									+ "Effect to be added:"
								+ "
");

			inventorySB.append("
");
				if(effect.getEffectsDescription(Main.game.getPlayer(), Main.game.getPlayer())!=null) {
					int i=0;
					for(String s : effect.getEffectsDescription(Main.game.getPlayer(), Main.game.getPlayer())) {
						if(i>0) {
							inventorySB.append("
");
						}
						inventorySB.append(""+Util.capitaliseSentence(s)+"");
						i++;
					}
				} else {
					inventorySB.append("-");
				}

				// Append enchantment capacity cost for weapons/clothing/tattoos
				if(Main.game.isEnchantmentCapacityEnabled()) {
					if((ingredient instanceof AbstractClothing)
							|| (ingredient instanceof AbstractWeapon)
							|| (ingredient instanceof Tattoo)) {
						if(effect.getItemEffectType()==ItemEffectType.CLOTHING
								|| effect.getItemEffectType()==ItemEffectType.WEAPON
								|| effect.getItemEffectType()==ItemEffectType.TATTOO) {
							if(effect.getPrimaryModifier()==TFModifier.CLOTHING_ATTRIBUTE
									|| effect.getPrimaryModifier()==TFModifier.CLOTHING_MAJOR_ATTRIBUTE) {
								int cost = Math.max(0, effect.getPotency().getClothingBonusValue());
								if(effect.getSecondaryModifier()==TFModifier.CORRUPTION
										|| effect.getSecondaryModifier()==TFModifier.FERTILITY
										|| effect.getSecondaryModifier()==TFModifier.VIRILITY) {
									cost = 0;
								}
								inventorySB.append("
"
										+ (cost>0
												?"[style.colourEnchantment("+Util.capitaliseSentence(Attribute.ENCHANTMENT_LIMIT.getName())+" cost)]: [style.boldBad("+cost+")]"
												:Util.capitaliseSentence(Attribute.ENCHANTMENT_LIMIT.getName())+" cost: [style.boldDisabled(0)]"));
							}
						}
					}
				}
			inventorySB.append("
");

			inventorySB.append("
");
				if(effects.size() >= ingredient.getEnchantmentLimit()
						|| ingredient.getEnchantmentEffect().getEffectsDescription(primaryMod, secondaryMod, potency, limit, Main.game.getPlayer(), Main.game.getPlayer())==null
						|| ingredient.getEnchantmentEffect().getEffectsDescription(primaryMod, secondaryMod, potency, limit, Main.game.getPlayer(), Main.game.getPlayer()).isEmpty()) {
					inventorySB.append(
							"
"
							+ "Add | "
							+ (ingredient instanceof Tattoo
									?UtilText.formatAsMoneyUncoloured(EnchantingUtils.getModifierEffectCost(true, ingredient, effect)*EnchantingUtils.FLAME_COST_MODIFER, "b")
									:UtilText.formatAsEssencesUncoloured(EnchantingUtils.getModifierEffectCost(true, ingredient, effect), "b", false))
							+ "

"
							+ "
");

				} else {
					inventorySB.append(
							"
"
							+ "Add | "
									+ (ingredient instanceof Tattoo
											?UtilText.formatAsMoney(EnchantingUtils.getModifierEffectCost(true, ingredient, effect)*EnchantingUtils.FLAME_COST_MODIFER, "b")
											:UtilText.formatAsEssences(EnchantingUtils.getModifierEffectCost(true, ingredient, effect), "b", false))
							+ "

"
							+ "
");

				}
			inventorySB.append("
");

		inventorySB.append("
");

		// Item crafting:
		inventorySB.append("
");

		int count = 1;
		if(ingredient instanceof AbstractItem) {
			count = Main.game.getPlayer().getItemCount((AbstractItem) ingredient);
		} else if(ingredient instanceof AbstractClothing) {
			count = Main.game.getPlayer().getClothingCount((AbstractClothing) ingredient);
		} else if(ingredient instanceof AbstractWeapon) {
			count = Main.game.getPlayer().getWeaponCount((AbstractWeapon) ingredient);
		}

			inventorySB.append("
");
				inventorySB.append("Input"
						+ "
"
						+ "
"+ingredient.getSVGString()+"
"
						+ "

"
						+ "
x" + count+ "
"
						+ "
");
			inventorySB.append("
");

			// Effects:
			inventorySB.append("
");
				inventorySB.append("Effects ("
									+ (effects.size()>=ingredient.getEnchantmentLimit()?"":"")+""
											+ effects.size()+"/"+ingredient.getEnchantmentLimit()+") | Cost: "
												+ (ingredient instanceof Tattoo
														?UtilText.formatAsMoney(EnchantingUtils.getCost(ingredient, effects)*EnchantingUtils.FLAME_COST_MODIFER, "b")
														:UtilText.formatAsEssences(EnchantingUtils.getCost(ingredient, effects), "b", false))
												+"
"
											+"<form style='padding:0; margin:0 0 4px 0; text-align:center;'><input type='text' id='output_name' value='" +UtilText.parseForHTMLDisplay(outputName)+"' style='padding:0;margin:0;width:80%;'></form>"
								);

				if(effects.isEmpty()) {
					inventorySB.append("
No effects added");
				} else {
					int cost = 0;

					for(int it=0; it<effects.size(); it++) {
						ItemEffect ie = effects.get(it);

						if(ie.getItemEffectType()==ItemEffectType.CLOTHING
								|| ie.getItemEffectType()==ItemEffectType.WEAPON
								|| ie.getItemEffectType()==ItemEffectType.TATTOO) {
							if(ie.getPrimaryModifier()==TFModifier.CLOTHING_ATTRIBUTE
									|| ie.getPrimaryModifier()==TFModifier.CLOTHING_MAJOR_ATTRIBUTE) {
								if(effect.getSecondaryModifier()==TFModifier.CORRUPTION
										|| effect.getSecondaryModifier()==TFModifier.FERTILITY
										|| effect.getSecondaryModifier()==TFModifier.VIRILITY) {
									cost = 0;
								}
								cost += Math.max(0, ie.getPotency().getClothingBonusValue());
							}
						}

						for(String s : ie.getEffectsDescription(Main.game.getPlayer(), Main.game.getPlayer())) {
							inventorySB.append(
									"
"
										+Util.capitaliseSentence(s)
										+(ingredient.getEffects().contains(ie)
												?"
"
														+ "X "
														+ (ingredient instanceof Tattoo
																?UtilText.formatAsMoney(EnchantingUtils.getModifierEffectCost(false, ingredient, ie)*EnchantingUtils.FLAME_COST_MODIFER, "b")
																:UtilText.formatAsEssences(EnchantingUtils.getModifierEffectCost(false, ingredient, ie), "b", false))
														+ "

"
													+ "
"
												:"
X
")
									+"
");
						}
					}

					if(Main.game.isEnchantmentCapacityEnabled()) {
						if((ingredient instanceof AbstractClothing)
								|| (ingredient instanceof AbstractWeapon)
								|| (ingredient instanceof Tattoo)) {
							inventorySB.append("
"
									+ (cost>0
											?"[style.colourEnchantment("+Util.capitaliseSentence(Attribute.ENCHANTMENT_LIMIT.getName())+" cost)]: [style.boldBad("+cost+")]"
											:Util.capitaliseSentence(Attribute.ENCHANTMENT_LIMIT.getName())+" cost: [style.boldDisabled(0)]"));
						}
					}
				}
			inventorySB.append("
");

			inventorySB.append("
");
				inventorySB.append("Output"
						+ "
"
						+ "
"+EnchantingUtils.getSVGString(ingredient, effects)+"
"
						+ "

"
						+ "
");
			inventorySB.append("
");

		inventorySB.append("
");
		inventorySB.append("

");

		return inventorySB.toString();
	}

	public static DialogueNode getEnchantmentMenu(AbstractCoreItem item) {
		return getEnchantmentMenu(item, null, null);
	}

	public static DialogueNode getEnchantmentMenu(AbstractCoreItem item, GameCharacter tattooBearer, InventorySlot tattooSlot) {
		EnchantmentDialogue.effects.clear();
		EnchantmentDialogue.resetEnchantmentVariables();
		EnchantmentDialogue.initModifiers(item, tattooBearer, tattooSlot);

		EnchantmentDialogue.setOutputName(EnchantingUtils.getPotionName(item, effects));

		return ENCHANTMENT_MENU;
	}

	/**
	 * Use getEnchantmentMenu() to get this DialogueNodeOld when initially opening the Enchantment menu, as it resets all variables for you.
	 */
	public static final DialogueNode ENCHANTMENT_MENU = new DialogueNode("Enchantments", "", true) {

		@Override
		public String getLabel() {
			return "Enchanting";
		}

		@Override
		public String getHeaderContent() {
			return inventoryView();
		}

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

		@Override
		public Response getResponse(int responseTab, int index) {
			if (index == 0) {
				return new Response("Back", "Stop enchanting.", InventoryDialogue.ITEM_INVENTORY) {
					@Override
					public DialogueNode getNextDialogue() {
						if(ingredient instanceof AbstractItem) {
							if(InventoryDialogue.getItem()==null) {
								InventoryDialogue.setItem((AbstractItem) ingredient);
							}
							return InventoryDialogue.ITEM_INVENTORY;

						} else if(ingredient instanceof AbstractClothing) {
							if(InventoryDialogue.getClothing()==null) {
								InventoryDialogue.setClothing((AbstractClothing) ingredient);
							}
							if(Main.game.getPlayer().getClothingCurrentlyEquipped().contains(ingredient)) {
								return InventoryDialogue.CLOTHING_EQUIPPED;
							} else {
								return InventoryDialogue.CLOTHING_INVENTORY;
							}

						} else if(ingredient instanceof AbstractWeapon){
							if(InventoryDialogue.getWeapon()==null) {
								InventoryDialogue.setWeapon(isEquippedIn, (AbstractWeapon) ingredient);
							}
							if(Main.game.getPlayer().hasWeaponEquipped((AbstractWeapon) ingredient)) {
								return InventoryDialogue.WEAPON_EQUIPPED;
							} else {
								return InventoryDialogue.WEAPON_INVENTORY;
							}

						} else if(ingredient instanceof Tattoo){
							if(BodyChanging.getTarget().isPlayer()) {
								return SuccubisSecrets.SHOP_BEAUTY_SALON_TATTOOS;
							} else {
								return CompanionManagement.SLAVE_MANAGEMENT_TATTOOS;
							}

						} else {
							throw new IllegalStateException("If it's not an item, not clothing, and not a weapon, then what?");
						}
					}
					@Override
					public void effects() {
						Main.game.setResponseTab(1);
						EnchantmentDialogue.resetEnchantmentVariables();
					}
				};

			// Ingredients:
			} else if (index == 1) {
				int price = EnchantingUtils.getCost(ingredient, effects)*EnchantingUtils.FLAME_COST_MODIFER;

				if((effects.equals(ingredient.getEffects())
						|| (effects.isEmpty() && ingredient instanceof AbstractItem))
//						 && outputName.equals(ingredient.getName())
						 ) {
					return new Response("Craft", "You need to add at least one effect before you can craft something!", null);

				} else if(canAffordCost(ingredient, effects)) {
					return new ResponseEffectsOnly((ingredient instanceof Tattoo
																?"Enchant ("+UtilText.formatAsMoney(price, "span")+")"
																:"Craft"),
													"Craft '"+EnchantingUtils.getPotionName(ingredient, effects)+"'."
															+ ((ingredient instanceof Tattoo)
																	?""
																	:" This will cost [style.boldArcane("+EnchantingUtils.getCost(ingredient, effects)+")] arcane essences.")){
						@Override
						public void effects() {
							Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
							EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());

							craftItem(ingredient, effects);

							if((previousIngredient instanceof AbstractItem && Main.game.getPlayer().hasItem((AbstractItem) previousIngredient))
									|| (previousIngredient instanceof AbstractClothing && Main.game.getPlayer().hasClothing((AbstractClothing) previousIngredient))
									|| (previousIngredient instanceof AbstractWeapon && Main.game.getPlayer().hasWeapon((AbstractWeapon) previousIngredient))) {
								ingredient = previousIngredient;
								effects = new ArrayList<>(previousEffects);
								Main.game.setContent(new Response("", "", ENCHANTMENT_MENU));

							} else {
								if(previousIngredient instanceof Tattoo) {
									if(BodyChanging.getTarget().isPlayer()) {
										Main.game.setContent(new Response("", "", SuccubisSecrets.SHOP_BEAUTY_SALON_TATTOOS));
									} else {
										Main.game.setContent(new Response("", "", CompanionManagement.SLAVE_MANAGEMENT_TATTOOS));
									}
								} else {
									Main.game.setContent(new Response("", "", InventoryDialogue.INVENTORY_MENU));
								}
							}

						}
					};

				} else {
					return new Response(
							(ingredient instanceof Tattoo
									?"Enchant ("+UtilText.formatAsMoneyUncoloured(price, "span")+")"
									:"Craft"),
							"You can't afford to craft this right now!", null);
				}

			// Save/load
			} else if (index == 2) {
				return new Response("Save/Load", "Save/Load enchantment recipes.", ENCHANTMENT_SAVE_LOAD) {
					@Override
					public void effects() {
						Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
						EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
						initSaveLoadMenu();
					}
				};

			} else {
				return null;
			}
		}

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

	public static boolean canAffordCost(AbstractCoreItem ingredient, List<ItemEffect> itemEffects) {
		if(ingredient instanceof Tattoo) {
			return Main.game.getPlayer().getMoney()  >= EnchantingUtils.getCost(ingredient, itemEffects)*EnchantingUtils.FLAME_COST_MODIFER;
		}
		return Main.game.getPlayer().getEssenceCount(ingredient.getRelatedEssence()) >= EnchantingUtils.getCost(ingredient, itemEffects);
	}

	public static AbstractCoreItem craftItem(AbstractCoreItem ingredient, List<ItemEffect> effects) {

		if(ingredient instanceof AbstractItem) {
			Main.game.getPlayer().removeItem((AbstractItem) ingredient);
			AbstractItem craftedItem = EnchantingUtils.craftItem(ingredient, effects);
			Main.game.getPlayer().addItem(craftedItem, false);
			Main.game.addEvent(new EventLogEntry(Main.game.getMinutesPassed(), "[style.colourExcellent(Item Enchanted)]", Util.capitaliseSentence(craftedItem.getName(false, true))), false);
			finaliseCrafting(ingredient, effects);
			return craftedItem;

		} else if(ingredient instanceof AbstractClothing) {
			Main.game.getPlayer().removeClothing((AbstractClothing) ingredient);
			AbstractClothing craftedClothing = EnchantingUtils.craftClothing(ingredient, effects);
			Main.game.getPlayer().addClothing(craftedClothing, false);
			Main.game.addEvent(new EventLogEntry(Main.game.getMinutesPassed(), "[style.colourExcellent(Clothing Enchanted)]", Util.capitaliseSentence(craftedClothing.getName(false, true))), false);
			finaliseCrafting(ingredient, effects);
			return craftedClothing;

		} else if(ingredient instanceof AbstractWeapon) {
			Main.game.getPlayer().removeWeapon((AbstractWeapon) ingredient);
			AbstractWeapon craftedWeapon = EnchantingUtils.craftWeapon(ingredient, effects);
			Main.game.getPlayer().addWeapon(craftedWeapon, false);
			Main.game.addEvent(new EventLogEntry(Main.game.getMinutesPassed(), "[style.colourExcellent(Weapon Enchanted)]", Util.capitaliseSentence(craftedWeapon.getName(false, true))), false);
			finaliseCrafting(ingredient, effects);
			return craftedWeapon;

		} else if(ingredient instanceof Tattoo) {
			Main.game.getPlayer().incrementMoney(-EnchantingUtils.getCost(ingredient, effects)*EnchantingUtils.FLAME_COST_MODIFER);
			Tattoo tattoo;
			if (EnchantmentDialogue.isEquipped) {
				EnchantmentDialogue.isEquippedTo.removeTattoo(EnchantmentDialogue.isEquippedIn);
				tattoo = EnchantingUtils.craftTattoo(ingredient, effects);
				EnchantmentDialogue.isEquippedTo.addTattoo(EnchantmentDialogue.isEquippedIn, (Tattoo) ingredient);
			} else {
				tattoo = EnchantingUtils.craftTattoo(ingredient, effects);
			}
			Main.game.addEvent(new EventLogEntry(Main.game.getMinutesPassed(), "[style.colourExcellent(Tattoo Enchanted)]", Util.capitaliseSentence(((Tattoo)ingredient).getName())), false);
			finaliseCrafting(ingredient, effects);
			return tattoo;
		}

		return null;
	}

	private static void finaliseCrafting(AbstractCoreItem ingredient, List<ItemEffect> effects) {
		if(!(ingredient instanceof Tattoo)) {
			Main.game.getPlayer().incrementEssenceCount(ingredient.getRelatedEssence(), -EnchantingUtils.getCost(ingredient, effects), false);
		}

		previousIngredient = ingredient;
		previousPrimaryMod = primaryMod;
		previousSecondaryMod = secondaryMod;
		previousEffects.clear();
		previousEffects.addAll(EnchantmentDialogue.effects);

		resetEnchantmentVariables();
		EnchantmentDialogue.effects.clear();
	}

	public static void resetEnchantmentVariables() {
		EnchantmentDialogue.ingredient = null;
		EnchantmentDialogue.primaryMod = TFModifier.NONE;
		EnchantmentDialogue.secondaryMod = TFModifier.NONE;
		EnchantmentDialogue.potency = TFPotency.MINOR_BOOST;
		EnchantmentDialogue.limit = 0;
		EnchantmentDialogue.tattooBearer = null;
		EnchantmentDialogue.tattooSlot = null;
		EnchantmentDialogue.isEquipped = false;
		EnchantmentDialogue.isEquippedIn = null;
		EnchantmentDialogue.isEquippedTo = null;
	}

	public static void resetNonTattooEnchantmentVariables() {
		if(!(ingredient instanceof Tattoo)) {
			EnchantmentDialogue.ingredient = null;
			EnchantmentDialogue.tattooBearer = null;
			EnchantmentDialogue.tattooSlot = null;
		}
		EnchantmentDialogue.primaryMod = TFModifier.NONE;
		EnchantmentDialogue.secondaryMod = TFModifier.NONE;
		EnchantmentDialogue.potency = TFPotency.MINOR_BOOST;
		EnchantmentDialogue.limit = 0;
	}

	public static void initModifiers(AbstractCoreItem ingredient) {
		initModifiers(ingredient, EnchantmentDialogue.tattooBearer, EnchantmentDialogue.tattooSlot);
	}

	public static void initModifiers(AbstractCoreItem ingredient, GameCharacter tattooBearer, InventorySlot tattooSlot) {
		EnchantmentDialogue.ingredient = ingredient;
		EnchantmentDialogue.tattooBearer = tattooBearer;
		EnchantmentDialogue.tattooSlot = tattooSlot;

		if(ingredient instanceof AbstractClothing
				|| ingredient instanceof Tattoo
				|| ingredient instanceof AbstractWeapon) {
			EnchantmentDialogue.effects = new ArrayList<>(ingredient.getEffects());
			if (ingredient instanceof Tattoo && tattooBearer.getTattooInSlot(tattooSlot) == ingredient) {
				EnchantmentDialogue.isEquipped = true;
				EnchantmentDialogue.isEquippedIn = tattooSlot;
				EnchantmentDialogue.isEquippedTo = tattooBearer;
			}
		} else {
			EnchantmentDialogue.effects = new ArrayList<>();
		}

		if(!EnchantmentDialogue.ingredient.getEnchantmentEffect().getPrimaryModifiers().contains(EnchantmentDialogue.primaryMod)) {
			EnchantmentDialogue.primaryMod = EnchantmentDialogue.ingredient.getEnchantmentEffect().getPrimaryModifiers().get(0);
		}
		if(!EnchantmentDialogue.ingredient.getEnchantmentEffect().getSecondaryModifiers(EnchantmentDialogue.primaryMod).contains(EnchantmentDialogue.secondaryMod)) {
			EnchantmentDialogue.secondaryMod = EnchantmentDialogue.ingredient.getEnchantmentEffect().getSecondaryModifiers(EnchantmentDialogue.primaryMod).get(0);
		}
		if(!EnchantmentDialogue.ingredient.getEnchantmentEffect().getPotencyModifiers(EnchantmentDialogue.primaryMod, EnchantmentDialogue.secondaryMod).contains(EnchantmentDialogue.potency)) {
			EnchantmentDialogue.potency = TFPotency.MINOR_BOOST;
		}
		if(EnchantmentDialogue.limit <= EnchantmentDialogue.ingredient.getEnchantmentEffect().getLimits(EnchantmentDialogue.primaryMod, EnchantmentDialogue.secondaryMod)) {
			EnchantmentDialogue.limit = EnchantmentDialogue.ingredient.getEnchantmentEffect().getLimits(EnchantmentDialogue.primaryMod, EnchantmentDialogue.secondaryMod);
		}
	}

	public static void initSaveLoadMenu() {
		loadedEnchantmentsMap = new HashMap<>();

		for(File f : getSavedEnchants()) {
			try {
				String name = Util.getFileIdentifier(f);
				LoadedEnchantment loadedEnchant = loadEnchant(name);
				if(ingredient instanceof Tattoo?loadedEnchant.getTattooType()!=null:loadedEnchant.getTattooType()==null) {
					loadedEnchantmentsMap.put(name, loadedEnchant);
				}
			} catch(Exception ex) {
			}
		}
	}

	public static String loadConfirmationName = "", overwriteConfirmationName = "", deleteConfirmationName = "";
	public static final DialogueNode ENCHANTMENT_SAVE_LOAD = new DialogueNode("Save enchantment files", "", true) {

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

		@Override
		public String getHeaderContent(){
			StringBuilder saveLoadSB = new StringBuilder();

			saveLoadSB.append(
					"
"
						+ "<list style='padding:0;margin:0;'>"
							+ "
Only standard characters (letters and numbers) will work for save file names.
"
							+ "
Hover over each item's icon to see the effects to be saved/loaded.
"
							+ "
If a name is [style.colourBad(red)], then you don't have a suitable item in your inventory, and cannot load that effect.
"
							+ "
You can only save/overwrite saves if your enchantment has at least one effect added.
"
						+ "</list>"
					+ "
"
					+ "
"
						+ "
"
							+ "Name"
						+ "
"
						+ "
"
							+ "Save | Load | Delete"
						+ "
"
					+ "
");

			int i=0;

			saveLoadSB.append(getSaveLoadRow(null, null, i%2==0));
			i++;

			for(Entry<String, LoadedEnchantment> entry : loadedEnchantmentsMap.entrySet()){
				saveLoadSB.append(getSaveLoadRow(entry.getKey(), entry.getValue(), i%2==0));
				i++;
			}

			saveLoadSB.append("

");

			return saveLoadSB.toString();
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if (index == 1) {
				return new Response("Confirmations: ",
						"Toggle confirmations being shown when you click to load, overwrite, or delete a saved game."
							+ " When turned on, it will take two clicks to apply any button press."
							+ " When turned off, it will only take one click.",
						ENCHANTMENT_SAVE_LOAD) {
					@Override
					public String getTitle() {
						return "Confirmations: "+(Main.getProperties().hasValue(PropertyValue.overwriteWarning)
								?"ON"
								:"OFF");
					}

					@Override
					public void effects() {
						loadConfirmationName = "";
						overwriteConfirmationName = "";
						deleteConfirmationName = "";
						Main.getProperties().setValue(PropertyValue.overwriteWarning, !Main.getProperties().hasValue(PropertyValue.overwriteWarning));
						Main.getProperties().savePropertiesAsXML();
					}
				};

			} else if (index == 0) {
				return new Response("Back", "Back to the enchantment menu.", ENCHANTMENT_MENU);

			} else {
				return null;
			}
		}
	};

	public static List<File> getSavedEnchants() {
		List<File> filesList = new ArrayList<>();

		File dir = new File("data/enchantments");
		if (dir.isDirectory()) {
			File[] directoryListing = dir.listFiles((path, name) -> name.endsWith(".xml"));
			if (directoryListing != null) {
				filesList.addAll(Arrays.asList(directoryListing));
			}
		}

		filesList.sort(Comparator.comparing(File::getName).reversed());

		return filesList;
	}

	private static String getSaveLoadRow(String baseName, LoadedEnchantment loadedEnchantment, boolean altColour) {

		if(loadedEnchantment!=null){
			String fileName = (baseName+".xml");

			boolean suitableItemAvailable = loadedEnchantment.isSuitableItemAvailable();

			return "
"

						+ "
"

							+ "
"
								+"
"
									+ "
"
										+ loadedEnchantment.getSVGString()
									+ "
"
									+ "

"
								+ "
"
							+ "
"

							+ "
"
								+ "
"+(!suitableItemAvailable?"[style.boldBad("+loadedEnchantment.getName()+")]":loadedEnchantment.getName())+"
"
								+ "
[style.colourDisabled(data/enchantments/)]"+baseName+"[style.colourDisabled(.xml)]
"
							+"
"

						+ "
"
						+ "
"
							+ (Main.game.isStarted() && !Main.game.isInCombat() && !Main.game.isInSex() && !EnchantmentDialogue.getEffects().isEmpty()
									?(fileName.equals(overwriteConfirmationName)
										?"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskSaveConfirm()+"

"
										:"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskOverwrite()+"

")
									:"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskSaveDisabled()+"

")

							+ (suitableItemAvailable
									? (fileName.equals(loadConfirmationName)
										?"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskLoadConfirm()+"

"
										:"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskLoad()+"

")
									:"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskLoadDisabled()+"

")

							+ (fileName.equals(deleteConfirmationName)
								?"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskDeleteConfirm()+"

"
								:"

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskDelete()+"

")
						+ "
"
					+ "
";

		} else {
			if(EnchantmentDialogue.getEffects().isEmpty()) {
				return "
"

							+ "
"
								+"[style.colourDisabled(Cannot save an enchantment that has no effects added!)]"
							+ "
"

							+ "
"
								+ "

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskSaveDisabled()+"

"
							+ "
"
						+ "
";

			} else {
				String svgString = "";
				if(EnchantmentDialogue.getIngredient() instanceof AbstractItem) {
					svgString = ((AbstractItem)EnchantmentDialogue.getIngredient()).getSVGString();

				} else if(EnchantmentDialogue.getIngredient() instanceof AbstractClothing) {
					svgString = ((AbstractClothing)EnchantmentDialogue.getIngredient()).getSVGString();

				} else if(EnchantmentDialogue.getIngredient() instanceof AbstractWeapon) {
					svgString = ((AbstractWeapon)EnchantmentDialogue.getIngredient()).getSVGString();

				} else if(EnchantmentDialogue.getIngredient() instanceof Tattoo) {
					svgString = ((Tattoo)EnchantmentDialogue.getIngredient()).getSVGString();
				}

				return "
"

							+ "
"

								+ "
"
									+"
"
										+ "
"
											+ svgString
										+ "
"
										+ "

"
									+ "
"
								+ "
"

								+ "
"
									+"<form style='padding:0;margin:0;text-align:center;'><input type='text' id='new_save_name' placeholder='Enter File Name' style='padding:0;margin:0;width:100%;'></form>"
								+"
"

							+ "
"

							+ "
"
								+ "

"+SVGImages.SVG_IMAGE_PROVIDER.getDiskSave()+"

"
							+ "
"
						+ "
";
			}

		}
	}

	public static void saveEnchant(String name, boolean allowOverwrite) {
		if (name.length()==0) {
			Main.game.flashMessage(Colour.GENERIC_BAD, "Name too short!");
			return;
		}
		if (name.length() > 32) {
			Main.game.flashMessage(Colour.GENERIC_BAD, "Name too long!");
			return;
		}
		if (name.contains("\"")) {//!name.matches("[a-zA-Z0-9]+[a-zA-Z0-9' _]*")) {
			Main.game.flashMessage(Colour.GENERIC_BAD, "Incompatible characters!");
			return;
		}

		File dir = new File("data/");
		dir.mkdir();

		dir = new File("data/enchantments");
		dir.mkdir();

		if (dir.isDirectory()) {
			File[] directoryListing = dir.listFiles((path, filename) -> filename.endsWith(".xml"));
			if (directoryListing != null) {
				for (File child : directoryListing) {
					if (child.getName().equals(name+".xml")){
						if(!allowOverwrite) {
							Main.game.flashMessage(Colour.GENERIC_BAD, "Name already exists!");
							return;
						}
					}
				}
			}
		}

		try {
			// Starting stuff:

			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

			Document doc = docBuilder.newDocument();

			Element enchantment = doc.createElement("enchantment");
			doc.appendChild(enchantment);

			if(EnchantmentDialogue.getIngredient() instanceof AbstractItem) {
				Element itemTypeElement = doc.createElement("itemType");
				enchantment.appendChild(itemTypeElement);
				itemTypeElement.setTextContent(((AbstractItem)EnchantmentDialogue.getIngredient()).getItemType().getId());

			} else if(EnchantmentDialogue.getIngredient() instanceof AbstractClothing) {
				Element itemTypeElement = doc.createElement("clothingType");
				enchantment.appendChild(itemTypeElement);
				itemTypeElement.setTextContent(((AbstractClothing)EnchantmentDialogue.getIngredient()).getClothingType().getId());

			} else if(EnchantmentDialogue.getIngredient() instanceof AbstractWeapon) {
				Element itemTypeElement = doc.createElement("weaponType");
				enchantment.appendChild(itemTypeElement);
				itemTypeElement.setTextContent(((AbstractWeapon)EnchantmentDialogue.getIngredient()).getWeaponType().getId());

			} else if(EnchantmentDialogue.getIngredient() instanceof Tattoo) {
				Element itemTypeElement = doc.createElement("tattooType");
				enchantment.appendChild(itemTypeElement);
				itemTypeElement.setTextContent(((Tattoo)EnchantmentDialogue.getIngredient()).getType().getId());
			}

			Element nameElement = doc.createElement("name");
			enchantment.appendChild(nameElement);
			nameElement.appendChild(doc.createCDATASection(EnchantmentDialogue.getOutputName()));

			Element itemEffects = doc.createElement("itemEffects");
			enchantment.appendChild(itemEffects);

			for(ItemEffect effect : effects) {
				effect.saveAsXML(itemEffects, doc);
			}

			// Ending stuff:

			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer transformer1 = tf.newTransformer();
			transformer1.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			StringWriter writer = new StringWriter();

			transformer1.transform(new DOMSource(doc), new StreamResult(writer));

			// Save this xml:
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
			DOMSource source = new DOMSource(doc);

			String saveLocation = "data/enchantments/"+name+".xml";
			StreamResult result = new StreamResult(saveLocation);

			transformer.transform(source, result);

		} catch (ParserConfigurationException pce) {
			pce.printStackTrace();
		} catch (TransformerException tfe) {
			tfe.printStackTrace();
		}
		Main.game.setContent(new Response("Save", "", EnchantmentDialogue.ENCHANTMENT_MENU));
	}

	public static LoadedEnchantment loadEnchant(String name) {
		if (isLoadEnchantAvailable(name)) {
			File file = new File("data/enchantments/"+name+".xml");

			if (file.exists()) {
				try {
					DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
					DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
					Document doc = dBuilder.parse(file);

					// Cast magic:
					doc.getDocumentElement().normalize();

					String importedName = ((Element) doc.getElementsByTagName("name").item(0)).getTextContent();

					Element enchantment = (Element) doc.getElementsByTagName("enchantment").item(0);
					Element itemEffects = (Element) enchantment.getElementsByTagName("itemEffects").item(0);
					List<ItemEffect> effectsToBeAdded = new ArrayList<>();
					for(int i=0; i<itemEffects.getElementsByTagName("effect").getLength(); i++) {
						Element e = ((Element)itemEffects.getElementsByTagName("effect").item(i));
						ItemEffect itemEffect = ItemEffect.loadFromXML(e, doc);
						if(itemEffect!=null) {
							effectsToBeAdded.add(itemEffect);
						}
					}

					if(doc.getElementsByTagName("itemType").item(0)!=null) {
						return new LoadedEnchantment(importedName, ItemType.getIdToItemMap().get(doc.getElementsByTagName("itemType").item(0).getTextContent()), effectsToBeAdded);

					} else if(doc.getElementsByTagName("clothingType").item(0)!=null) {
						return new LoadedEnchantment(importedName, ClothingType.getClothingTypeFromId(doc.getElementsByTagName("clothingType").item(0).getTextContent()), effectsToBeAdded);

					} else if(doc.getElementsByTagName("weaponType").item(0)!=null) {
						return new LoadedEnchantment(importedName, WeaponType.getWeaponTypeFromId(doc.getElementsByTagName("weaponType").item(0).getTextContent()), effectsToBeAdded);

					} else if(doc.getElementsByTagName("tattooType").item(0)!=null) {
						return new LoadedEnchantment(importedName, TattooType.getTattooTypeFromId(doc.getElementsByTagName("tattooType").item(0).getTextContent()), effectsToBeAdded);
					}

				} catch(Exception e) {
					e.printStackTrace();
				}
			}
		}

		return null;
	}

	public static boolean isLoadEnchantAvailable(String name) {
		File file = new File("data/enchantments/"+name+".xml");

		if(!file.exists()) {
			return false;
		}

		return true;
	}

	public static void deleteEnchant(String name) {
		File file = new File("data/enchantments/"+name+".xml");

		if (file.exists()) {
			try {
				file.delete();
				Main.game.setContent(new Response("", "", Main.game.getCurrentDialogueNode()));
			} catch (Exception ex) {
				ex.printStackTrace();
			}

		} else {
			Main.game.flashMessage(Colour.GENERIC_BAD, "File not found...");
		}
	}

	public static AbstractCoreItem getIngredient() {
		return ingredient;
	}

	public static void setIngredient(AbstractCoreItem ingredient) {
		EnchantmentDialogue.ingredient = ingredient;
	}

	public static AbstractCoreItem getPreviousIngredient() {
		return previousIngredient;
	}

	public static void setPreviousIngredient(AbstractCoreItem previousIngredient) {
		EnchantmentDialogue.previousIngredient = previousIngredient;
	}

	public static List<ItemEffect> getEffects() {
		return effects;
	}

	public static boolean addEffect(ItemEffect effect) {
		boolean defaultName = EnchantingUtils.getPotionName(EnchantmentDialogue.getIngredient(), EnchantmentDialogue.getEffects()).equalsIgnoreCase(EnchantmentDialogue.getOutputName());

		boolean added = false;

		if(!(ingredient instanceof Tattoo) || effects.size()<ingredient.getEnchantmentLimit()) {
			added = getEffects().add(effect);

			if(added) {
				if(defaultName) {
					EnchantmentDialogue.setOutputName(EnchantingUtils.getPotionName(EnchantmentDialogue.getIngredient(), EnchantmentDialogue.getEffects()));
				} else {
					if(Main.game.getCurrentDialogueNode().equals(EnchantmentDialogue.ENCHANTMENT_MENU)) {
						Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
						EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
					}
				}
			}
		}

		return added;
	}

	public static boolean removeEffect(int index) {
		boolean defaultName = EnchantingUtils.getPotionName(EnchantmentDialogue.getIngredient(), EnchantmentDialogue.getEffects()).equalsIgnoreCase(EnchantmentDialogue.getOutputName());
		getEffects().remove(index);

		if(defaultName) {
			EnchantmentDialogue.setOutputName(EnchantingUtils.getPotionName(EnchantmentDialogue.getIngredient(), EnchantmentDialogue.getEffects()));
		} else {
			if(Main.game.getCurrentDialogueNode().equals(EnchantmentDialogue.ENCHANTMENT_MENU)) {
				Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
				EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
			}
		}

		return true;
	}

	public static boolean removeEffect(ItemEffect effect) {
		boolean defaultName = EnchantingUtils.getPotionName(EnchantmentDialogue.getIngredient(), EnchantmentDialogue.getEffects()).equalsIgnoreCase(EnchantmentDialogue.getOutputName());
		boolean removed = getEffects().remove(effect);

		if(removed) {
			if(defaultName) {
				EnchantmentDialogue.setOutputName(EnchantingUtils.getPotionName(EnchantmentDialogue.getIngredient(), EnchantmentDialogue.getEffects()));
			} else {
				if(Main.game.getCurrentDialogueNode().equals(EnchantmentDialogue.ENCHANTMENT_MENU)) {
					Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
					EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
				}
			}
		}

		return removed;
	}

	public static TFModifier getPrimaryMod() {
		return primaryMod;
	}

	public static void setPrimaryMod(TFModifier primaryMod) {
		EnchantmentDialogue.primaryMod = primaryMod;
		if(Main.game.getCurrentDialogueNode().equals(EnchantmentDialogue.ENCHANTMENT_MENU)) {
			Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
			EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
		}
	}

	public static TFModifier getSecondaryMod() {
		return secondaryMod;
	}

	public static void setSecondaryMod(TFModifier secondaryMod) {
		EnchantmentDialogue.secondaryMod = secondaryMod;
		if(Main.game.getCurrentDialogueNode().equals(EnchantmentDialogue.ENCHANTMENT_MENU)) {
			Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
			EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
		}
	}

	public static TFPotency getPotency() {
		return potency;
	}

	public static void setPotency(TFPotency potency) {
		EnchantmentDialogue.potency = potency;
		if(Main.game.getCurrentDialogueNode().equals(EnchantmentDialogue.ENCHANTMENT_MENU)) {
			Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
			EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
		}
	}

	public static int getLimit() {
		return limit;
	}

	public static void setLimit(int limit) {
		EnchantmentDialogue.limit = limit;
		if(Main.game.getCurrentDialogueNode().equals(EnchantmentDialogue.ENCHANTMENT_MENU)) {
			Main.mainController.getWebEngine().executeScript("document.getElementById('hiddenPField').innerHTML=document.getElementById('output_name').value;");
			EnchantmentDialogue.setOutputName(Main.mainController.getWebEngine().getDocument().getElementById("hiddenPField").getTextContent());
		}
	}

	public static String getOutputName() {
		return outputName;
	}

	public static void setOutputName(String outputName) {
		EnchantmentDialogue.outputName = outputName;
	}

	public static Map<String, LoadedEnchantment> getLoadedEnchantmentsMap() {
		return loadedEnchantmentsMap;
	}

	public static InventorySlot getTattooSlot() {
		return tattooSlot;
	}

	public static GameCharacter getTattooBearer() {
		return tattooBearer;
	}
}


code:
package com.lilithsthrone.game.dialogue.utils;

import com.lilithsthrone.game.character.GameCharacter;
import com.lilithsthrone.game.dialogue.DialogueNode;
import com.lilithsthrone.game.dialogue.responses.Response;
import com.lilithsthrone.main.Main;
import com.lilithsthrone.rendering.RenderingEngine;

/**
 * @since 0.1.99
 * @version 0.1.99
 * @author Innoxia
 */
public class GiftDialogue {

	private static GameCharacter receiver;
	private static DialogueNode dialogueToReturnTo;
	private static int returnDialogueTab;
	private static DialogueNode dialogueToProceedTo;
	private static int proceedDialogueTab;

	/**
	 * @param receiver The NPC to receive the gift.
	 * @param dialogueToReturnTo The DialogueNode that should be returned if the player backs out of the gift menu.
	 * @param returnDialogueTab The tab which should be selected when returning to dialogueToReturnTo.
	 * @param dialogueToProceedTo The DialogueNode that should be returned if the player gives a gift to the receiver.
	 * @param proceedDialogueTab The tab which should be selected when proceeding to dialogueToProceedTo.
	 * @return
	 */
	public static DialogueNode getGiftDialogue(GameCharacter receiver, DialogueNode dialogueToReturnTo, int returnDialogueTab, DialogueNode dialogueToProceedTo, int proceedDialogueTab) {
		GiftDialogue.receiver = receiver;
		GiftDialogue.dialogueToReturnTo = dialogueToReturnTo;
		GiftDialogue.returnDialogueTab = returnDialogueTab;
		GiftDialogue.dialogueToProceedTo = dialogueToProceedTo;
		GiftDialogue.proceedDialogueTab = proceedDialogueTab;

		return GIFT_DIALOGUE;
	}

	public static final DialogueNode GIFT_DIALOGUE = new DialogueNode("Choose Gift", "-", true) {

		@Override
		public String getContent() {
			return UtilText.parse(receiver,
					"<p>"
						+ "The following items are suitable to be given as a gift to [npc.name]. Select the one that you'd like to give to [npc.herHim]."
					+ "</p>")
					+RenderingEngine.ENGINE.getGiftDiv(receiver);
		}

		@Override
		public Response getResponse(int responseTab, int index) {
			if (index == 0) {
				return new Response("Back", "Return to the previous screen.", dialogueToReturnTo) {
					@Override
					public void effects() {
						Main.game.setResponseTab(returnDialogueTab);
					}
				};

			} else {
				return null;
			}
		}
	};

	public static GameCharacter getReceiver() {
		return receiver;
	}

	public static DialogueNode getDialogueToProceedTo() {
		return dialogueToProceedTo;
	}

	public static int getProceedDialogueTab() {
		return proceedDialogueTab;
	}
}