package com.hero;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;

import javax.swing.JOptionPane;

import org.jdom.CDATA;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import com.hero.objects.Adder;
import com.hero.objects.GenericObject;
import com.hero.objects.List;
import com.hero.objects.characteristics.Characteristic;
import com.hero.objects.disads.Disadvantage;
import com.hero.objects.martialarts.Maneuver;
import com.hero.objects.perks.Follower;
import com.hero.objects.perks.Perk;
import com.hero.objects.perks.Vehicle;
import com.hero.objects.powers.Duplication;
import com.hero.objects.powers.Multiform;
import com.hero.objects.powers.Power;
import com.hero.objects.powers.Summon;
import com.hero.objects.skills.Skill;
import com.hero.objects.talents.MageSight;
import com.hero.objects.talents.Talent;
import com.hero.util.Base64;
import com.hero.util.Rounder;
import com.hero.util.Utility;
import com.hero.util.XMLUtility;

public class Hero implements Cloneable {

	private int agePosition = 0;

	private long ageParentID = -1;

	private String alternateIdentities;

	private String appearance;

	private String background;

	// Basic template info:
	private int basePoints;

	private String campaignName;

	private String campaignUse;

	private long characteristicCalcTime;

	private ArrayList<GenericObject> characteristics;

	private double characteristicTotal;

	// Basic Character info:
	protected String characterName;

	protected boolean dirty = false;

	private int disadPoints;

	protected ArrayList<GenericObject> disads;

	private int disadsUsed;

	private long disadsUsedCalcTime;

	protected ArrayList<GenericObject> equipment;

	private int experience;

	private String exportTemplate;

	private String eyeColor;

	private String genre;

	private String gm;

	private String hairColor;

	private double height;

	private byte[] imageData;

	private String imageFileName;

	private String imageFilePath;

	private boolean isLoading = false;

	protected long lastEdit;

	protected int lastTab;

	protected ArrayList<GenericObject> maneuvers;

	private Adder ncmObject;

	private int ncmPosition = 0;

	private long ncmParentID = -1;

	private boolean ncmSelected;

	private String notes1;

	private String notes2;

	private String notes3;

	private String notes4;

	private String notes5;

	protected File openFile;

	protected String originalTemplateID;

	protected ArrayList<GenericObject> perks;

	private String personality;

	protected String playerName;

	protected ArrayList<GenericObject> powers;

	private boolean prefabLoad;

	private String quote;

	protected Rules rules;

	protected File saveFile;

	protected ArrayList<GenericObject> skills;

	private long spentTotalCalcTime;

	private String tactics;

	protected ArrayList<GenericObject> talents;

	private String templatePath;

	private double total = -999999999999d;

	private double weight;

	private ArrayList<Prefab> prefabs;

	private boolean mOnlineCharacter = false;

	private String mRole;
	private String mUse;
	private String mGender;

	/**
	 * Creates a new Hero, using the specified Template
	 * 
	 * @param template
	 *            The template to be used for the new Hero
	 */
	public Hero() throws Exception {
		isLoading = true;
		Utility.garbageCollect("Loading new character/prefab");
		if (HeroDesigner.getActiveTemplate() == null) {
			throw new Exception("Null Template!");
		}
		if (HeroDesigner.getActiveHero() != null) {
			originalTemplateID = HeroDesigner.getActiveTemplate().getId();
		}
		rules = new Rules();
		if (HeroDesigner.getInstance().getPrefs().getLastRule().trim().length() == 0) {
			rules.useDefault();
		} else {
			rules.loadRulesDefinition(HeroDesigner.getInstance().getPrefs()
					.getLastRule());
		}
		basePoints = rules.getBasePoints();
		disadPoints = rules.getDisadPoints();
		ncmSelected = rules.isNCMSelected();
		init();
		isLoading = false;
	}

	/**
	 */
	public Hero(InputStream pIS) {
		isLoading = true;
		mOnlineCharacter = true;
		Utility.garbageCollect("Loading file from HD Online stream");
		String templateName = "builtIn.Superheroic.hdt";

		try {
			Element root = new Element("EMPTY");
			SAXBuilder builder = new SAXBuilder(false);
			Document doc = builder.build(pIS);
			builder = null;
			root = doc.getRootElement();
			Element t = root.getChild("TEMPLATE");
			if (t != null) {
				boolean newFile = false;
				String check = XMLUtility.getValue(t, "originalPath");
				if (check != null) {
					File f = new File(check);
					if (f.exists()) {
						if (f.lastModified() > saveFile.lastModified()) {
							int input = JOptionPane.YES_OPTION;
							if (!HeroDesigner.headless) {
								input = JOptionPane
										.showConfirmDialog(
												HeroDesigner.getAppFrame(),
												"There appears to be a newer copy of the template (hdt) file used on this character.  Would you like to load the new template file?",
												"New Template Found for "
														+ saveFile.getName(),
												JOptionPane.YES_NO_OPTION);
							}
							if (input == JOptionPane.YES_OPTION) {
								HeroDesigner.getInstance().setTemplate(check,
										false, false, false);
								if (HeroDesigner.getActiveTemplate() == null) {
									HeroDesigner.getInstance().setTemplate(
											"builtIn.Superheroic.hdt", true,
											false, false);
								}
								originalTemplateID = HeroDesigner
										.getActiveTemplate().getId();
								templatePath = HeroDesigner.getActiveTemplate()
										.getId();
								newFile = true;
							}
						}
					} else {
						f = new File(HeroDesigner.getInstance().getPrefs()
								.getTemplateDir()
								+ File.separator + f.getName());
						if (f.exists()) {
							if (f.lastModified() > saveFile.lastModified()) {
								int input = JOptionPane.YES_OPTION;
								if (!HeroDesigner.headless) {
									input = JOptionPane
											.showConfirmDialog(
													HeroDesigner.getAppFrame(),
													"There appears to be a newer copy of the template (hdt) file used on this character.  Would you like to load the new template file?",
													"New Template Found for "
															+ saveFile
																	.getName(),
													JOptionPane.YES_NO_OPTION);
								}
								if (input == JOptionPane.YES_OPTION) {
									HeroDesigner.getInstance().setTemplate(
											check, false, false, false);
									if (HeroDesigner.getActiveTemplate() == null) {
										HeroDesigner.getInstance().setTemplate(
												"builtIn.Superheroic.hdt",
												true, false, false);
									}
									originalTemplateID = HeroDesigner
											.getActiveTemplate().getId();
									templatePath = HeroDesigner
											.getActiveTemplate().getId();
									newFile = true;
								}
							}
						}
					}
				}
				if (!newFile) {
					Template temp = new Template(t, openFile.getAbsolutePath(),
							HeroDesigner.getInstance().getActiveTemplate(),
							new Date(saveFile.lastModified()));
					HeroDesigner.getInstance().setActiveTemplate(temp);
					originalTemplateID = HeroDesigner.getActiveTemplate()
							.getId();
					templatePath = HeroDesigner.getActiveTemplate().getId();
				}
			} else if (root.getAttributeValue("TEMPLATE") != null
					&& root.getAttributeValue("TEMPLATE").trim().length() > 0) {
				templateName = root.getAttributeValue("TEMPLATE");
				HeroDesigner.getInstance().setTemplate(templateName,
						templateName.startsWith("builtIn."), false, false);
				if (HeroDesigner.getActiveTemplate() == null) {
					HeroDesigner.getInstance().setTemplate(
							"builtIn.Superheroic.hdt", true, false, false);
				}
				originalTemplateID = HeroDesigner.getActiveTemplate().getId();
				templatePath = HeroDesigner.getActiveTemplate().getId();
			} else {
				Element basic = root.getChild("BASIC_CONFIGURATION");
				if (basic != null) {
					// get the template
					templateName = XMLUtility.getValue(basic, "TEMPLATE");
					if (templateName == null
							|| templateName.trim().length() == 0) {
						templateName = "builtIn.Superheroic.hdt";
					}
				}
				HeroDesigner.getInstance().setTemplate(templateName,
						templateName.startsWith("builtIn."), false, false);
				if (HeroDesigner.getActiveTemplate() == null) {
					HeroDesigner.getInstance().setTemplate(
							"builtIn.Superheroic.hdt", true, false, false);
				}
				originalTemplateID = HeroDesigner.getActiveTemplate().getId();
				templatePath = HeroDesigner.getActiveTemplate().getId();
			}

			initFromSave(doc);

		} catch (Exception exp) {
			templateName = "builtIn.Superheroic.hdt";
			exp.printStackTrace();
			if (!HeroDesigner.headless) {
				JOptionPane
						.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred retrieving the template used for the saved character.\nUsing 'Standard Super' template.\n\nError Message: "
										+ exp.getMessage(),
								"Error retrieving template",
								JOptionPane.ERROR_MESSAGE);
			}
		}

		isLoading = false;
	}

	/**
	 * Loads an existing Hero from the specified file.
	 * 
	 * @param saveFile
	 *            The file that the Hero is saved in.
	 */
	public Hero(File saveFile) {
		isLoading = true;
		Utility.garbageCollect("Loading file " + saveFile.getAbsolutePath());
		String templateName = "builtIn.Superheroic.hdt";
		openFile = saveFile;
		if (saveFile.getName().trim().toUpperCase().endsWith(".HDT")) {
			templateName = saveFile.getAbsolutePath();
			originalTemplateID = templateName;
			HeroDesigner.getInstance().setTemplate(templateName, false, false,
					false);
		} else {
			this.saveFile = saveFile;
			try {
				Element root = new Element("EMPTY");
				if (saveFile.exists() && saveFile.canRead()) {
					SAXBuilder builder = new SAXBuilder(false);
					Document doc = builder.build(saveFile);
					builder = null;
					root = doc.getRootElement();
					Element t = root.getChild("TEMPLATE");
					if (t != null) {
						boolean newFile = false;
						String check = XMLUtility.getValue(t, "originalPath");
						if (check != null) {
							File f = new File(check);
							if (f.exists()) {
								if (f.lastModified() > saveFile.lastModified()) {
									int input = JOptionPane.YES_OPTION;
									if (!HeroDesigner.headless) {
										input = JOptionPane
												.showConfirmDialog(
														HeroDesigner
																.getAppFrame(),
														"There appears to be a newer copy of the template (hdt) file used on this character.  Would you like to load the new template file?",
														"New Template Found for "
																+ saveFile
																		.getName(),
														JOptionPane.YES_NO_OPTION);
									}
									if (input == JOptionPane.YES_OPTION) {
										HeroDesigner.getInstance().setTemplate(
												check, false, false, false);
										if (HeroDesigner.getActiveTemplate() == null) {
											HeroDesigner
													.getInstance()
													.setTemplate(
															"builtIn.Superheroic.hdt",
															true, false, false);
										}
										originalTemplateID = HeroDesigner
												.getActiveTemplate().getId();
										templatePath = HeroDesigner
												.getActiveTemplate().getId();
										newFile = true;
									}
								}
							} else {
								f = new File(HeroDesigner.getInstance()
										.getPrefs().getTemplateDir()
										+ File.separator + f.getName());
								if (f.exists()) {
									if (f.lastModified() > saveFile
											.lastModified()) {
										int input = JOptionPane.YES_OPTION;
										if (!HeroDesigner.headless) {
											input = JOptionPane
													.showConfirmDialog(
															HeroDesigner
																	.getAppFrame(),
															"There appears to be a newer copy of the template (hdt) file used on this character.  Would you like to load the new template file?",
															"New Template Found for "
																	+ saveFile
																			.getName(),
															JOptionPane.YES_NO_OPTION);
										}
										if (input == JOptionPane.YES_OPTION) {
											HeroDesigner.getInstance()
													.setTemplate(check, false,
															false, false);
											if (HeroDesigner
													.getActiveTemplate() == null) {
												HeroDesigner
														.getInstance()
														.setTemplate(
																"builtIn.Superheroic.hdt",
																true, false,
																false);
											}
											originalTemplateID = HeroDesigner
													.getActiveTemplate()
													.getId();
											templatePath = HeroDesigner
													.getActiveTemplate()
													.getId();
											newFile = true;
										}
									}
								}
							}
						}
						if (!newFile) {
							Template temp = new Template(t, openFile
									.getAbsolutePath(), HeroDesigner
									.getInstance().getActiveTemplate(),
									new Date(saveFile.lastModified()));
							HeroDesigner.getInstance().setActiveTemplate(temp);
							originalTemplateID = HeroDesigner
									.getActiveTemplate().getId();
							templatePath = HeroDesigner.getActiveTemplate()
									.getId();
						}
					} else if (root.getAttributeValue("TEMPLATE") != null
							&& root.getAttributeValue("TEMPLATE").trim()
									.length() > 0) {
						templateName = root.getAttributeValue("TEMPLATE");
						HeroDesigner.getInstance().setTemplate(templateName,
								templateName.startsWith("builtIn."), false,
								false);
						if (HeroDesigner.getActiveTemplate() == null) {
							HeroDesigner.getInstance().setTemplate(
									"builtIn.Superheroic.hdt", true, false,
									false);
						}
						originalTemplateID = HeroDesigner.getActiveTemplate()
								.getId();
						templatePath = HeroDesigner.getActiveTemplate().getId();
					} else {
						Element basic = root.getChild("BASIC_CONFIGURATION");
						if (basic != null) {
							// get the template
							templateName = XMLUtility.getValue(basic,
									"TEMPLATE");
							if (templateName == null
									|| templateName.trim().length() == 0) {
								templateName = "builtIn.Superheroic.hdt";
							}
						}
						HeroDesigner.getInstance().setTemplate(templateName,
								templateName.startsWith("builtIn."), false,
								false);
						if (HeroDesigner.getActiveTemplate() == null) {
							HeroDesigner.getInstance().setTemplate(
									"builtIn.Superheroic.hdt", true, false,
									false);
						}
						originalTemplateID = HeroDesigner.getActiveTemplate()
								.getId();
						templatePath = HeroDesigner.getActiveTemplate().getId();
					}
				}
			} catch (Exception exp) {
				templateName = "builtIn.Superheroic.hdt";
				exp.printStackTrace();
				if (!HeroDesigner.headless) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.getAppFrame(),
									"An error occurred retrieving the template used for the saved character.\nUsing 'Standard Super' template.\n\nError Message: "
											+ exp.getMessage(),
									"Error retrieving template",
									JOptionPane.ERROR_MESSAGE);
				}
			}
		}
		initFromSave();
		isLoading = false;
	}

	protected Hero(File saveFile, boolean isPrefabLoad) {
		isLoading = true;
		Utility.garbageCollect("Loading file " + saveFile.getAbsolutePath());
		this.saveFile = saveFile;
		openFile = saveFile;
		prefabLoad = isPrefabLoad;
		String templateName = "builtIn.Superheroic.hdt";
		try {
			Element root = new Element("EMPTY");
			if (saveFile.exists() && saveFile.canRead()) {
				SAXBuilder builder = new SAXBuilder(false);
				Document doc = builder.build(saveFile);
				builder = null;
				root = doc.getRootElement();
				Element t = root.getChild("TEMPLATE");
				if (t != null) {
					if (!isPrefabLoad) {
						Template temp = new Template(t, openFile
								.getAbsolutePath(), HeroDesigner.getInstance()
								.getActiveTemplate(), new Date(saveFile
								.lastModified()));
						HeroDesigner.getInstance().setActiveTemplate(temp);
					}
					originalTemplateID = HeroDesigner.getActiveTemplate()
							.getId();
					templatePath = HeroDesigner.getActiveTemplate().getId();
				} else if (root.getAttributeValue("TEMPLATE") != null
						&& root.getAttributeValue("TEMPLATE").trim().length() > 0
						&& !isPrefabLoad) {
					templateName = root.getAttributeValue("TEMPLATE");
					HeroDesigner.getInstance().setTemplate(templateName,
							templateName.startsWith("builtIn."), false, false);
					if (HeroDesigner.getActiveTemplate() == null) {
						HeroDesigner.getInstance().setTemplate(
								"builtIn.Superheroic.hdt", true, false, false);
					}
					originalTemplateID = HeroDesigner.getActiveTemplate()
							.getId();
					templatePath = HeroDesigner.getActiveTemplate().getId();
				} else {
					Element basic = root.getChild("BASIC_CONFIGURATION");
					if (basic != null) {
						// get the template
						templateName = XMLUtility.getValue(basic, "TEMPLATE");
						if (templateName == null
								|| templateName.trim().length() == 0) {
							templateName = "builtIn.Superheroic.hdt";
						}
					}
					if (!isPrefabLoad) {
						HeroDesigner.getInstance().setTemplate(templateName,
								templateName.startsWith("builtIn."), false,
								false);
						if (HeroDesigner.getActiveTemplate() == null) {
							HeroDesigner.getInstance().setTemplate(
									"builtIn.Superheroic.hdt", true, false,
									false);
						}
					}
					originalTemplateID = HeroDesigner.getActiveTemplate()
							.getId();
					templatePath = HeroDesigner.getActiveTemplate().getId();
				}
			}
		} catch (Exception exp) {
			templateName = "builtIn.Superheroic.hdt";
			exp.printStackTrace();
			if (!HeroDesigner.headless) {
				JOptionPane
						.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred retrieving the template used for the saved character.\nUsing 'Standard Super' template.\n\nError Message: "
										+ exp.getMessage(),
								"Error retrieving template",
								JOptionPane.ERROR_MESSAGE);
			}
		}

		initFromSave();
		isLoading = false;
	}

	public void setIsLoading(boolean val) {
		isLoading = val;
	}

	/**
	 * Load an existing character
	 * 
	 * @param of
	 *            The file to load the character from
	 * @param templateFile
	 *            The file containing the character template to use on this
	 *            character
	 * @param image
	 *            The file containing the image that has been set to this
	 *            character (can be null)
	 * @param sf
	 *            The file to save the character to
	 */
	public Hero(File of, File templateFile, File image, File sf) {
		isLoading = true;
		Utility.garbageCollect("Loading file " + sf.getAbsolutePath());
		saveFile = sf;
		openFile = of;
		if (templateFile != null) {
			HeroDesigner.getInstance().setTemplate(
					templateFile.getAbsolutePath(), false, false, false);
		} else {
			String templateName = "builtIn.Superheroic.hdt";
			try {
				Element root = new Element("EMPTY");
				if (of != null && of.exists() && of.canRead()) {
					SAXBuilder builder = new SAXBuilder(false);
					Document doc = builder.build(of);
					builder = null;
					root = doc.getRootElement();
					Element basic = root.getChild("BASIC_CONFIGURATION");
					if (basic != null) {
						// get the template
						templateName = XMLUtility.getValue(basic, "TEMPLATE");
						if (templateName == null
								|| templateName.trim().length() == 0) {
							templateName = "builtIn.Superheroic.hdt";
						}
					}
				}
			} catch (Exception exp) {
				templateName = "builtIn.Superheroic.hdt";
				exp.printStackTrace();
				if (!HeroDesigner.headless) {
					JOptionPane
							.showMessageDialog(
									HeroDesigner.getAppFrame(),
									"An error occurred retrieving the template used for the saved character.\nUsing 'Standard Super' template.\n\nError Message: "
											+ exp.getMessage(),
									"Error retrieving template",
									JOptionPane.ERROR_MESSAGE);
				}
			}
			HeroDesigner.getInstance().setTemplate(templateName,
					templateName.startsWith("builtIn."), false, false);
			if (HeroDesigner.getActiveTemplate() == null) {
				HeroDesigner.getInstance().setTemplate(
						"builtIn.Superheroic.hdt", true, false, false);
			}
			originalTemplateID = HeroDesigner.getActiveTemplate().getId();
		}
		if (HeroDesigner.getActiveTemplate() == null) {
			HeroDesigner.getInstance().setTemplate("builtIn.Superheroic.hdt",
					true, false, false);
		}
		originalTemplateID = HeroDesigner.getActiveTemplate().getId();
		templatePath = HeroDesigner.getActiveTemplate().getId();
		initFromSave();
		if (image != null && image.exists()) {
			imageData = new byte[(int) image.length()];
			imageFileName = image.getName();
			imageFilePath = image.getAbsolutePath();
			try {
				FileInputStream fis = new FileInputStream(image);
				fis.read(imageData);
				fis.close();
			} catch (FileNotFoundException exp) {
				exp.printStackTrace();
			} catch (IOException exp) {
				exp.printStackTrace();
			}
		}
		isLoading = false;
	}

	public Hero(Element root, Template template) {
		isLoading = true;
		Utility.garbageCollect("Loading file from template");
		HeroDesigner.getInstance().setActiveTemplate(template);
		originalTemplateID = template.getId();
		templatePath = template.getId();
		init();
		restoreCharacter(root);
		isLoading = false;
	}

	public Hero(Element root) {
		isLoading = true;
		Utility.garbageCollect("Loading file from template");
		Template template = HeroDesigner.getInstance().getActiveTemplate();
		originalTemplateID = template.getId();
		templatePath = template.getId();
		init();
		restoreCharacter(root);
		isLoading = false;
	}

	public void setUse(String pUse) {
		if (pUse != null) {
			if (pUse.equals("Solo") || pUse.equals("Group")
					|| pUse.equals("Master")) {
				mUse = pUse;
			} else {
				mUse = "";
			}
		} else
			mUse = "";
	}

	public String getUse() {
		if (mUse == null)
			mUse = "";
		return mUse;
	}

	public void setRole(String pRole) {
		if (pRole != null) {
			if (pRole.equals("Protagonist") || pRole.equals("Antagonist")
					|| pRole.equals("Neutral/NPC")) {
				mRole = pRole;
			} else {
				mRole = "";
			}
		} else {
			mRole = "";
		}
	}

	public String getRole() {
		if (mRole == null)
			mRole = "";
		return mRole;
	}

	public void setGender(String pGender) {
		if (pGender != null) {
			if (pGender.equals("Male") || pGender.equals("Female")
					|| pGender.equals("Other") || pGender.equals("None")) {
				mGender = pGender;
			} else {
				mGender = "";
			}
		} else {
			mGender = "";
		}
	}
	
	public String getGender() {
		if (mGender == null) mGender = "";
		return mGender;
	}
	

	public Properties getMetaData() {
		Properties meta = new Properties();
		meta.setProperty("RulesEdition", HeroDesigner.getActiveTemplate()
				.is6E() ? "6th Edition" : "5th Edition");
		// template
		if (HeroDesigner.getActiveTemplate() != null) {
			meta.setProperty("TemplateName", HeroDesigner.getActiveTemplate()
					.getName());
		} else {
			meta.setProperty("TemplateName", "[Unknown]");
		}
		total = 0;
		int charTotal = 0;
		int skillTotal = 0;
		int talentTotal = 0;
		int perkTotal = 0;
		int maneuverTotal = 0;
		int powerTotal = 0;
		long maxActive = 0;
		ArrayList<GenericObject> chars = getCharacteristics();

		for (GenericObject o : chars) {
			total += o.getRealCost();
			charTotal += o.getRealCost();
			if (o.getActiveCost() > maxActive) {
				maxActive = Rounder.roundHalfUp(o.getActiveCost());
			}
		}
		meta.setProperty("CharacteristicPoints", "" + charTotal);
		chars = getSkills();
		for (GenericObject o : chars) {
			total += o.getRealCost();
			skillTotal += o.getRealCost();
			if (o.getActiveCost() > maxActive) {
				maxActive = Rounder.roundHalfUp(o.getActiveCost());
			}
		}
		meta.setProperty("SkillsPoints", "" + skillTotal);
		chars = getPerks();
		for (GenericObject o : chars) {
			total += o.getRealCost();
			perkTotal += o.getRealCost();
			if (o.getActiveCost() > maxActive) {
				maxActive = Rounder.roundHalfUp(o.getActiveCost());
			}
		}
		meta.setProperty("PerksPoints", "" + perkTotal);
		chars = getTalents();
		for (GenericObject o : chars) {
			total += o.getRealCost();
			talentTotal += o.getRealCost();
			if (o.getActiveCost() > maxActive) {
				maxActive = Rounder.roundHalfUp(o.getActiveCost());
			}
		}
		meta.setProperty("TalentsPoints", "" + talentTotal);
		chars = getManeuvers();
		for (GenericObject o : chars) {
			total += o.getRealCost();
			maneuverTotal += o.getRealCost();
			if (o.getActiveCost() > maxActive) {
				maxActive = Rounder.roundHalfUp(o.getActiveCost());
			}
		}
		meta.setProperty("ManeuversPoints", "" + maneuverTotal);
		chars = getPowers();
		for (GenericObject o : chars) {
			total += o.getRealCost();
			powerTotal += o.getRealCost();
			if (o.getActiveCost() > maxActive) {
				maxActive = Rounder.roundHalfUp(o.getActiveCost());
			}
		}
		meta.setProperty("PowersPoints", "" + powerTotal);
		meta.setProperty("TotalPoints", "" + total);
		meta.setProperty("MaxActivePoints", "" + maxActive);
		meta.setProperty("DisadPoints", "" + this.getDisadsUsed());
		meta.setProperty("ImageDataAssigned", getImageData() != null
				&& getImageData().length > 0 ? "Yes" : "No");
		return meta;
	}

	public ArrayList<Prefab> getPrefabs() {
		return prefabs;
	}

	protected void applyPackageDeal(Hero deal) {
		// step 1: add in the prefabs:
		OUTER: for (Prefab p : deal.getPrefabs()) {
			for (Prefab check : prefabs) {
				if (check.getName().equals(p.getName())) {
					continue OUTER;
				}
			}
			prefabs.add(p);
		}

		// step 2: adjust characteristics
		for (GenericObject c : deal.getCharacteristics()) {
			for (GenericObject ch : getCharacteristics()) {
				if (ch.getXMLID().equals(c.getXMLID())) {
					ch.setLevels(ch.getLevels() + c.getLevels());
				}
			}
		}

		// step 3: add disads
		DISADS: for (GenericObject disad : deal.getDisads()) {
			for (GenericObject check : getDisads()) {
				if (check.getID() == disad.getID()) {
					continue DISADS;
				}
			}
			addDisad(disad);
		}

		// step 4: add other abilities (preferences-based)
		SKILLS: for (GenericObject skill : deal.getSkills()) {
			for (GenericObject check : getSkills()) {
				if (check.getID() == skill.getID()) {
					continue SKILLS;
				}
			}
			for (GenericObject check : getSkills()) {
				boolean exists = false;
				if (check.getXMLID().equals(skill.getXMLID())
						&& check.isExclusive()) {
					exists = true;
				} else if (check.getTextOutput().equals(skill.getTextOutput())) {
					exists = true;
				}
				if (exists) {
					// if (!addExistingAbility(check, skill)) {
					// continue SKILLS;
					// }
				}
			}
			addSkill(skill);
		}
		PERKS: for (GenericObject perk : deal.getPerks()) {
			for (GenericObject check : getPerks()) {
				if (check.getID() == perk.getID()) {
					continue PERKS;
				}
			}
			for (GenericObject check : getPerks()) {
				boolean exists = false;
				if (check.getXMLID().equals(perk.getXMLID())
						&& check.isExclusive()) {
					exists = true;
				} else if (check.getTextOutput().equals(perk.getTextOutput())) {
					exists = true;
				}
				if (exists) {
					// if (!addExistingAbility(check, perk)) {
					// continue PERKS;
					// }
				}
			}
			addPerk(perk);
		}
		TALENTS: for (GenericObject talent : deal.getTalents()) {
			for (GenericObject check : getTalents()) {
				if (check.getID() == talent.getID()) {
					continue TALENTS;
				}
			}
			for (GenericObject check : getTalents()) {
				boolean exists = false;
				if (check.getXMLID().equals(talent.getXMLID())
						&& check.isExclusive()) {
					exists = true;
				} else if (check.getTextOutput().equals(talent.getTextOutput())) {
					exists = true;
				}
				if (exists) {
					// if (!addExistingAbility(check, talent)) {
					// continue TALENTS;
					// }
				}
			}
			addTalent(talent);
		}
		MARTIALARTS: for (GenericObject ma : deal.getManeuvers()) {
			for (GenericObject check : getManeuvers()) {
				if (check.getID() == ma.getID()) {
					continue MARTIALARTS;
				}
			}
			for (GenericObject check : getManeuvers()) {
				boolean exists = false;
				if (check.getXMLID().equals(ma.getXMLID())
						&& check.isExclusive()) {
					exists = true;
				} else if (check.getTextOutput().equals(ma.getTextOutput())) {
					exists = true;
				}
				if (exists) {
					// if (!addExistingAbility(check, ma)) {
					// continue MARTIALARTS;
					// }
				}
			}
			addManeuver(ma);
		}
		POWERS: for (GenericObject power : deal.getPowers()) {
			for (GenericObject check : getPowers()) {
				if (check.getID() == power.getID()) {
					continue POWERS;
				}
			}
			for (GenericObject check : getPowers()) {
				boolean exists = false;
				if (check.getXMLID().equals(power.getXMLID())
						&& check.isExclusive()) {
					exists = true;
				} else if (check.getTextOutput().equals(power.getTextOutput())) {
					exists = true;
				}
				if (exists) {
					// if (!addExistingAbility(check, power)) {
					// continue POWERS;
					// }
				}
			}
			addPower(power);
		}
		EQUIPMENT: for (GenericObject power : deal.getEquipment()) {
			for (GenericObject check : getEquipment()) {
				if (check.getID() == power.getID()) {
					continue EQUIPMENT;
				}
			}
			for (GenericObject check : getEquipment()) {
				boolean exists = false;
				if (check.getXMLID().equals(power.getXMLID())
						&& check.isExclusive()) {
					exists = true;
				} else if (check.getTextOutput().equals(power.getTextOutput())) {
					exists = true;
				}
				if (exists) {
					// if (!addExistingAbility(check, power)) {
					// continue EQUIPMENT;
					// }
				}
			}
			addEquipment(power);
		}
	}

	protected void addDisad(GenericObject val) {
		if (val instanceof com.hero.objects.List) {
			// first we set the parent of any objects in the disad list that
			// should be in this list
			for (GenericObject go : disads) {
				if (go.getParentID() == val.getID()) {
					((com.hero.objects.List) val).addObject(go);
					go.setParent((com.hero.objects.List) val);
				}
			}
		}
		// now we check to see if the object being added belongs to any List
		if (val.getParentID() > 0) {
			for (GenericObject go : disads) {
				if (go instanceof com.hero.objects.List
						&& go.getID() == val.getParentID()) {
					((com.hero.objects.List) go).addObject(val);
					val.setParent((com.hero.objects.List) go);
				}
			}
		}
		// if wer get here, just add the danged object ;-)
		getDisads().add(val);
	}

	protected void addEquipment(GenericObject val) {
		val.setPower(true);
		val.setIsEquipment(true);
		if (val instanceof com.hero.objects.List) {
			// first we set the parent of any objects in the equipment list
			// that should be in this list
			for (GenericObject go : equipment) {
				if (go.getParentID() == val.getID()) {
					((com.hero.objects.List) val).addObject(go);
					go.setParent((com.hero.objects.List) val);
				}
			}
		}
		// now we check to see if the object being added belongs to any List
		if (val.getParentID() > 0) {
			for (GenericObject go : equipment) {
				if (go instanceof com.hero.objects.List
						&& go.getID() == val.getParentID()) {
					((com.hero.objects.List) go).addObject(val);
					val.setParent((com.hero.objects.List) go);
				}
			}
		}
		// if wer get here, just add the danged object ;-)
		getEquipment().add(val);
	}

	protected void addManeuver(GenericObject val) {
		if (val instanceof com.hero.objects.List) {
			// first we set the parent of any objects in the maneuver list that
			// should be in this list
			for (GenericObject go : maneuvers) {
				if (go.getParentID() == val.getID()) {
					((com.hero.objects.List) val).addObject(go);
					go.setParent((com.hero.objects.List) val);
				}
			}
		}
		// now we check to see if the object being added belongs to any List
		if (val.getParentID() > 0) {
			for (GenericObject go : maneuvers) {
				if (go instanceof com.hero.objects.List
						&& go.getID() == val.getParentID()) {
					((com.hero.objects.List) go).addObject(val);
					val.setParent((com.hero.objects.List) go);
				}
			}
		}
		// if wer get here, just add the danged object ;-)
		maneuvers.add(val);
	}

	protected void addPerk(GenericObject val) {
		if (val instanceof com.hero.objects.List) {
			// first we set the parent of any objects in the perk list that
			// should be in this list
			for (GenericObject go : perks) {
				if (go.getParentID() == val.getID()) {
					((com.hero.objects.List) val).addObject(go);
					go.setParent((com.hero.objects.List) val);
				}
			}
		}
		// now we check to see if the object being added belongs to any List
		if (val.getParentID() > 0) {
			for (GenericObject go : perks) {
				if (go instanceof com.hero.objects.List
						&& go.getID() == val.getParentID()) {
					((com.hero.objects.List) go).addObject(val);
					val.setParent((com.hero.objects.List) go);
				}
			}
		}
		// if wer get here, just add the danged object ;-)
		perks.add(val);
	}

	protected void addPower(GenericObject val) {
		val.setPower(true);
		val.setIsEquipment(false);
		if (val instanceof com.hero.objects.List) {
			// first we set the parent of any objects in the power list that
			// should be in this list
			for (GenericObject go : powers) {
				if (go.getParentID() == val.getID()) {
					((com.hero.objects.List) val).addObject(go);
					go.setParent((com.hero.objects.List) val);
				}
			}
		}
		// now we check to see if the object being added belongs to any List
		if (val.getParentID() > 0) {
			for (GenericObject go : powers) {
				if (go instanceof com.hero.objects.List
						&& go.getID() == val.getParentID()) {
					((com.hero.objects.List) go).addObject(val);
					val.setParent((com.hero.objects.List) go);
				}
			}
		}
		// if wer get here, just add the danged object ;-)
		powers.add(val);
	}

	protected void addSkill(GenericObject val) {
		if (val instanceof com.hero.objects.List) {
			// first we set the parent of any objects in the skill list that
			// should be in this list
			for (GenericObject go : skills) {
				if (go.getParentID() == val.getID()) {
					((com.hero.objects.List) val).addObject(go);
					go.setParent((com.hero.objects.List) val);
				}
			}
		}
		// now we check to see if the object being added belongs to any List
		if (val.getParentID() > 0) {
			for (GenericObject go : skills) {
				if (go instanceof com.hero.objects.List
						&& go.getID() == val.getParentID()) {
					((com.hero.objects.List) go).addObject(val);
					val.setParent((com.hero.objects.List) go);
				}
			}
		}
		// if wer get here, just add the danged object ;-)
		skills.add(val);
	}

	protected void addTalent(GenericObject val) {
		if (val instanceof com.hero.objects.List) {
			// first we set the parent of any objects in the perk list that
			// should be in this list
			for (GenericObject go : talents) {
				if (go.getParentID() == val.getID()) {
					((com.hero.objects.List) val).addObject(go);
					go.setParent((com.hero.objects.List) val);
				}
			}
		}
		// now we check to see if the object being added belongs to any List
		if (val.getParentID() > 0) {
			for (GenericObject go : talents) {
				if (go instanceof com.hero.objects.List
						&& go.getID() == val.getParentID()) {
					((com.hero.objects.List) go).addObject(val);
					val.setParent((com.hero.objects.List) go);
				}
			}
		}
		// if wer get here, just add the danged object ;-)
		talents.add(val);
	}

	/**
	 * Returns an Adder which is set to the values for the Age Disadvantage for
	 * this character.
	 * 
	 * @return An Adder set to the values for the Age Disadvantage for this
	 *         Character. May be null.
	 */
	public Adder getAgeObject() {
		if (HeroDesigner.getInstance().getActiveTemplate().getNCMObject() == null) {
			return null;
		}
		if (!ncmHasAge()) {
			return null;
		}
		if (ncmObject.getSelectedOption() == null) {
			return null;
		}
		Adder ret = null;
		ret = (Adder) ncmObject.getSelectedOption().clone();
		if (ret.getXMLID().equals(ncmObject.getXMLID())) {
			return null;
		}
		ret.setID(ncmObject.getSelectedOption().getID());
		ret.setBaseCost(ret.getBaseCost() - getBaseNCMObject().getBaseCost());
		ret.setPosition(agePosition);
		if (ageParentID > 0) {
			List parent = null;
			for (GenericObject o : disads) {
				if (o instanceof List && o.getID() == ageParentID) {
					parent = (List) o;
					break;
				}
			}
			if (parent != null) {
				ret.setParent(parent);
				if (!parent.getObjects().contains(ret)) {
					parent.getObjects().add(ret);
				}
			}
		}
		if (isNCMSelected()) {
			ret.setSelected(true);
		} else {
			ret.setSelected(false);
		}
		if (ret == null) {
			return null;
		}
		if (ret.getAlias().trim().length() == 0) {
			ret.setAlias(ret.getDisplay());
		}
		return ret;
	}

	public String getAlternateIdentities() {
		return alternateIdentities;
	}

	public String getAppearance() {
		return appearance;
	}

	public String getBackground() {
		return background;
	}

	/**
	 * @return Returns an Adder representing the Normal Characteristics Maxima
	 *         Disadvantage
	 */
	Adder getBaseNCMObject() {
		if (!ncmHasAge()) {
			return null;
		}
		Adder ret = null;
		for (Adder ad : ncmObject.getOptions()) {
			if (ad.getXMLID().equals(ncmObject.getXMLID())) {
				ret = ad;
			}
		}
		return ret;
	}

	public int getBasePoints() {
		return basePoints;
	}

	public String getCampaignName() {
		return campaignName;
	}

	public String getCampaignUse() {
		return campaignUse;
	}

	/**
	 * Returns the Characteristic object for the specified characteristic
	 * identifier
	 * 
	 * @param characteristic
	 *            The Characteristic object to return
	 * @return The Characteristic object corresponding to the specified int
	 *         value
	 * @see com.hero.util.Constants
	 */
	public Characteristic getCharacteristic(int characteristic) {
		Characteristic ret = null;
		for (int i = 0; i < characteristics.size(); i++) {
			Characteristic ch = (Characteristic) characteristics.get(i);
			if (ch.getType() == characteristic) {
				ret = ch;
			}
		}
		return ret;
	}

	/**
	 * @return a Vector of all Characteristics on this character.
	 */
	public ArrayList<GenericObject> getCharacteristics() {
		return characteristics;
	}

	/**
	 * @return the total points spent on Characteristics
	 */
	public double getCharacteristicsTotal() {
		if (characteristicCalcTime > 0 && characteristicCalcTime > lastEdit) {
			return characteristicTotal;
		}
		characteristicTotal = 0;
		for (GenericObject characteristic : characteristics) {
			characteristicTotal += characteristic.getRealCost();
		}
		characteristicCalcTime = System.currentTimeMillis();
		return characteristicTotal;
	}

	public String getCharacterName() {
		return characterName;
	}

	/**
	 * @return the available Disadvantage points.
	 */
	public int getDisadPoints() {
		return disadPoints;
	}

	/**
	 * @return a Vector of Disadvantage objects assigned to this character
	 */
	public ArrayList<GenericObject> getDisads() {
		return disads;
	}

	/**
	 * @return the number of Disadvantage points used.
	 */
	public int getDisadsUsed() {
		if (disadsUsedCalcTime > 0 && disadsUsedCalcTime > lastEdit) {
			return disadsUsed;
		}
		// check if NCM has been selected....if so, make sure it's in the
		// list...
		if (isNCMSelected() && getNCMObject() != null) {
			if (GenericObject.findObjectByID(getDisads(), "NCM") == null) {
				getDisads().add(getNCMObject());
				if (getAgeObject() != null) {
					getDisads().add(getAgeObject());
				}
			}
		} else {
			if (GenericObject.findObjectByID(getDisads(), "NCM") != null) {
				getDisads().remove(getNCMObject());
				if (getAgeObject() != null) {
					getDisads().remove(getAgeObject());
				}
			}
		}
		disadsUsed = 0;
		ArrayList<GenericObject> chars = getDisads();
		for (GenericObject o : chars) {
			disadsUsed += o.getRealCost();
		}
		disadsUsedCalcTime = System.currentTimeMillis();
		return disadsUsed;
	}

	/**
	 * @return a Vector of GenericObjects of all Equipment purchased for this
	 *         character.
	 */
	public ArrayList<GenericObject> getEquipment() {
		if (equipment == null) {
			equipment = new ArrayList<GenericObject>();
		}
		if (!isPrefab() && !getRules().isEquipmentAllowed()) {
			return new ArrayList<GenericObject>();
		}
		return equipment;
	}

	/**
	 * @return available Experience Points.
	 */
	public int getExperience() {
		return experience;
	}

	/**
	 * @return The absolute file path to the export template specified on this
	 *         character. May be null.
	 */
	public String getExportTemplate() {
		if (exportTemplate == null) {
			return null;
		}
		File test = new File(exportTemplate);
		if (test.exists()) {
			return exportTemplate;
		}
		return null;
	}

	public String getEyeColor() {
		return eyeColor;
	}

	/**
	 * @return an Adder representing the Normal Characteristics Maxima
	 *         Disadvantage.
	 */
	public Adder getFullNCMObject() {
		if (HeroDesigner.getInstance().getActiveTemplate().getNCMObject() == null) {
			return null;
		}
		return ncmObject;
	}

	public String getGenre() {
		return genre;
	}

	public String getGm() {
		return gm;
	}

	public String getHairColor() {
		return hairColor;
	}

	/**
	 * @return the value for the height of this character. Will be returned in
	 *         the proper units, as specified under the AppPrefs
	 * @see com.hero.AppPrefs
	 */
	public int getHeight() {
		if (!HeroDesigner.getInstance().getPrefs().isMetric()) {
			return (int) Rounder.roundHalfUp(height);
		} else {
			int ret = (int) Rounder.roundHalfUp(height * 2.54); // cm
			// ->
			// in.
			return ret;
		}
	}

	public byte[] getImageData() {
		return imageData;
	}

	public String getImageFileName() {
		if (imageFileName == null) {
			imageFileName = "";
		}
		return imageFileName;
	}

	/**
	 * @return The index of the last tab selected on this character. This value
	 *         is not persisted.
	 */
	public int getLastTabIndex() {
		if (lastTab < 0 || lastTab > 9) {
			lastTab = 0;
		}
		return lastTab;
	}

	/**
	 * @return A Vector of GenericObjects representing all purchases on the
	 *         Martial Arts tab
	 */
	public ArrayList<GenericObject> getManeuvers() {
		return maneuvers;
	}

	public Adder getNCMObject() {
		if (HeroDesigner.getInstance().getActiveTemplate().getNCMObject() == null) {
			return null;
		}
		if (ncmObject == null) {
			return null;
		}
		Adder ret = null;
		ret = (Adder) ncmObject.clone();
		ret.setID(ncmObject.getID());
		if (isNCMSelected()) {
			ret.setSelected(true);
		} else if (ncmObject != null) {
			ret.setSelected(false);
		}
		if (ncmHasAge()
				&& (!ret.getSelectedOption().getXMLID().equals(ret.getXMLID()) || ret
						.getSelectedOption().getAlias().equals(
								ret.getSelectedOption().getDisplay()))) {
			ret.setOptions(new ArrayList<Adder>());
			ret.setSelectedOption(null);
			ret.setBaseCost(getBaseNCMObject().getBaseCost());
			ret.setPosition(ncmPosition);
		}
		if (ret.getAlias().trim().length() == 0) {
			ret.setAlias(ret.getDisplay());
		}
		if (ncmParentID > 0) {
			List parent = null;
			for (GenericObject o : disads) {
				if (o instanceof List && o.getID() == ncmParentID) {
					parent = (List) o;
					break;
				}
			}
			if (parent != null) {
				ret.setParent(parent);
				if (!parent.getObjects().contains(ret)) {
					parent.getObjects().add(ret);
				}
			}
		}

		return ret;
	}

	public String getNotes1() {
		if (notes1 == null) {
			notes1 = "";
		}
		return notes1;
	}

	public String getNotes2() {
		if (notes2 == null) {
			notes2 = "";
		}
		return notes2;
	}

	public String getNotes3() {
		if (notes3 == null) {
			notes3 = "";
		}
		return notes3;
	}

	public String getNotes4() {
		if (notes4 == null) {
			notes4 = "";
		}
		return notes4;
	}

	public String getNotes5() {
		if (notes5 == null) {
			notes5 = "";
		}
		return notes5;
	}

	/**
	 * @return The "path" to the template that this character was saved with.
	 *         This is a good way to get back to the original template after
	 *         template changes have taken place.
	 */
	public String getOriginalTemplateID() {
		return originalTemplateID;
	}

	/**
	 * @return A Vector of Perk objects representing the purchases on the Perks
	 *         tab.
	 */
	public ArrayList<GenericObject> getPerks() {
		return perks;
	}

	public String getPersonality() {
		return personality;
	}

	public String getPlayerName() {
		return playerName;
	}

	/**
	 * @return A Vector of GenericObjects representing the character's purchases
	 *         on the Powers tab.
	 */
	public ArrayList<GenericObject> getPowers() {
		return powers;
	}

	public String getQuote() {
		return quote;
	}

	/**
	 * @return The Rules object that has been assigned to this character.
	 */
	public Rules getRules() {
		if (rules == null) {
			rules = new Rules();
			if (HeroDesigner.getInstance().getPrefs().getLastRule().trim()
					.length() <= 0) {
				rules.useDefault();
			} else {
				rules.loadRulesDefinition(HeroDesigner.getInstance().getPrefs()
						.getLastRule());
			}
		}
		return rules;
	}

	/**
	 * @return The File that the character should be saved to.
	 */
	public File getSaveFile() {
		return saveFile;
	}

	/**
	 * @return a jdom Element populated with the save XML for this character.
	 */
	public Element getSaveXML() {
		Element root = new Element("CHARACTER");
		root.setAttribute("version", "6.0");
		Element basic = new Element("BASIC_CONFIGURATION");
		basic.setAttribute("BASE_POINTS", "" + getBasePoints());
		basic.setAttribute("DISAD_POINTS", "" + getDisadPoints());
		basic.setAttribute("EXPERIENCE", "" + getExperience());
		if (isNCMSelected() && ncmObject != null) {
			Element el = ncmObject.getSaveXML();
			el.setName("NCM");
			basic.addContent(el);
		}

		if (exportTemplate != null) {
			basic.setAttribute("EXPORT_TEMPLATE", exportTemplate);
		}
		root.addContent(basic);
		Element info = new Element("CHARACTER_INFO");
		info.setAttribute("CHARACTER_NAME", getCharacterName());
		info.setAttribute("ALTERNATE_IDENTITIES", getAlternateIdentities());
		info.setAttribute("PLAYER_NAME", getPlayerName());
		info.setAttribute("HEIGHT", "" + height);
		info.setAttribute("WEIGHT", "" + weight);
		info.setAttribute("HAIR_COLOR", getHairColor());
		info.setAttribute("EYE_COLOR", getEyeColor());
		info.setAttribute("CAMPAIGN_NAME", getCampaignName());
		info.setAttribute("GENRE", getGenre());
		info.setAttribute("GM", getGm());
		Element bg = new Element("BACKGROUND");
		bg.addContent(getBackground());
		info.addContent(bg);
		Element ps = new Element("PERSONALITY");
		ps.addContent(getPersonality());
		info.addContent(ps);
		Element qt = new Element("QUOTE");
		qt.addContent(getQuote());
		info.addContent(qt);
		Element tc = new Element("TACTICS");
		tc.addContent(getTactics());
		info.addContent(tc);
		Element cu = new Element("CAMPAIGN_USE");
		cu.addContent(getCampaignUse());
		info.addContent(cu);
		Element ap = new Element("APPEARANCE");
		ap.addContent(getAppearance());
		info.addContent(ap);
		Element n1 = new Element("NOTES1");
		n1.addContent(getNotes1());
		info.addContent(n1);
		Element n2 = new Element("NOTES2");
		n2.addContent(getNotes2());
		info.addContent(n2);
		Element n3 = new Element("NOTES3");
		n3.addContent(getNotes3());
		info.addContent(n3);
		Element n4 = new Element("NOTES4");
		n4.addContent(getNotes4());
		info.addContent(n4);
		Element n5 = new Element("NOTES5");
		n5.addContent(getNotes5());
		info.addContent(n5);
		root.addContent(info);
		Element chars = new Element("CHARACTERISTICS");
		for (GenericObject ch : characteristics) {
			chars.addContent(ch.getSaveXML());
		}
		root.addContent(chars);
		Element sks = new Element("SKILLS");
		for (GenericObject obj : skills) {
			sks.addContent(obj.getSaveXML());
		}
		root.addContent(sks);
		Element pks = new Element("PERKS");
		for (GenericObject obj : perks) {
			pks.addContent(obj.getSaveXML());
		}
		root.addContent(pks);
		Element tlt = new Element("TALENTS");
		for (GenericObject obj : talents) {
			tlt.addContent(obj.getSaveXML());
		}
		root.addContent(tlt);
		Element mas = new Element("MARTIALARTS");
		for (GenericObject obj : maneuvers) {
			mas.addContent(obj.getSaveXML());
		}
		root.addContent(mas);
		Element pws = new Element("POWERS");
		for (GenericObject obj : powers) {
			pws.addContent(obj.getSaveXML());
		}
		root.addContent(pws);
		Element dsd = new Element("DISADVANTAGES");
		for (GenericObject obj : disads) {
			dsd.addContent(obj.getSaveXML());
		}
		root.addContent(dsd);
		if (equipment != null) {
			Element eqp = new Element("EQUIPMENT");
			for (GenericObject obj : equipment) {
				eqp.addContent(obj.getSaveXML());
			}
			root.addContent(eqp);
		}

		if (!isPrefab()) {
			// add in imaqe data (base64) as CDATA element
			if (imageData != null && getImageFileName() != null
					&& imageData.length > 0
					&& getImageFileName().trim().length() > 0) {
				try {
					String encoded = Base64.encodeBytes(imageData);
					Element image = new Element("IMAGE");
					CDATA content = new CDATA(encoded);
					image.setContent(content);
					image.setAttribute("FileName", getImageFileName());
					image.setAttribute("FilePath", (imageFilePath == null ? ""
							: imageFilePath));
					root.addContent(image);
				} catch (Exception exp) {
					exp.printStackTrace();
				}
			}

			// add in the rules element
			if (getRules().isDefault()) {
				basic.setAttribute("RULES", "Default");
			} else {
				Element rules = getRules().getRulesXML();
				root.addContent(rules);
			}
			// add in the template
			if (HeroDesigner.getInstance().getActiveTemplate().getId()
					.startsWith("builtIn")) {
				root.setAttribute("TEMPLATE", HeroDesigner.getInstance()
						.getActiveTemplate().getId());
			} else {
				Element template = HeroDesigner.getInstance()
						.getActiveTemplate().getTemplateXML();
				if (template.getAttributeValue("originalPath") == null) {
					template.setAttribute("originalPath", HeroDesigner
							.getInstance().getActiveTemplate().getId());
				}
				template.removeChildren("CHARACTER");
				template.removeChildren("PREFAB");
				template.removeChildren("RULES");
				template.detach();
				root.addContent(template);
			}
			for (Prefab prefab : prefabs) {
				Element pr = prefab.getSaveXML();
				pr.removeChildren("TEMPLATE");
				pr.removeChildren("IMAGE");
				pr.removeChildren("RULES");
				basic = pr.getChild("BASIC_CONFIGURATION");
				basic.setAttribute("RULES", "");
				basic.setAttribute("TEMPLATE", "");
				root.addContent(pr);
			}
		} else { // it's a prefab...
			root.setAttribute("TEMPLATE", HeroDesigner.getInstance()
					.getActiveTemplate().getId());
		}
		return root;
	}

	/**
	 * @return a Vector of Skill objects corresponding to the purchases on the
	 *         Skills tab.
	 */
	public ArrayList<GenericObject> getSkills() {
		if (skills == null) {
			skills = new ArrayList<GenericObject>();
		}
		return skills;
	}

	/**
	 * @return the total points spent on this character.
	 */
	public double getSpentTotal() {
		if (spentTotalCalcTime > 0 && spentTotalCalcTime > lastEdit
				&& spentTotalCalcTime > HeroDesigner.lastEdit) {
			return total;
		}
		total = 0;
		ArrayList<GenericObject> chars = getCharacteristics();
		for (GenericObject o : chars) {
			total += o.getRealCost();
		}
		chars = getSkills();
		for (GenericObject o : chars) {
			total += o.getRealCost();
		}
		chars = getPerks();
		for (GenericObject o : chars) {
			total += o.getRealCost();
		}
		chars = getTalents();
		for (GenericObject o : chars) {
			total += o.getRealCost();
		}
		chars = getManeuvers();
		for (GenericObject o : chars) {
			total += o.getRealCost();
		}
		chars = getPowers();
		for (GenericObject o : chars) {
			total += o.getRealCost();
		}
		spentTotalCalcTime = System.currentTimeMillis();
		return total;
	}

	public String getTactics() {
		return tactics;
	}

	/**
	 * @return a Vector of Talent objects corresponding to the purchases made on
	 *         the Talents tab.
	 */
	public ArrayList<GenericObject> getTalents() {
		return talents;
	}

	/**
	 * @return The "path" to the template specified for this character (may be
	 *         built-in)
	 */
	public String getTemplatePath() {
		return templatePath;
	}

	/**
	 * @return The weight of this character in either metric or English units
	 *         (depending on the settings in the Application Preferences).
	 * @see com.hero.AppPrefs
	 */
	public int getWeight() {
		if (!HeroDesigner.getInstance().getPrefs().isMetric()) {
			int ret = (int) Rounder.roundHalfUp(weight);
			return ret;
		} else {
			int ret = (int) Rounder.roundHalfUp(weight * 453.5924) / 1000;
			// lbs -> kg
			return ret;
		}
	}

	protected void init() {
		Template template = HeroDesigner.getActiveTemplate();
		characteristics = new ArrayList<GenericObject>();
		skills = new ArrayList<GenericObject>();
		perks = new ArrayList<GenericObject>();
		talents = new ArrayList<GenericObject>();
		maneuvers = new ArrayList<GenericObject>();
		powers = new ArrayList<GenericObject>();
		disads = new ArrayList<GenericObject>();
		equipment = new ArrayList<GenericObject>();
		prefabs = new ArrayList<Prefab>();
		// Basic template info:
		experience = 0;
		if (template.getNCMObject() != null) {
			ncmObject = (Adder) template.getNCMObject().clone();
		} else {
			ncmObject = null;
		}
		// Basic Character info:
		characterName = "";
		alternateIdentities = "";
		playerName = "";
		height = 78.74015748031496;
		weight = 220.4622476037958;
		hairColor = "Brown";
		eyeColor = "Brown";
		campaignName = "";
		genre = "";
		gm = "";
		background = "";
		personality = "";
		quote = "";
		tactics = "";
		campaignUse = "";
		appearance = "";
		exportTemplate = null;
		ArrayList<GenericObject> chars = template.getCharacteristics();
		for (GenericObject ch : chars) {
			ch = ch.clone();
			ch.setID(System.currentTimeMillis() + (int) (Math.random() * 1000));
			ch.setPower(false);
			characteristics.add(ch);
		}
	}

	protected void initFromSave() {
		try {
			if (openFile != null && openFile.exists() && openFile.canRead()) {
				SAXBuilder builder = new SAXBuilder(false);
				Document doc = builder.build(openFile);
				initFromSave(doc);
			} else {
				initFromSave(null);
			}
		} catch (Exception e) {
			e.printStackTrace();
			if (!HeroDesigner.headless) {
				JOptionPane
						.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred loading the character.\nData loss may occur if you save.\n\nPlease shut down Hero Designer and restart.\n\nError Message: "
										+ e.getMessage(),
								"Error loading character",
								JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	protected void initFromSave(Document pRoot) {
		init();
		if (!prefabLoad) {
			HeroDesigner.activeHero = this; // necessary to load in
		}
		// abilities....
		Template template = HeroDesigner.getActiveTemplate();
		try {
			Element root = new Element("EMPTY");
			Element mainRoot = new Element("EMPTY");
			if (pRoot != null) {
				try {
					Document doc = pRoot;
					root = doc.getRootElement();
					mainRoot = root;
					if (openFile != null
							&& openFile.getName().trim().toUpperCase()
									.endsWith(".HDT")) {
						root = root.getChild("CHARACTER");
					}
					if (root == null) {
						// old (v2) style template
						return;
					}
					if (prefabLoad) {
						String check = XMLUtility.getValue(root, "NAME");
						if (check != null && check.trim().length() > 0) {
							characterName = check;
						}
					}
					if (!isPrefab()) {
						Element r = root.getChild("RULES");
						boolean rulesHandled = false;
						if (r != null) {
							String p = XMLUtility.getValue(r, "path");
							File f = new File(p);
							if (!f.exists()) {
								f = new File(HeroDesigner.getInstance()
										.getPrefs().getRulesDir()
										+ File.separator + f.getName());
							}
							if (f.exists()) {
								if (f.lastModified() > saveFile.lastModified()) {
									int input = JOptionPane.YES_OPTION;
									if (!HeroDesigner.headless) {
										JOptionPane
												.showConfirmDialog(
														HeroDesigner
																.getAppFrame(),
														"There appears to be a newer copy of the campaign rules (hdr) file used on this character.  Would you like to load the new rules file?",
														"New Campaign Rules Found for "
																+ saveFile
																		.getName(),
														JOptionPane.YES_NO_OPTION);
									}
									if (input == JOptionPane.YES_OPTION) {
										Rules temp = new Rules();
										temp.loadRulesDefinition(f
												.getAbsolutePath());
										rules = temp;
										rulesHandled = true;
									}
								}
							}
							if (!rulesHandled) {
								Rules temp = new Rules();
								if (openFile != null) {
									temp.setCurrentSet(openFile.getName());
								} else {
									temp.setCurrentSet("HD Online Character");
								}
								temp.restoreRules(r);
								rules = temp;
								rulesHandled = true;
							}
						}
						if (!rulesHandled) {
							if (openFile != null
									&& openFile.getName().trim().toUpperCase()
											.endsWith(".HDT")) {
								if (root == null) {
									Element ruleRoot = mainRoot
											.getChild("RULES");
									rules = new Rules();
									if (ruleRoot != null) {
										rules.setCurrentSet(openFile.getName());
										rules.restoreRules(ruleRoot);
									} else if (HeroDesigner.getInstance()
											.getPrefs().getLastRule().trim()
											.length() > 0) {
										rules.useDefault();
									} else {
										rules.loadRulesDefinition(HeroDesigner
												.getInstance().getPrefs()
												.getLastRule());
									}
									return;
								}
							}
							Element basic = root
									.getChild("BASIC_CONFIGURATION");
							if (basic != null) {
								if (openFile != null
										&& openFile.getName().trim()
												.toUpperCase().endsWith(".HDT")) {
									Element ruleRoot = mainRoot
											.getChild("RULES");
									rules = new Rules();
									if (ruleRoot != null) {
										rules.setCurrentSet(openFile.getName());
										rules.restoreRules(ruleRoot);
									} else if (HeroDesigner.getInstance()
											.getPrefs().getLastRule().trim()
											.length() == 0) {
										rules.useDefault();
									} else {
										rules.loadRulesDefinition(HeroDesigner
												.getInstance().getPrefs()
												.getLastRule());
									}
								} else {

									String ruleCheck = XMLUtility.getValue(
											basic, "RULES");
									if (rules == null
											&& (ruleCheck == null
													|| ruleCheck.trim()
															.length() == 0 || ruleCheck
													.trim().equalsIgnoreCase(
															"Default"))) {
										rules = new Rules();
										// if
										// (HeroDesigner.getInstance().getPrefs()
										// .getLastRule().trim().length() == 0)
										rules.useDefault();
										// else
										// rules.loadRulesDefinition(HeroDesigner
										// .getInstance().getPrefs()
										// .getLastRule());
									} else if (rules == null) {
										File file = new File(ruleCheck);
										if (file.exists()) {
											rules = new Rules();
											rules
													.loadRulesDefinition(ruleCheck);
										} else {
											if (HeroDesigner.getInstance()
													.getPrefs()
													.getDefaultRulesDir() != null) {
												file = new File(HeroDesigner
														.getInstance()
														.getPrefs()
														.getDefaultRulesDir()
														+ File.separator
														+ file.getName());
											}
											if (file.exists()) {
												rules = new Rules();
												rules
														.loadRulesDefinition(ruleCheck);
											} else {
												rules = new Rules();
												rules.useDefault();
											}
										}
									}
								}
							}
						}
					}
				} catch (Exception exp) {
					exp.printStackTrace();
					if (!HeroDesigner.headless) {
						JOptionPane
								.showMessageDialog(
										HeroDesigner.getAppFrame(),
										"An error occurred loading the template.\nData loss may occur if you save.\n\nPlease shut down Hero Designer and restart.\n\nError Message: "
												+ exp.getMessage(),
										"Error loading template",
										JOptionPane.ERROR_MESSAGE);
					}
				}
			}

			restoreCharacter(root);
			if (!isPrefab()) {
				Element img = root.getChild("IMAGE");
				if (img != null && img.getContent(0) instanceof CDATA) {
					CDATA cd = (CDATA) img.getContent(0);
					String encoded = cd.getText();
					boolean refreshed = false;
					imageFileName = img.getAttributeValue("FileName");
					imageFilePath = img.getAttributeValue("FilePath");
					if (imageFilePath != null
							&& imageFilePath.trim().length() > 0) {
						File f = new File(imageFilePath);
						if (!f.exists()) {
							f = new File(HeroDesigner.getInstance().getPrefs()
									.getImageDir()
									+ File.separator + f.getName());
						}
						if (f.exists()) {
							if (f.lastModified() > saveFile.lastModified()) {
								int input = JOptionPane.YES_OPTION;
								if (!HeroDesigner.headless) {
									input = JOptionPane
											.showConfirmDialog(
													HeroDesigner.getAppFrame(),
													"There appears to be a newer copy of the image file used on this character.  Would you like to load the new image file?",
													"New Image Found for "
															+ saveFile
																	.getName(),
													JOptionPane.YES_NO_OPTION);
								}
								if (input == JOptionPane.YES_OPTION) {
									try {
										imageData = new byte[(int) f.length()];
										FileInputStream fis = new FileInputStream(
												f);
										fis.read(imageData);
										fis.close();
										refreshed = true;
									} catch (Exception exp) {
									}
								}
							}
						}
					}
					if (!refreshed) {
						imageData = Base64.decode(encoded);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			if (!HeroDesigner.headless) {
				JOptionPane
						.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred loading the character.\nData loss may occur if you save.\n\nPlease shut down Hero Designer and restart.\n\nError Message: "
										+ e.getMessage(),
								"Error loading character",
								JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	protected void restoreCharacter(Element root) {
		Template template = HeroDesigner.getActiveTemplate();
		try {
			if (!isPrefab()) {
				if (template.getNCMObject() != null) {
					ncmObject = (Adder) template.getNCMObject().clone();
				} else {
					ncmObject = null;
				}
				// restore basic configuration
				Element basic = root.getChild("BASIC_CONFIGURATION");
				if (basic != null) {
					Element ncm = basic.getChild("NCM");
					if (ncmObject != null && ncm != null) {
						ncmObject.restoreFromSave(ncm);
						ncmSelected = true;
					} else {
						ncmSelected = false;
					}
					String check = XMLUtility.getValue(basic, "BASE_POINTS");
					if (check != null && check.trim().length() > 0) {
						try {
							basePoints = Integer.parseInt(check);
						} catch (Exception exp) {
						}
					}
					check = XMLUtility.getValue(basic, "EXPORT_TEMPLATE");
					if (check != null && check.trim().length() > 0) {
						File f = new File(check);
						if (f.exists()) {
							exportTemplate = f.getAbsolutePath();
						} else if (HeroDesigner.getInstance().getPrefs()
								.getDefaultExportDir() != null) {
							f = new File(HeroDesigner.getInstance().getPrefs()
									.getDefaultExportDir()
									+ File.separator + f.getName());
							if (f.exists()) {
								exportTemplate = f.getAbsolutePath();
							}
						}
					}
					check = XMLUtility.getValue(basic, "DISAD_POINTS");
					if (check != null && check.trim().length() > 0) {
						try {
							disadPoints = Integer.parseInt(check);
						} catch (Exception exp) {
						}
					}
					check = XMLUtility.getValue(basic, "EXPERIENCE");
					if (check != null && check.trim().length() > 0) {
						try {
							experience = Integer.parseInt(check);
						} catch (Exception exp) {
						}
					}
				}
			}
			// restore character info.
			Element info = root.getChild("CHARACTER_INFO");
			if (info != null) {
				String check = XMLUtility.getValue(info, "CHARACTER_NAME");
				if (check != null && check.trim().length() > 0) {
					characterName = check;
				}
				check = XMLUtility.getValue(info, "PLAYER_NAME");
				if (check != null && check.trim().length() > 0) {
					playerName = check;
				}
				if (!isPrefab()) {
					check = XMLUtility.getValue(info, "ALTERNATE_IDENTITIES");
					if (check != null && check.trim().length() > 0) {
						alternateIdentities = check;
					}
					check = XMLUtility.getValue(info, "IMAGE_PATH");
					if (check != null && check.trim().length() > 0) {
						File file = new File(check);
						if (file.exists() && file.canRead()) {
							imageData = new byte[(int) file.length()];
							FileInputStream fis = new FileInputStream(file);
							fis.read(imageData);
							imageFileName = file.getName();
							imageFilePath = file.getAbsolutePath();
						} else {
							file = new File(HeroDesigner.getInstance()
									.getPrefs().getImageDir()
									+ File.separator + file.getName());
							if (file.exists() && file.canRead()) {
								imageData = new byte[(int) file.length()];
								FileInputStream fis = new FileInputStream(file);
								fis.read(imageData);
								imageFileName = file.getName();
								imageFilePath = file.getAbsolutePath();
							}
						}
					}
					check = XMLUtility.getValue(info, "HEIGHT");
					if (check != null && check.trim().length() > 0) {
						try {
							height = Double.parseDouble(check);
						} catch (Exception exp) {
						}
					}
					check = XMLUtility.getValue(info, "WEIGHT");
					if (check != null && check.trim().length() > 0) {
						try {
							weight = Double.parseDouble(check);
						} catch (Exception exp) {
						}
					}
					check = XMLUtility.getValue(info, "HAIR_COLOR");
					if (check != null && check.trim().length() > 0) {
						hairColor = check;
					}
					check = XMLUtility.getValue(info, "EYE_COLOR");
					if (check != null && check.trim().length() > 0) {
						eyeColor = check;
					}
					check = XMLUtility.getValue(info, "CAMPAIGN_NAME");
					if (check != null && check.trim().length() > 0) {
						campaignName = check;
					}
					check = XMLUtility.getValue(info, "GENRE");
					if (check != null && check.trim().length() > 0) {
						genre = check;
					}
					check = XMLUtility.getValue(info, "GM");
					if (check != null && check.trim().length() > 0) {
						gm = check;
					}
					check = XMLUtility.getValue(info, "BACKGROUND");
					if (check != null && check.trim().length() > 0) {
						background = check;
					}
					check = XMLUtility.getValue(info, "PERSONALITY");
					if (check != null && check.trim().length() > 0) {
						personality = check;
					}
					check = XMLUtility.getValue(info, "QUOTE");
					if (check != null && check.trim().length() > 0) {
						quote = check;
					}
					check = XMLUtility.getValue(info, "TACTICS");
					if (check != null && check.trim().length() > 0) {
						tactics = check;
					}
					check = XMLUtility.getValue(info, "CAMPAIGN_USE");
					if (check != null && check.trim().length() > 0) {
						campaignUse = check;
					}
					check = XMLUtility.getValue(info, "APPEARANCE");
					if (check != null && check.trim().length() > 0) {
						appearance = check;
					}
					check = XMLUtility.getValue(info, "NOTES1");
					if (check != null && check.trim().length() > 0) {
						notes1 = check;
					}
					check = XMLUtility.getValue(info, "NOTES2");
					if (check != null && check.trim().length() > 0) {
						notes2 = check;
					}
					check = XMLUtility.getValue(info, "NOTES3");
					if (check != null && check.trim().length() > 0) {
						notes3 = check;
					}
					check = XMLUtility.getValue(info, "NOTES4");
					if (check != null && check.trim().length() > 0) {
						notes4 = check;
					}
					check = XMLUtility.getValue(info, "NOTES5");
					if (check != null && check.trim().length() > 0) {
						notes5 = check;
					}
				}
			}
			Hashtable<String, GenericObject> skillHash = new Hashtable<String, GenericObject>();
			for (GenericObject go : template.getSkills()) {
				if (go instanceof Skill) {
					skillHash.put(go.getXMLID(), go);
				} else if (go instanceof List) {
					List l = (List) go;
					for (GenericObject go2 : l.getObjects()) {
						if (go2 instanceof Skill) {
							skillHash.put(go2.getXMLID(), go2);
						}
					}
				}
			}
			Hashtable<String, GenericObject> perkHash = new Hashtable<String, GenericObject>();
			for (GenericObject go : template.getPerks()) {
				if (go instanceof Perk) {
					perkHash.put(go.getXMLID(), go);
				} else if (go instanceof List) {
					List l = (List) go;
					for (GenericObject go2 : l.getObjects()) {
						if (go2 instanceof Perk) {
							perkHash.put(go2.getXMLID(), go2);
						}
					}
				}
			}
			Hashtable<String, GenericObject> talentHash = new Hashtable<String, GenericObject>();
			for (GenericObject go : template.getTalents()) {
				if (go instanceof Talent || go instanceof MageSight) {
					talentHash.put(go.getXMLID(), go);
				} else if (go instanceof List) {
					List l = (List) go;
					for (GenericObject go2 : l.getObjects()) {
						if (go2 instanceof Talent || go2 instanceof MageSight) {
							talentHash.put(go2.getXMLID(), go2);
						}
					}
				}
			}
			Hashtable<String, GenericObject> maneuverHash = new Hashtable<String, GenericObject>();
			for (GenericObject go : template.getMartialArts()) {
				if (go instanceof Maneuver) {
					maneuverHash.put(go.getDisplay(), go);
				} else if (go instanceof com.hero.objects.List) {
					com.hero.objects.List li = (com.hero.objects.List) go;
					ArrayList<GenericObject> ms = li.getObjects();
					for (GenericObject man : ms) {
						maneuverHash.put(man.getDisplay(), man);
					}
				}
			}
			Hashtable<String, GenericObject> powerHash = new Hashtable<String, GenericObject>();
			for (GenericObject go : template.getPowers()) {
				if (go instanceof Power) {
					powerHash.put(go.getXMLID(), go);
				} else if (go instanceof List) {
					List l = (List) go;
					for (GenericObject go2 : l.getObjects()) {
						if (go2 instanceof Power) {
							powerHash.put(go2.getXMLID(), go2);
						} else if (go2 instanceof List) {
							List l2 = (List) go2;
							for (GenericObject go3 : l2.getObjects()) {
								if (go3 instanceof Power) {
									powerHash.put(go3.getXMLID(), go3);
								}
							}
						}
					}
				}
			}
			Hashtable<String, GenericObject> disadHash = new Hashtable<String, GenericObject>();
			for (GenericObject go : template.getDisads()) {
				if (go instanceof Disadvantage) {
					disadHash.put(go.getXMLID(), go);
				} else if (go instanceof List) {
					List l = (List) go;
					for (GenericObject go2 : l.getObjects()) {
						if (go2 instanceof Disadvantage) {
							disadHash.put(go2.getXMLID(), go);
						}
					}
				}
			}
			// restore Characteristics
			Element chs = root.getChild("CHARACTERISTICS");
			if (chs != null) {
				for (GenericObject ch : characteristics) {
					Element chk = chs.getChild(ch.getXMLID());
					if (chk != null) {
						ch.restoreFromSave(chk);
						ch.setPower(false);
					}
				}
			}
			// restore Skills
			Element skillElement = root.getChild("SKILLS");
			// restore Skill Lists
			if (skillElement != null) {
				java.util.List list = skillElement.getChildren("LIST");
				Iterator iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.List li = new com.hero.objects.List(l);
					li.restoreFromSave(l);
					addSkill(li);
				}
				// restore Skill Enhancers
				for (GenericObject en : HeroDesigner.getActiveTemplate()
						.getSkillEnhancers()) {
					Element e = skillElement.getChild(en.getXMLID());
					if (e != null) {
						en = en.clone();
						en.restoreFromSave(e);
						addSkill(en);
					}
				}
				// now restore the actual Skills
				java.util.List skillList = skillElement.getChildren("SKILL");
				iterator = skillList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject skill = skillHash.get(xmlID);
						if (skill != null) {
							skill = skill.clone();
							skill.restoreFromSave(sk);
							addSkill(skill);
						}
					}
				}
				Collections.sort(skills, new Comparator() {
					public int compare(Object o1, Object o2) {
						if (o1 instanceof GenericObject
								&& o2 instanceof GenericObject) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							return go1.getPosition() - go2.getPosition();
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}

					public boolean equals(Object o) {
						return false;
					}
				});
			}
			// restore Perks
			Element perkElement = root.getChild("PERKS");
			// restore Perk Lists
			if (perkElement != null) {
				java.util.List list = perkElement.getChildren("LIST");
				Iterator iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.List li = new com.hero.objects.List(l);
					li.restoreFromSave(l);
					addPerk(li);
				}
				// restore Perk Enhancers
				for (GenericObject en : HeroDesigner.getActiveTemplate()
						.getSkillEnhancers()) {
					Element e = perkElement.getChild(en.getXMLID());
					if (e != null) {
						en = en.clone();
						en.restoreFromSave(e);
						addPerk(en);
					}
				}
				// now restore the actual Perks
				java.util.List perkList = perkElement.getChildren("PERK");
				iterator = perkList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject perk = perkHash.get(xmlID);
						if (perk != null) {
							perk = perk.clone();
							perk.restoreFromSave(sk);
							addPerk(perk);
						}
					}
				}
				Collections.sort(perks, new Comparator() {
					public int compare(Object o1, Object o2) {
						if (o1 instanceof GenericObject
								&& o2 instanceof GenericObject) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							return go1.getPosition() - go2.getPosition();
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}

					public boolean equals(Object o) {
						return false;
					}
				});
			}
			// restore Talents
			Element talentElement = root.getChild("TALENTS");
			// restore Talent Lists
			if (talentElement != null) {
				java.util.List list = talentElement.getChildren("LIST");
				Iterator iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.List li = new com.hero.objects.List(l);
					li.restoreFromSave(l);
					addTalent(li);
				}
				// now restore the actual Talents
				java.util.List talentList = talentElement.getChildren("TALENT");
				iterator = talentList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						if (talentHash.get(xmlID) != null) {
							GenericObject talent = talentHash.get(xmlID);
							if (talent != null) {
								talent = talent.clone();
								talent.restoreFromSave(sk);
								addTalent(talent);
							}
						}
					}
				}
				Collections.sort(talents, new Comparator() {
					public int compare(Object o1, Object o2) {
						if (o1 instanceof GenericObject
								&& o2 instanceof GenericObject) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							return go1.getPosition() - go2.getPosition();
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}

					public boolean equals(Object o) {
						return false;
					}
				});
			}
			// restore Maneuvers
			Element maneuverElement = root.getChild("MARTIALARTS");
			// restore Maneuver Lists
			if (maneuverElement != null) {
				java.util.List list = maneuverElement.getChildren("LIST");
				Iterator iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.List li = new com.hero.objects.List(l);
					li.restoreFromSave(l);
					addManeuver(li);
				}
				list = maneuverElement.getChildren("EXTRADC");
				GenericObject xdc = null;
				for (GenericObject o : HeroDesigner.getActiveTemplate()
						.getMartialArts()) {
					if (o.getXMLID().equals("EXTRADC")) {
						xdc = o;
						break;
					}
				}
				iterator = list.iterator();
				if (xdc != null) {
					while (iterator.hasNext()) {
						Element lm = (Element) iterator.next();
						GenericObject xdc2 = xdc.clone();
						xdc2.restoreFromSave(lm);
						addManeuver(xdc2);
					}
				}
				list = maneuverElement.getChildren("RANGEDDC");
				GenericObject rdc = null;
				for (GenericObject o : HeroDesigner.getActiveTemplate()
						.getMartialArts()) {
					if (o.getXMLID().equals("RANGEDDC")) {
						rdc = o;
						break;
					}
				}
				iterator = list.iterator();
				if (rdc != null) {
					while (iterator.hasNext()) {
						Element lm = (Element) iterator.next();
						GenericObject rdc2 = rdc.clone();
						rdc2.restoreFromSave(lm);
						addManeuver(rdc2);
					}
				}
				GenericObject ele = null;
				for (GenericObject o : HeroDesigner.getActiveTemplate()
						.getMartialArts()) {
					if (o.getXMLID().equals("WEAPON_ELEMENT")) {
						ele = o;
						break;
					}
				}
				list = maneuverElement.getChildren("WEAPON_ELEMENT");
				iterator = list.iterator();
				if (ele != null) {
					while (iterator.hasNext()) {
						Element lm = (Element) iterator.next();
						GenericObject ele2 = ele.clone();
						ele2.restoreFromSave(lm);
						addManeuver(ele2);
					}
				}
				// now restore the actual Maneuvers
				java.util.List maneuverList = maneuverElement
						.getChildren("MANEUVER");
				iterator = maneuverList.iterator();
				OUTER: while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String display = XMLUtility.getValue(sk, "DISPLAY");
						// else { //it's a maneuver....
						for (GenericObject o : template.getMartialArts()) {
							if (o instanceof Maneuver) {
								if (o.getDisplay().equals(display)) {
									o = o.clone();
									o.restoreFromSave(sk);
									addManeuver(o);
									continue OUTER;
								}
							} else if (o instanceof com.hero.objects.List) {
								com.hero.objects.List li = (com.hero.objects.List) o;
								ArrayList<GenericObject> ms = li.getObjects();
								for (GenericObject o2 : ms) {
									if (o2.getDisplay().equals(display)) {
										o2 = o2.clone();
										o2.restoreFromSave(sk);
										addManeuver(o2);
										continue OUTER;
									}
								}
							}
						}
						// if we get here,then it's a custom maneuver...
						Maneuver custom = new Maneuver(sk);
						custom.restoreFromSave(sk);
						custom.setCustom(true);
						addManeuver(custom);
					}
					// }
				}
				Collections.sort(maneuvers, new Comparator() {
					public int compare(Object o1, Object o2) {
						if (o1 instanceof GenericObject
								&& o2 instanceof GenericObject) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							return go1.getPosition() - go2.getPosition();
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}

					public boolean equals(Object o) {
						return false;
					}
				});
			}
			// restore Powers
			Element powersElement = root.getChild("POWERS");
			// restore Power Lists, MPs, ECs and VPPs
			if (powersElement != null) {
				java.util.List list = powersElement.getChildren("LIST");
				Iterator iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.List li = new com.hero.objects.List(l);
					li.restoreFromSave(l);
					addPower(li);
				}
				list = powersElement.getChildren("MULTIPOWER");
				iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.Multipower li = new com.hero.objects.Multipower(
							l);
					li.restoreFromSave(l);
					addPower(li);
				}
				list = powersElement.getChildren("ELEMENTAL_CONTROL");
				iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.ElementalControl li = new com.hero.objects.ElementalControl(
							l);
					li.restoreFromSave(l);
					addPower(li);
				}
				list = powersElement.getChildren("VPP");
				iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.VariablePowerPool li = new com.hero.objects.VariablePowerPool(
							l);
					li.restoreFromSave(l);
					addPower(li);
				}
				// now restore the actual Powers
				java.util.List powerList = powersElement.getChildren("POWER");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject p = powerHash.get(xmlID);
						if (p != null) {
							p = p.clone();
							p.restoreFromSave(sk);
							addPower(p);
						}
					}
				}
				// now restore the Power Skills
				powerList = powersElement.getChildren("SKILL");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject power = skillHash.get(xmlID);
						if (power != null) {
							power = power.clone();
							power.restoreFromSave(sk);
							addPower(power);
						}
					}
				}
				// now restore the Power Perks
				powerList = powersElement.getChildren("PERK");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject power = perkHash.get(xmlID);
						if (power != null) {
							power = power.clone();
							power.restoreFromSave(sk);
							addPower(power);
						}
					}
				}
				// now restore the Power Talents
				powerList = powersElement.getChildren("TALENT");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject power = talentHash.get(xmlID);
						if (power != null) {
							power = power.clone();
							power.restoreFromSave(sk);
							addPower(power);
						}
					}
				}
				// now restore the Power Chars
				for (GenericObject ch : characteristics) {
					powerList = powersElement.getChildren(ch.getXMLID());
					iterator = powerList.iterator();
					while (iterator.hasNext()) {
						Element sk = (Element) iterator.next();
						if (sk != null) {
							String xmlID = ch.getXMLID();
							LOOP: for (GenericObject power : characteristics) {
								if (power.getXMLID().equals(xmlID)) {
									power = power.clone();
									power.restoreFromSave(sk);
									power.setPower(true);
									addPower(power);
									break LOOP;
								}
							}
						}
					}
				}
				Collections.sort(powers, new Comparator() {
					public int compare(Object o1, Object o2) {
						if (o1 instanceof GenericObject
								&& o2 instanceof GenericObject) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							return go1.getPosition() - go2.getPosition();
						}
						return o1.toString().compareTo(o2.toString());
					}

					public boolean equals(Object o) {
						return false;
					}
				});
			}
			// restore Equipment
			powersElement = root.getChild("EQUIPMENT");
			// restore Power Lists, MPs, ECs and VPPs
			if (powersElement != null) {
				java.util.List list = powersElement.getChildren("LIST");
				Iterator iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.List li = new com.hero.objects.List(l);
					li.restoreFromSave(l);
					addEquipment(li);
				}
				list = powersElement.getChildren("MULTIPOWER");
				iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.Multipower li = new com.hero.objects.Multipower(
							l);
					li.restoreFromSave(l);
					addEquipment(li);
				}
				list = powersElement.getChildren("ELEMENTAL_CONTROL");
				iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.ElementalControl li = new com.hero.objects.ElementalControl(
							l);
					li.restoreFromSave(l);
					addEquipment(li);
				}
				list = powersElement.getChildren("VPP");
				iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.VariablePowerPool li = new com.hero.objects.VariablePowerPool(
							l);
					li.restoreFromSave(l);
					addEquipment(li);
				}
				// now restore the actual Powers
				java.util.List powerList = powersElement.getChildren("POWER");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject p = powerHash.get(xmlID);
						if (p != null) {
							p = p.clone();
							p.restoreFromSave(sk);
							addEquipment(p);
						}
					}
				}
				// now restore the Power Skills
				powerList = powersElement.getChildren("SKILL");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject power = skillHash.get(xmlID);
						if (power != null) {
							power = power.clone();
							power.restoreFromSave(sk);
							addEquipment(power);
						}
					}
				}
				// now restore the Power Perks
				powerList = powersElement.getChildren("PERK");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject power = perkHash.get(xmlID);
						if (power != null) {
							power = power.clone();
							power.restoreFromSave(sk);
							addEquipment(power);
						}
					}
				}
				// now restore the Power Talents
				powerList = powersElement.getChildren("TALENT");
				iterator = powerList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String xmlID = XMLUtility.getValue(sk, "XMLID");
						GenericObject power = talentHash.get(xmlID);
						if (power != null) {
							power = power.clone();
							power.restoreFromSave(sk);
							addEquipment(power);
						}
					}
				}
				// now restore the Power Chars
				for (GenericObject ch : characteristics) {
					powerList = powersElement.getChildren(ch.getXMLID());
					iterator = powerList.iterator();
					while (iterator.hasNext()) {
						Element sk = (Element) iterator.next();
						if (sk != null) {
							String xmlID = ch.getXMLID();
							LOOP: for (GenericObject power : characteristics) {
								if (power.getXMLID().equals(xmlID)) {
									power = power.clone();
									power.restoreFromSave(sk);
									power.setPower(true);
									addEquipment(power);
									break LOOP;
								}
							}
						}
					}
				}
				Collections.sort(equipment, new Comparator() {
					public int compare(Object o1, Object o2) {
						if (o1 instanceof GenericObject
								&& o2 instanceof GenericObject) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							return go1.getPosition() - go2.getPosition();
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}

					public boolean equals(Object o) {
						return false;
					}
				});
			}
			// restore Disads
			Element disadElement = root.getChild("DISADVANTAGES");
			// restore Disad Lists
			if (disadElement != null) {
				java.util.List list = disadElement.getChildren("LIST");
				Iterator iterator = list.iterator();
				while (iterator.hasNext()) {
					Element l = (Element) iterator.next();
					com.hero.objects.List li = new com.hero.objects.List(l);
					li.restoreFromSave(l);
					addDisad(li);
				}
				// now restore the actual Disads
				java.util.List maneuverList = disadElement.getChildren("DISAD");
				iterator = maneuverList.iterator();
				OUTER: while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String id = XMLUtility.getValue(sk, "XMLID");
						GenericObject disad = disadHash.get(id);
						if (disad != null) {
							disad = disad.clone();
							disad.restoreFromSave(sk);
							addDisad(disad);
							continue OUTER;
						}
						// if we get here, then it's a custom Disad
						// (unimplemented)
					}
				}
				// now check for NCM and Age
				java.util.List adderList = disadElement.getChildren("ADDER");
				iterator = adderList.iterator();
				while (iterator.hasNext()) {
					Element sk = (Element) iterator.next();
					if (sk != null) {
						String id = XMLUtility.getValue(sk, "XMLID");
						if (id == null) {
							continue;
						}
						Adder ad = new Adder(sk);
						ad.restoreFromSave(sk);
						if (ad.getXMLID().equals("NCM")) {
							ncmPosition = ad.getPosition();
							ncmParentID = ad.getParentID();
						} else {
							agePosition = ad.getPosition();
							ageParentID = ad.getParentID();
						}
					}
				}
				if (isNCMSelected()) {
					disads.add(getNCMObject());
					if (getAgeObject() != null) {
						getDisads().add(getAgeObject());
					}
				}
				Collections.sort(disads, new Comparator() {
					public int compare(Object o1, Object o2) {
						if (o1 instanceof GenericObject
								&& o2 instanceof GenericObject) {
							GenericObject go1 = (GenericObject) o1;
							GenericObject go2 = (GenericObject) o2;
							return go1.getPosition() - go2.getPosition();
						} else {
							return o1.toString().compareTo(o2.toString());
						}
					}

					public boolean equals(Object o) {
						return false;
					}
				});
			}
			java.util.List l = root.getChildren("PREFAB");
			prefabs = new ArrayList<Prefab>();
			if (l != null && l.size() > 0) {
				for (int i = 0; i < l.size(); i++) {
					Prefab p = new Prefab((Element) l.get(i));
					prefabs.add(p);
				}
			}
		} catch (Exception exp) {
			exp.printStackTrace();
			if (!HeroDesigner.headless) {
				JOptionPane
						.showMessageDialog(
								HeroDesigner.getAppFrame(),
								"An error occurred loading the character.\nData loss may occur if you save.\n\nPlease shut down Hero Designer and restart.\n\nError Message: "
										+ exp.getMessage(),
								"Error loading character",
								JOptionPane.ERROR_MESSAGE);
			}
		}
		HeroDesigner.lastEdit = System.currentTimeMillis();
	}

	/**
	 * @return The "dirty" state of the character (if changes have been made)
	 */
	public boolean isDirty() {
		return dirty;
	}

	public boolean isLoading() {
		return isLoading;
	}

	public boolean isNCMSelected() {
		if (ncmObject == null) {
			return false;
		}
		return ncmSelected;
	}

	public boolean isPrefab() {
		return false;
	}

	/**
	 * @return Returns true if the Normal Characteristic Maxima supports the Age
	 *         Disadvantage.
	 */
	boolean ncmHasAge() {
		if (ncmObject.getOptions().size() == 0) {
			return false;
		}
		boolean ret = false;
		for (Adder ad : ncmObject.getOptions()) {
			if (ad.getXMLID().equals(ncmObject.getXMLID())) {
				ret = true;
			}
		}
		return ret;
	}

	/**
	 * Reloads the character from the save file after a template change. Sets
	 * the template to the currently active template in HeroDesigner
	 * 
	 * @see com.hero.HeroDesigner
	 */
	public void resetTemplate() {
		if (HeroDesigner.getActiveTemplate() == null) {
			return; // do nothing....
		}
		String orig = templatePath;
		templatePath = HeroDesigner.getActiveTemplate().getId();
		/*
		 * if (orig != null && templatePath.equals(orig)) { return; }
		 */
		initFromSave();
	}

	public void setAlternateIdentities(String alternateIdentities) {
		if (this.alternateIdentities.equals(alternateIdentities)) {
			return;
		}
		this.alternateIdentities = alternateIdentities;
		setDirty(true);
	}

	public void setAppearance(String appearance) {
		if (this.appearance.equals(appearance)) {
			return;
		}
		this.appearance = appearance;
		setDirty(true);
	}

	public void setBackground(String val) {
		if (background.equals(val)) {
			return;
		}
		background = val;
		setDirty(true);
	}

	public void setBasePoints(int basePoints) {
		if (this.basePoints == basePoints) {
			return;
		}
		this.basePoints = basePoints;
		setDirty(true);
	}

	public void setCampaignName(String campaignName) {
		if (this.campaignName.equals(campaignName)) {
			return;
		}
		this.campaignName = campaignName;
		setDirty(true);
	}

	public void setCampaignUse(String val) {
		if (campaignUse.equals(val)) {
			return;
		}
		campaignUse = val;
		setDirty(true);
	}

	public void setCharacterName(String characterName) {
		if (this.characterName.equals(characterName)) {
			return;
		}
		this.characterName = characterName;
		HeroDesigner.getInstance().setActiveName(characterName);
		HeroDesigner.getInstance().updateStatus();
		setDirty(true);
	}

	/**
	 * Call this when changes have been made to the character which require
	 * saving.
	 * 
	 * @param val
	 */
	public void setDirty(boolean val) {
		if (val) {
			lastEdit = System.currentTimeMillis();
			HeroDesigner.getInstance().setDirty(val);
		}
		if (dirty == val) {
			return;
		}
		dirty = val;
		HeroDesigner.getInstance().setDirty(dirty);
	}

	public void setDisadPoints(int disadPoints) {
		if (this.disadPoints == disadPoints) {
			return;
		}
		this.disadPoints = disadPoints;
		setDirty(true);
	}

	protected void setDisads(ArrayList<GenericObject> disads) {
		this.disads = disads;
	}

	protected void setEquipment(ArrayList<GenericObject> equipment) {
		this.equipment = equipment;
	}

	public void setExperience(int experience) {
		if (this.experience == experience) {
			return;
		}
		this.experience = experience;
		setDirty(true);
	}

	/**
	 * Sets the export template for this character to the specified path. The
	 * parameter passed in should be a full (absolute) path to the export
	 * template file.
	 * 
	 * @param val
	 *            The absolute path to the export template file.
	 */
	public void setExportTemplate(String val) {
		if (exportTemplate != null && val != null) {
			if (exportTemplate.equals(val)) {
				setDirty(true);
			}
		} else {
			setDirty(true);
		}
		if (val != null) {
			File test = new File(val);
			if (test.exists()) {
				exportTemplate = val;
			} else {
				exportTemplate = null;
			}
		} else {
			exportTemplate = null;
		}
	}

	public void setEyeColor(String eyeColor) {
		if (this.eyeColor.equals(eyeColor)) {
			return;
		}
		this.eyeColor = eyeColor;
		setDirty(true);
	}

	public void setGenre(String genre) {
		if (this.genre.equals(genre)) {
			return;
		}
		this.genre = genre;
		setDirty(true);
	}

	public void setGm(String gm) {
		if (this.gm.equals(gm)) {
			return;
		}
		this.gm = gm;
		setDirty(true);
	}

	public void setHairColor(String hairColor) {
		if (this.hairColor.equals(hairColor)) {
			return;
		}
		this.hairColor = hairColor;
		setDirty(true);
	}

	/**
	 * Sets the height for the character
	 * 
	 * @param val
	 *            The value for the height of the character. Specified in the
	 *            units currently specified under AppPrefs
	 * @see com.hero.AppPrefs
	 */
	public void setHeight(int val) {
		double set = val;
		if (HeroDesigner.getInstance().getPrefs().isMetric()) {
			set = val / 2.54; // cm -> in
		}
		if (height == set) {
			return;
		}
		height = set;
		setDirty(true);
	}

	public void setImageData(byte[] data, File file) {
		imageData = data;
		if (file == null) {
			imageFileName = "";
			imageFilePath = "";
		} else {
			imageFileName = file.getName();
			imageFilePath = file.getAbsolutePath();
		}
		setDirty(true);
	}

	/**
	 * Sets the last tab index selected on the character. This value is not
	 * persisted, it's only used when switching between characters (it keeps the
	 * tab selection consistent)
	 * 
	 * @param val
	 *            The index of the tab to select.
	 */
	public void setLastTabIndex(int val) {
		lastTab = val;
	}

	protected void setManeuvers(ArrayList<GenericObject> maneuvers) {
		this.maneuvers = maneuvers;
	}

	public void setNCMSelected(boolean val) {
		ncmSelected = val;
	}

	public void setNotes1(String val) {
		if (getNotes1().equals(val)) {
			return;
		}
		notes1 = val;
		setDirty(true);
	}

	public void setNotes2(String val) {
		if (getNotes2().equals(val)) {
			return;
		}
		notes2 = val;
		setDirty(true);
	}

	public void setNotes3(String val) {
		if (getNotes3().equals(val)) {
			return;
		}
		notes3 = val;
		setDirty(true);
	}

	public void setNotes4(String val) {
		if (getNotes4().equals(val)) {
			return;
		}
		notes4 = val;
		setDirty(true);
	}

	public void setNotes5(String val) {
		if (getNotes5().equals(val)) {
			return;
		}
		notes5 = val;
		setDirty(true);
	}

	protected void setPerks(ArrayList<GenericObject> perks) {
		this.perks = perks;
	}

	public void setPersonality(String val) {
		if (personality.equals(val)) {
			return;
		}
		personality = val;
		setDirty(true);
	}

	public void setPlayerName(String playerName) {
		if (this.playerName.equals(playerName)) {
			return;
		}
		this.playerName = playerName;
		setDirty(true);
	}

	protected void setPowers(ArrayList<GenericObject> powers) {
		this.powers = powers;
	}

	public void setQuote(String val) {
		if (quote.equals(val)) {
			return;
		}
		quote = val;
		setDirty(true);
	}

	public void setSaveFile(File file) {
		saveFile = file;
		openFile = file;
	}

	protected void setSkills(ArrayList<GenericObject> skills) {
		this.skills = skills;
	}

	public void setTactics(String val) {
		if (tactics.equals(val)) {
			return;
		}
		tactics = val;
		setDirty(true);
	}

	protected void setTalents(ArrayList<GenericObject> talents) {
		this.talents = talents;
	}

	/**
	 * Sets the path to the character template to use. The path should be either
	 * an absolute path to a file on the system or a reference to one of the
	 * "built in" templates.
	 * 
	 * @param path
	 */
	public void setTemplatePath(String path) {
		templatePath = path;
	}

	/**
	 * Sets the weight of the character. The value specified should be in the
	 * same units as are selected under AppPrefs.
	 * 
	 * @param val
	 * @see com.hero.AppPrefs
	 */
	public void setWeight(int val) {
		double set = val;
		if (HeroDesigner.getInstance().getPrefs().isMetric()) {
			set = val * 1000d / 453.5924; // g -> lbs
		}
		if (weight == set) {
			return;
		}
		weight = set;
		setDirty(true);
	}

	public boolean hasFileAssociations() {
		boolean ret = false;
		for (int i = 0; i < getPerks().size(); i++) {
			if (getPerks().get(i) instanceof Follower) {
				Follower f = (Follower) getPerks().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						return true;
					}
				}
			} else if (getPerks().get(i) instanceof Vehicle) {
				Vehicle f = (Vehicle) getPerks().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						return true;
					}
				}
			}
		}
		for (int i = 0; i < getPowers().size(); i++) {
			if (getPowers().get(i) instanceof Duplication) {
				Duplication f = (Duplication) getPowers().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						return true;
					}
				}
			} else if (getPowers().get(i) instanceof Multiform) {
				Multiform f = (Multiform) getPowers().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						return true;
					}
				}
			} else if (getPowers().get(i) instanceof Summon) {
				Summon f = (Summon) getPowers().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						return true;
					}
				}
			}
		}

		return ret;
	}

	public ArrayList<File> getFileAssociations() {
		ArrayList<File> ret = new ArrayList<File>();
		for (int i = 0; i < getPerks().size(); i++) {
			if (getPerks().get(i) instanceof Follower) {
				Follower f = (Follower) getPerks().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						ret.add(check);
					}
				}
			} else if (getPerks().get(i) instanceof Vehicle) {
				Vehicle f = (Vehicle) getPerks().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						ret.add(check);
					}
				}
			}
		}
		for (int i = 0; i < getPowers().size(); i++) {
			if (getPowers().get(i) instanceof Duplication) {
				Duplication f = (Duplication) getPowers().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						ret.add(check);
					}
				}
			} else if (getPowers().get(i) instanceof Multiform) {
				Multiform f = (Multiform) getPowers().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						ret.add(check);
					}
				}
			} else if (getPowers().get(i) instanceof Summon) {
				Summon f = (Summon) getPowers().get(i);
				if (f.getFilePath() != null
						&& f.getFilePath().trim().length() > 0) {
					File check = new File(f.getFilePath());
					if (check.exists()) {
						ret.add(check);
					}
				}
			}
		}

		return ret;
	}
}