package com.hero.objects;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Iterator;

import javax.swing.JOptionPane;

import org.jdom.Element;

import com.hero.HeroDesigner;
import com.hero.objects.characteristics.Characteristic;
import com.hero.objects.disads.Disadvantage;
import com.hero.objects.enhancers.Enhancer;
import com.hero.objects.martialarts.Maneuver;
import com.hero.objects.martialarts.WeaponElement;
import com.hero.objects.modifiers.Modifier;
import com.hero.objects.powers.Automaton;
import com.hero.objects.powers.CompoundPower;
import com.hero.objects.powers.EnduranceReserveRecovery;
import com.hero.objects.powers.MentalAwareness;
import com.hero.objects.powers.MentalIllusions;
import com.hero.objects.powers.NakedModifier;
import com.hero.objects.skills.Skill;
import com.hero.objects.skills.TransportFamiliarity;
import com.hero.objects.skills.WeaponFamiliarity;
import com.hero.ui.dialog.GenericDialog;
import com.hero.util.Rounder;
import com.hero.util.XMLUtility;

/**
 * Copyright (c) 2000 - 2005, CompNet Design, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, is prohibited unless the following conditions are met: 1.
 * Express written consent of CompNet Design, Inc. is obtained by the developer.
 * 2. Redistributions must retain this copyright notice. THIS SOFTWARE IS
 * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * @author CompNet Design, Inc.
 * @version $Revision$
 */

public class GenericObject implements Cloneable, Comparable {
	private static long idCount;

	private static Hashtable<String, String> idTranslations;

	public static long lastEdit;

	protected String abbreviation;

	protected boolean adjustAliasWithLevels = true;

	protected String alias;

	protected boolean allowsOtherAdders;

	protected boolean allowsOtherModifiers;

	private boolean appAdjusted = false;

	protected int apperend;

	public ArrayList<Adder> assignedAdders;

	protected ArrayList<Modifier> assignedModifiers;

	protected ArrayList<Adder> availableAdders;

	protected ArrayList<Modifier> availableModifiers;

	protected double baseCost;

	protected boolean carried;

	protected String defense;

	protected String definition;

	public String display;

	public String textOutput;

	protected boolean displayActiveCost;

	protected boolean doesBODY;

	protected boolean doesDamage;

	protected boolean doesKnockback;

	protected String duration;

	protected boolean dynamicDisplay;

	protected Enhancer enhancerApplied;

	protected ArrayList<String> examples;

	protected boolean exclusive;

	protected boolean fixedValue;

	protected long id;

	protected boolean includedInTemplate;

	protected boolean includeNotesInPrintout;

	protected String input;

	protected String inputLabel;

	protected boolean isEquipment;

	protected boolean isPower;

	protected boolean isPrefab;

	protected boolean killing;

	protected double levelCost;

	protected int levelMultiplier;

	protected int levelPower;

	protected int levels;

	protected String levelsLbl;

	protected double levelValue;

	protected CompoundPower mainPower;

	protected double maxCost;

	protected int maxLevel;

	protected boolean maxSet;

	protected double minimumCost;

	protected int minimumLevel;

	protected boolean minSet;

	protected double multiplier;

	protected String name;

	protected String notes;

	protected String optionLbl;

	protected ArrayList<Adder> options;

	protected double origBaseCost;

	protected String origDisplay;

	protected boolean otherInputAllowed;

	protected com.hero.objects.List parent;

	protected long parentID;

	protected int position;

	protected boolean positionLocked;

	protected double price;

	protected String range;

	protected Adder selectedOption;

	protected boolean showBuildDialog;

	protected boolean showOption;

	protected boolean stopSign;

	protected String target;

	protected ArrayList<String> types;

	protected boolean ultra;

	protected boolean useENDReserve;

	protected boolean userInput;

	protected boolean usesEND;

	protected boolean visible;

	protected boolean warnSign;

	protected double weight;

	protected String wgAbbreviation;

	protected String xmlID;

	protected String graphic;

	protected String color;

	protected String sfx;

	private boolean listModCheck = false;

	protected int quantity = 1;

	private boolean costsENDToMaintain = true;

	private boolean continuingEffect = false;

	public void setListModCheck(boolean val) {
		listModCheck = val;
	}

	public boolean isListModCheck() {
		return listModCheck;
	}

	protected ArrayList<String> sources;

	protected static ArrayList<String> allSources = new ArrayList<String>();

	public static final String[] standardSFX = new String[] { "Acid", "Alien",
			"Air/Wind", "Animal", "Body Control", "Chi", "Cosmic Energy",
			"Cyberkinesis", "Darkness", "Density Alteration",
			"Dimensional Manipulation", "Earth/Stone", "Electricity",
			"Emotion Control", "Fire/Heat", "Force", "Gravity", "Ice/Cold",
			"Illusion", "Kinetic Energy", "Light", "Luck", "Magic/Mystic",
			"Magnetism", "Martial Arts", "Matter Manipulation",
			"Mental/Psionic", "Metamorphic", "Mutant", "Precognition",
			"Radiation", "Serum Based", "Shape Alteration", "Size Alteration",
			"Sleep/Dream", "Solar/Celestial", "Sonic", "Speedster",
			"Strength/Toughness", "Stretching", "Telekinetic", "Teleportation",
			"Time", "Vibration", "Water", "Weather", "Wood/Plant",
			"Miscellaneous" };

	public String getGraphic() {
		if (graphic == null || graphic.trim().length() == 0) {
			graphic = "Burst";
		}
		if (getParentList() != null
				&& getParentList() instanceof ElementalControl) {
			return getParentList().getGraphic();
		}
		return graphic;
	}

	public String getColor() {
		if (color == null || color.trim().length() == 0) {
			color = "255 255 255";
		}
		if (getParentList() != null
				&& getParentList() instanceof ElementalControl) {
			return getParentList().getColor();
		}
		return color;
	}

	public String getSFX() {
		if (sfx == null || sfx.trim().length() == 0) {
			sfx = "Default";
		}
		if (getParentList() != null
				&& getParentList() instanceof ElementalControl) {
			return getParentList().getSFX();
		}
		return sfx;
	}

	public void setSFX(String val) {
		boolean changeHS = true;
		if (!getGraphic().equalsIgnoreCase(
				GenericObject.getGraphicForSFX(getSFX()))) {
			changeHS = false;
		}
		if (!getColor()
				.equalsIgnoreCase(GenericObject.getColorForSFX(getSFX()))) {
			changeHS = false;
		}
		sfx = val;
		if (changeHS) {
			graphic = GenericObject.getGraphicForSFX(sfx);
			color = GenericObject.getColorForSFX(sfx);
		}
	}

	public static String getGraphicForSFX(String fx) {
		if (fx.trim().toUpperCase().equals("ACID")) {
			return "block";
		}
		if (fx.trim().toUpperCase().equals("ALIEN")) {
			return "zap";
		}
		if (fx.trim().toUpperCase().equals("AIR/WIND")) {
			return "rads";
		}
		if (fx.trim().toUpperCase().equals("ANIMAL")) {
			return "block";
		}
		if (fx.trim().toUpperCase().equals("BIOCHEMICAL")) {
			return "clump";
		}
		if (fx.trim().toUpperCase().equals("BODY CONTROL")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("CHI")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("COSMIC ENERGY")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("CYBERKINESIS")) {
			return "rads";
		}
		if (fx.trim().toUpperCase().equals("DARKNESS")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("DENSITY ALTERATION")) {
			return "clump";
		}
		if (fx.trim().toUpperCase().equals("DIMENSIONAL MANIPULATION")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("EARTH/STONE")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("ELECTRICITY")) {
			return "zap";
		}
		if (fx.trim().toUpperCase().equals("EMOTION CONTROL")) {
			return "block";
		}
		if (fx.trim().toUpperCase().equals("FIRE/HEAT")) {
			return "zap";
		}
		if (fx.trim().toUpperCase().equals("FORCE")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("GRAVITY")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("ICE/COLD")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("ILLUSION")) {
			return "clump";
		}
		if (fx.trim().toUpperCase().equals("KINETIC ENERGY")) {
			return "rads";
		}
		if (fx.trim().toUpperCase().equals("LIGHT")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("LUCK")) {
			return "block";
		}
		if (fx.trim().toUpperCase().equals("MAGIC/MYSTIC")) {
			return "rads";
		}
		if (fx.trim().toUpperCase().equals("MAGNETISM")) {
			return "zap";
		}
		if (fx.trim().toUpperCase().equals("MARTIAL ARTS")) {
			return "block";
		}
		if (fx.trim().toUpperCase().equals("MATTER MANIPULATION")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("MENTAL PSIONIC")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("METAMORPHIC")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("MUTANT")) {
			return "clump";
		}
		if (fx.trim().toUpperCase().equals("PLASMA")) {
			return "block";
		}
		if (fx.trim().toUpperCase().equals("PRECOGNITION")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("RADIATION")) {
			return "rads";
		}
		if (fx.trim().toUpperCase().equals("SERUM BASED")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("SHAPE ALTERATION")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("SIZE ALTERATION")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("SLEEP/DREAM")) {
			return "burst";
		}
		if (fx.trim().toUpperCase().equals("SOLAR/CELESTIAL")) {
			return "zap";
		}
		if (fx.trim().toUpperCase().equals("SONIC")) {
			return "zap";
		}
		if (fx.trim().toUpperCase().equals("SPEEDSTER")) {
			return "rads";
		}
		if (fx.trim().toUpperCase().equals("STRENGTH/TOUGHNESS")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("STRETCHING")) {
			return "block";
		}
		if (fx.trim().toUpperCase().equals("TELEKINETIC")) {
			return "clump";
		}
		if (fx.trim().toUpperCase().equals("TELEPORTATION")) {
			return "zap";
		}
		if (fx.trim().toUpperCase().equals("TIME")) {
			return "rads";
		}
		if (fx.trim().toUpperCase().equals("VIBRATION")) {
			return "clump";
		}
		if (fx.trim().toUpperCase().equals("WATER")) {
			return "clump";
		}
		if (fx.trim().toUpperCase().equals("WEATHER")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("WOOD/PLANT")) {
			return "hole";
		}
		if (fx.trim().toUpperCase().equals("MISCELLANEOUS")) {
			return "burst";
		}
		return "burst";
	}

	public static String getColorForSFX(String fx) {
		if (fx.trim().toUpperCase().equals("ACID")) {
			return "180 255 0";
		}
		if (fx.trim().toUpperCase().equals("ALIEN")) {
			return "180 0 180";
		}
		if (fx.trim().toUpperCase().equals("AIR/WIND")) {
			return "0 255 255";
		}
		if (fx.trim().toUpperCase().equals("ANIMAL")) {
			return "255 180 0";
		}
		if (fx.trim().toUpperCase().equals("BIOCHEMICAL")) {
			return "0 255 0";
		}
		if (fx.trim().toUpperCase().equals("BODY CONTROL")) {
			return "180 0 255";
		}
		if (fx.trim().toUpperCase().equals("CHI")) {
			return "0 255 180";
		}
		if (fx.trim().toUpperCase().equals("COSMIC ENERGY")) {
			return "255 100 0";
		}
		if (fx.trim().toUpperCase().equals("CYBERKINESIS")) {
			return "127 127 127";
		}
		if (fx.trim().toUpperCase().equals("DARKNESS")) {
			return "0 0 0";
		}
		if (fx.trim().toUpperCase().equals("DENSITY ALTERATION")) {
			return "0 0 180";
		}
		if (fx.trim().toUpperCase().equals("DIMENSIONAL MANIPULATION")) {
			return "90 180 90";
		}
		if (fx.trim().toUpperCase().equals("EARTH/STONE")) {
			return "255 180 0";
		}
		if (fx.trim().toUpperCase().equals("ELECTRICITY")) {
			return "255 255 0";
		}
		if (fx.trim().toUpperCase().equals("EMOTION CONTROL")) {
			return "180 180 255";
		}
		if (fx.trim().toUpperCase().equals("FIRE/HEAT")) {
			return "255 0 0 ";
		}
		if (fx.trim().toUpperCase().equals("FORCE")) {
			return "0 0 255";
		}
		if (fx.trim().toUpperCase().equals("GRAVITY")) {
			return "50 100 200";
		}
		if (fx.trim().toUpperCase().equals("ICE/COLD")) {
			return "255 255 255";
		}
		if (fx.trim().toUpperCase().equals("ILLUSION")) {
			return "255 0 255";
		}
		if (fx.trim().toUpperCase().equals("KINETIC ENERGY")) {
			return "200 100 50";
		}
		if (fx.trim().toUpperCase().equals("LIGHT")) {
			return "255 255 255";
		}
		if (fx.trim().toUpperCase().equals("LUCK")) {
			return "0 255 0";
		}
		if (fx.trim().toUpperCase().equals("MAGIC/MYSTIC")) {
			return "255 0 180";
		}
		if (fx.trim().toUpperCase().equals("MAGNETISM")) {
			return "255 0 0";
		}
		if (fx.trim().toUpperCase().equals("MARTIAL ARTS")) {
			return "255 255 255";
		}
		if (fx.trim().toUpperCase().equals("MATTER MANIPULATION")) {
			return "100 100 100";
		}
		if (fx.trim().toUpperCase().equals("MENTAL PSIONIC")) {
			return "0 100 255";
		}
		if (fx.trim().toUpperCase().equals("METAMORPHIC")) {
			return "200 100 0";
		}
		if (fx.trim().toUpperCase().equals("MUTANT")) {
			return "0 255 0";
		}
		if (fx.trim().toUpperCase().equals("PLASMA")) {
			return "255 0 255";
		}
		if (fx.trim().toUpperCase().equals("PRECOGNITION")) {
			return "100 100 100";
		}
		if (fx.trim().toUpperCase().equals("RADIATION")) {
			return "0 255 0";
		}
		if (fx.trim().toUpperCase().equals("SERUM BASED")) {
			return "255 255 0";
		}
		if (fx.trim().toUpperCase().equals("SHAPE ALTERATION")) {
			return "180 180 255";
		}
		if (fx.trim().toUpperCase().equals("SIZE ALTERATION")) {
			return "255 180 0";
		}
		if (fx.trim().toUpperCase().equals("SLEEP/DREAM")) {
			return "255 100 255";
		}
		if (fx.trim().toUpperCase().equals("SOLAR/CELESTIAL")) {
			return "255 255 0";
		}
		if (fx.trim().toUpperCase().equals("SONIC")) {
			return "0 0 255";
		}
		if (fx.trim().toUpperCase().equals("SPEEDSTER")) {
			return "255 0 0";
		}
		if (fx.trim().toUpperCase().equals("STRENGTH/TOUGHNESS")) {
			return "180 255 0";
		}
		if (fx.trim().toUpperCase().equals("STRETCHING")) {
			return "0 180 255";
		}
		if (fx.trim().toUpperCase().equals("TELEKINETIC")) {
			return "0 255 180";
		}
		if (fx.trim().toUpperCase().equals("TELEPORTATION")) {
			return "90 90 90";
		}
		if (fx.trim().toUpperCase().equals("TIME")) {
			return "180 0 255";
		}
		if (fx.trim().toUpperCase().equals("VIBRATION")) {
			return "255 0 255";
		}
		if (fx.trim().toUpperCase().equals("WATER")) {
			return "0 0 255";
		}
		if (fx.trim().toUpperCase().equals("WEATHER")) {
			return "180 0 255";
		}
		if (fx.trim().toUpperCase().equals("WOOD/PLANT")) {
			return "0 255 0";
		}
		if (fx.trim().toUpperCase().equals("MISCELLANEOUS")) {
			return "255 255 255";
		}
		return "255 255 255";
	}

	/**
	 * Empty constructor.
	 */
	public GenericObject() {
		includedInTemplate = true;
		resetID();
	}

	/**
	 * Initializes the values for this object from the given element. Assumes
	 * the passed in element contains the values/attributes needed.
	 * 
	 * @param element
	 */
	public GenericObject(Element element) {
		id = System.currentTimeMillis() + GenericObject.idCount;
		if (element == null) {
			includedInTemplate = false;
		} else {
			String check = XMLUtility.getValue(element, "XMLID");
			if (check != null && check.trim().length() > 0) {
				xmlID = check;
			}
			init(element);
			includedInTemplate = true;
		}
	}

	/**
	 * Initializes the object from the passed in element, assuming that the
	 * element contains a child with the specified XMLID for the child name. All
	 * values are read in from the child.
	 * 
	 * @param root
	 * @param xmlID
	 */
	public GenericObject(Element root, String xmlID) {
		id = System.currentTimeMillis() + GenericObject.idCount;
		this.xmlID = xmlID;
		if (root == null) {
			includedInTemplate = false;
		} else {
			Element element = root.getChild(xmlID);
			if (element == null) {
				includedInTemplate = false;
			} else {
				init(element);
				includedInTemplate = true;
			}
		}
	}

	/**
	 * Simple utility method that iterates through the passed in Vector looking
	 * for a GenericObject with the specified XMLID
	 * 
	 * @param list
	 * @param id
	 * @return
	 */
	public static GenericObject findObjectByID(
			ArrayList<? extends GenericObject> list, String id) {
		for (int i = 0; i < list.size(); i++) {
			GenericObject o = list.get(i);
			if (o instanceof com.hero.objects.List) {
				com.hero.objects.List l2 = (com.hero.objects.List) o;
				GenericObject o2 = GenericObject.findObjectByID(
						l2.getObjects(), id);
				if (o2 != null) {
					return o2;
				}
			}
			if (o.getXMLID().equals("COMPOUNDPOWER")) {
				CompoundPower l2 = (CompoundPower) o;
				GenericObject o2 = GenericObject.findObjectByID(l2.getPowers(),
						id);
				if (o2 != null) {
					return o2;
				}
			}
			if (o.getXMLID().equals(id.trim().toUpperCase())) {
				return o;
			}
		}
		return null;
	}

	/**
	 * Utility method that provides translations from old (v1) XMLIDs to the new
	 * (v2) values. Key = old id, value = new id.
	 * 
	 * @return
	 */
	protected static Hashtable<String, String> getIDTranslations() {
		if (GenericObject.idTranslations == null) {
			GenericObject.idTranslations = new Hashtable<String, String>();
			GenericObject.idTranslations.put("RADIOTRANSMISSION",
					"RADIOPERCEIVETRANSMIT");
			GenericObject.idTranslations.put("IRPERCEPTION",
					"INFRAREDPERCEPTION");
			GenericObject.idTranslations.put("NRAY", "NRAYPERCEPTION");
			GenericObject.idTranslations.put("UVPERCEPTION",
					"ULTRAVIOLETPERCEPTION");
			GenericObject.idTranslations.put("HEARING", "HEARINGGROUP");
			GenericObject.idTranslations.put("RADIO", "RADIOGROUP");
			GenericObject.idTranslations.put("SIGHT", "SIGHTGROUP");
			GenericObject.idTranslations.put("MENAL", "MENTALGROUP");
			GenericObject.idTranslations.put("SMELL", "SMELLGROUP");
			GenericObject.idTranslations.put("TOUCH", "TOUCHGROUP");
		}
		return GenericObject.idTranslations;
	}

	public boolean allowsOtherAdders() {
		return allowsOtherAdders;
	}

	public boolean allowsOtherModifiers() {
		return allowsOtherModifiers;
	}

	/**
	 * Checks whether the specified Adder can be assigned to the object. Default
	 * is true.
	 * 
	 * @param value
	 * @return True if the Adder specified can be added in.
	 */
	public boolean canAdd(Adder value) {
		return true;
	}

	public void resetInternal() {

	}

	/**
	 * Clones the object. This performs a deep clone operation on all contained
	 * objects.
	 */
	public GenericObject clone() {
		GenericObject clone = this;

		try {
			clone = (GenericObject) super.clone();
		} catch (CloneNotSupportedException e) {
		}
		// set the id for those objects that check it for their methods (like
		// SenseAdders)
		clone.setID(getID());
		clone.setPower(isPower());
		ArrayList<Adder> assAdClone = new ArrayList<Adder>();
		if (assignedAdders != null) {
			for (int i = 0; i < assignedAdders.size(); i++) {
				Adder ad = assignedAdders.get(i);
				ad = ad.clone();
				if (ad.isExclusive()) {
					if (GenericObject.findObjectByID(assAdClone, ad.getXMLID()) != null) {
						continue;
					}
				}
				assAdClone.add(ad);
			}
		}
		clone.setAssignedAdders(assAdClone);
		ArrayList<Modifier> assignedMods = new ArrayList<Modifier>();
		if (assignedModifiers == null) {
			assignedModifiers = new ArrayList<Modifier>();
		}
		for (int i = 0; i < assignedModifiers.size(); i++) {
			Modifier mod = assignedModifiers.get(i);
			mod = mod.clone();
			mod.setParent(clone);
			assignedMods.add(mod);
		}
		clone.setAssignedModifiers(assignedMods);
		if (availableAdders != null) {
			ArrayList<Adder> vec = new ArrayList<Adder>();
			for (int i = 0; i < availableAdders.size(); i++) {
				Adder ad = availableAdders.get(i);
				ad = (Adder) ad.clone();
				vec.add(ad);
			}
			clone.setAvailableAdders(vec);
		}
		if (availableModifiers != null) {
			ArrayList<Modifier> vec = new ArrayList<Modifier>();
			for (int i = 0; i < availableModifiers.size(); i++) {
				Modifier mod = availableModifiers.get(i);
				mod = mod.clone();
				vec.add(mod);
			}
			clone.setAvailableModifiers(vec);
		}
		if (examples != null) {
			clone.setExamples((ArrayList<String>) examples.clone());
		}
		clone.setTypes((ArrayList<String>) getTypes().clone());
		// clone.resetID();
		if (options != null) {
			ArrayList<Adder> vec = new ArrayList<Adder>();
			for (int i = 0; i < options.size(); i++) {
				Adder op = options.get(i);
				op = (Adder) op.clone();
				vec.add(op);
			}
			clone.setOptions(vec);
		}
		clone.resetInternal();
		if (getSelectedOption() != null) {
			Adder op = (Adder) getSelectedOption().clone();
			op.setID(getSelectedOption().getID());
			clone.setSelectedOption(op);
		} else {
			clone.setSelectedOption(null);
		}
		clone.resetID();
		return clone;
	}

	public int compareTo(Object o) {
		if (o instanceof GenericObject) {
			GenericObject go = (GenericObject) o;
			if (getPosition() == go.getPosition()) {
				return getSortingValue().compareTo(go.getSortingValue());
			} else {
				return getPosition() - go.getPosition();
			}
		} else {
			return toString().compareTo(o.toString());
		}
	}

	/**
	 * Returns true if this object contains the specified type (String) in its
	 * types Vector.
	 * 
	 * @param type
	 * @return
	 */
	public boolean containsType(String type) {
		return types.contains(type);
	}

	/**
	 * Returns true if the object does BODY damage. Takes into account Modifiers
	 * that have been assigned directly to this object (not any list Modifiers).
	 * 
	 * @return
	 */
	public boolean doesBODY() {
		boolean ret = doesBODY;
		if (ret) {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(), "AVAD") != null) {
				ret = false;
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(), "NND") != null) {
				ret = false;
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(), "AVLD") != null) {
				ret = false;
			}
			if (GenericObject
					.findObjectByID(getAllAssignedModifiers(), "BOECV") != null) {
				ret = false;
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"STUNONLY") != null) {
				ret = false;
			}
		}
		if (!ret) {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"DOESBODY") != null) {
				ret = true;
			}
		}
		return ret;
	}

	/**
	 * Returns true if this object does damage.
	 * 
	 * @return
	 */
	public boolean doesDamage() {
		boolean ret = doesDamage;
		return ret;
	}

	/**
	 * Returns true if this object does Knockback. Takes into account Modifiers
	 * that have been assigned to this object (not any List Modifiers).
	 * 
	 * @return
	 */
	public boolean doesKnockback() {
		boolean ret = doesKnockback;
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "STUNONLY") != null) {
			ret = false;
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "NND") != null) {
			ret = false;
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "AVLD") != null) {
			ret = false;
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "BOECV") != null) {
			ret = false;
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "DOESBODY") != null) {
			ret = true;
		}
		if (!ret
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"DOESKB") != null) {
			ret = true;
		}
		if (ret
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"NOKB") != null) {
			ret = false;
		}
		return ret;
	}

	/**
	 * Default equals method. Compares ids.
	 */
	public boolean equals(Object o) {
		if (o instanceof GenericObject) {
			long compID = ((GenericObject) o).getID();
			if (compID == id) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public int hashCode() {
		return (int) id;
	}

	/**
	 * Returns the abbreviation for this object.
	 * 
	 * @return
	 */
	public String getAbbr() {
		if (abbreviation == null) {
			abbreviation = getAlias();
		}
		if (HeroDesigner.getInstance().getPrefs().useWG()) {
			if (wgAbbreviation == null) {
				abbreviation = getAlias();
			} else {
				abbreviation = wgAbbreviation;
			}
		}
		if (display != null
				&& display.indexOf("[LVL]") >= 0
				&& (adjustAliasWithLevels || abbreviation.indexOf("[LVL]") >= 0)) {
			boolean swapIt = abbreviation.equals(display);
			String part1 = "";
			String part2 = "";
			if (display.indexOf("[LVL]") > 0) {
				part1 = display.substring(0, display.indexOf("[LVL]"));
			}
			if (display.indexOf("[LVL]") + 5 < display.length()) {
				part2 = display.substring(display.indexOf("[LVL]") + 5,
						display.length());
			}
			String lvlString = ""
					+ NumberFormat.getInstance().format(
							(getLevels() * getLevelMultiplier()));
			if (levelPower != 1) {
				lvlString = ""
						+ NumberFormat.getInstance().format(
								(long) (getLevelMultiplier() * Math.pow(
										levelPower, getLevels())));
			}
			if (swapIt) {
				abbreviation = part1 + lvlString + part2;
			} else if (abbreviation.indexOf("[LVL]") >= 0) {
				part1 = "";
				part2 = "";
				if (abbreviation.indexOf("[LVL]") > 0) {
					part1 = abbreviation.substring(0,
							abbreviation.indexOf("[LVL]"));
				}
				if (abbreviation.indexOf("[LVL]") + 5 < abbreviation.length()) {
					part2 = abbreviation.substring(
							abbreviation.indexOf("[LVL]") + 5,
							abbreviation.length());
				}
				abbreviation = part1 + lvlString + part2;
			} else {
				int index1 = -1;
				int index2 = -1;
				for (int i = 0; i < abbreviation.length(); i++) {
					char ch = abbreviation.charAt(i);
					if (Character.isDigit(ch) && index1 == -1) {
						index1 = i;
					} else if (!Character.isDigit(ch) && ch != ',') {
						if (index1 >= 0 && index2 == -1) {
							index2 = i;
							break;
						}
					}
				}
				if (index1 >= 0) {
					part1 = abbreviation.substring(0, index1);
					if (index2 > index1) {
						part2 = abbreviation.substring(index2,
								abbreviation.length());
					} else {
						part2 = "";
					}
					abbreviation = part1 + lvlString + part2;
				}
			}
		}
		return abbreviation == null ? "" : abbreviation;
	}

	public void setQuantity(int val) {
		quantity = val;
	}

	public int getQuantity() {
		return quantity;
	}

	/**
	 * Returns the Active Cost for this object.
	 * 
	 * @return
	 */
	public double getActiveCost() {
		double total = getTotalCost();
		double advantageTotal = 0d;
		boolean advantagesApplied = false;
		for (int i = 0; i < assignedModifiers.size(); i++) {
			Modifier mod = assignedModifiers.get(i);
			if (mod.getTotalValue() > 0) {
				advantageTotal += mod.getTotalValue();
				advantagesApplied = true;
			}
		}
		List parent = getParentList();
		if (getMainPower() != null) {
			parent = getMainPower().getParentList();
		}
		if (parent != null) {
			ArrayList<Modifier> parentMods = parent.getAssignedModifiers();
			LOOP: for (int i = 0; i < parentMods.size(); i++) {
				Modifier mod = parentMods.get(i);
				if (mod.getTypes().contains("VPP")) {
					continue LOOP;
				}
				if (mod.getXMLID().equals("CHARGES")
						&& parent instanceof Multipower) {
					continue LOOP;
				}
				if (mod.getTotalValue() > 0
						&& (GenericObject.findObjectByID(assignedModifiers,
								mod.getXMLID()) == null
								|| mod.getXMLID().equals("GENERIC_OBJECT")
								|| mod.getXMLID().equals("CUSTOM_MODIFIER") || mod
								.getXMLID().equals("MODIFIER"))) {
					if (parent instanceof Multipower
							|| parent instanceof ElementalControl) {
						continue;
					} else {
						advantageTotal += mod.getTotalValue();
					}
					advantagesApplied = true;
				}
			}
		}
		double val = total * (1 + advantageTotal);
		if (advantagesApplied) {
			val = Rounder.roundHalfDown(val);
			if (total > 0 && val < 1) {
				val = 1;
			}
		}
		return val;
	}

	/**
	 * Returns the Active Cost for this object, ignoring any Modifier with the
	 * specified XMLID. This can be useful in such instances as calculating the
	 * Area of a Power with Area Of Effect, where you do not take the AOE
	 * Modifier into account in determining the Active Points available for the
	 * base area.
	 * 
	 * @param excludeID
	 * @return
	 */
	public double getActiveCost(String excludeID) {
		double total = getTotalCost();
		double advantageTotal = 0d;
		boolean advantagesApplied = false;
		for (int i = 0; i < assignedModifiers.size(); i++) {
			Modifier mod = assignedModifiers.get(i);
			if (mod.getTotalValue() > 0 && !mod.getXMLID().equals(excludeID)) {
				advantageTotal += mod.getTotalValue();
				advantagesApplied = true;
			}
		}
		List parent = getParentList();
		if (getMainPower() != null) {
			parent = getMainPower().getParentList();
		}
		if (parent != null) {
			ArrayList<Modifier> parentMods = parent.getAssignedModifiers();
			LOOP: for (int i = 0; i < parentMods.size(); i++) {
				Modifier mod = parentMods.get(i);
				if (mod.getTypes().contains("VPP")) {
					continue LOOP;
				}
				if (mod.getXMLID().equals("CHARGES")
						&& parent instanceof Multipower) {
					continue LOOP;
				}
				if (mod.getTotalValue() > 0
						&& !mod.getXMLID().equals(excludeID)
						&& (GenericObject.findObjectByID(assignedModifiers,
								mod.getXMLID()) == null
								|| mod.getXMLID().equals("GENERIC_OBJECT")
								|| mod.getXMLID().equals("CUSTOM_MODIFIER") || mod
								.getXMLID().equals("MODIFIER"))) {
					if (parent instanceof Multipower
							|| parent instanceof ElementalControl) {
						continue;
					} else {
						advantageTotal += mod.getTotalValue();
					}
					advantagesApplied = true;
				}
			}
		}
		double val = total * (1 + advantageTotal);
		if (advantagesApplied) {
			val = Rounder.roundHalfDown(val);
		}
		return val;
	}

	protected void getAdderSaveXML(Element parent) {
		for (Adder ad : getAssignedAdders()) {
			parent.addContent(ad.getSaveXML());
		}
	}

	/**
	 * Returns the display string for all assigned adders. Takes into account
	 * the display settings for each Adder.
	 * 
	 * @return
	 */
	public String getAdderString() {
		ArrayList<String> vec = new ArrayList<String>();
		ArrayList<String> groupVec = new ArrayList<String>();
		for (Adder adder : getAssignedAdders()) {
			if ((adder.getAvailableAdders().size() > 0 || adder.isGroup())
					&& adder.isSelected()) {
				adder.addAliasToVector(groupVec);
			} else {
				adder.addAliasToVector(vec);
			}
		}
		Collections.sort(groupVec, new Comparator() {
			public int compare(Object o1, Object o2) {
				String s1 = o1.toString().toUpperCase();
				String s2 = o2.toString().toUpperCase();
				return s1.compareTo(s2);
			}

			public boolean equals(Object o) {
				return false;
			}
		});
		Collections.sort(vec, new Comparator() {
			public int compare(Object o1, Object o2) {
				String s1 = o1.toString().toUpperCase();
				String s2 = o2.toString().toUpperCase();
				return s1.compareTo(s2);
			}

			public boolean equals(Object o) {
				return false;
			}
		});
		StringBuffer ret = new StringBuffer();
		for (String s : groupVec) {
			if (s.trim().length() > 0) {
				if (ret.length() > 0) {
					ret.append(", ");
				}
				ret.append(s.trim());
			}
		}
		for (String s : vec) {
			if (s.trim().length() > 0) {
				if (ret.length() > 0) {
					ret.append(", ");
				}
				ret.append(s.trim());
			}
		}
		return ret.toString();
	}

	public String getAlias() {
		if (alias == null) {
			if (HeroDesigner.getInstance().getPrefs().useWG()
					&& HeroDesigner.getInstance().getPrefs().useAbbreviations()
					&& wgAbbreviation != null) {
				alias = wgAbbreviation;
			} else if (!HeroDesigner.getInstance().getPrefs().useWG()
					&& HeroDesigner.getInstance().getPrefs().useAbbreviations()
					&& abbreviation != null) {
				alias = abbreviation;
			} else {
				alias = display;
			}
		}
		if (alias == null) {
			alias = "";
		}
		if (display != null && display.indexOf("[LVL]") >= 0
				&& (adjustAliasWithLevels || alias.indexOf("[LVL]") >= 0)) {
			boolean swapIt = alias.equals(display);
			String part1 = "";
			String part2 = "";
			if (display.indexOf("[LVL]") > 0) {
				part1 = display.substring(0, display.indexOf("[LVL]"));
			}
			if (display.indexOf("[LVL]") + 5 < display.length()) {
				part2 = display.substring(display.indexOf("[LVL]") + 5,
						display.length());
			}
			String lvlString = ""
					+ NumberFormat.getInstance().format(
							(getLevels() * getLevelMultiplier()));
			if (levelPower != 1) {
				lvlString = ""
						+ NumberFormat.getInstance().format(
								(long) (getLevelMultiplier() * Math.pow(
										levelPower, getLevels())));
			}
			if (swapIt) {
				alias = part1 + lvlString + part2;
			} else if (alias.indexOf("[LVL]") >= 0) {
				part1 = "";
				part2 = "";
				if (alias.indexOf("[LVL]") > 0) {
					part1 = alias.substring(0, alias.indexOf("[LVL]"));
				}
				if (alias.indexOf("[LVL]") + 5 < alias.length()) {
					part2 = alias.substring(alias.indexOf("[LVL]") + 5,
							alias.length());
				}
				alias = part1 + lvlString + part2;
			} else {
				int index1 = -1;
				int index2 = -1;
				for (int i = 0; i < alias.length(); i++) {
					char ch = alias.charAt(i);
					if (Character.isDigit(ch) && index1 == -1) {
						index1 = i;
					} else if (!Character.isDigit(ch) && ch != ',') {
						if (index1 >= 0 && index2 == -1) {
							index2 = i;
							break;
						}
					}
				}
				if (index1 >= 0) {
					part1 = alias.substring(0, index1);
					if (index2 > index1) {
						part2 = alias.substring(index2, alias.length());
					} else {
						part2 = "";
					}
					alias = part1 + lvlString + part2;
				}
			}
		}
		return alias == null ? "" : alias;
	}

	/**
	 * Gets the assigned Modifiers for this object, including any common
	 * Modifiers on a parent List/Framework (if available).
	 * 
	 * @return
	 */
	public ArrayList<Modifier> getAllAssignedModifiers() {
		ArrayList<Modifier> vec = new ArrayList<Modifier>();
		vec.addAll(getAssignedModifiers());
		List parent = getParentList();
		if (getMainPower() != null) {
			parent = getMainPower().getParentList();
		}
		if (parent != null) {
			for (Modifier mod : parent.getAssignedModifiers()) {
				if (GenericObject.findObjectByID(vec, mod.getXMLID()) == null) {
					vec.add(mod);
				}
			}
		}
		return vec;
	}

	public int getAPPerEnd() {
		int ret = 10;
		if (HeroDesigner.getActiveHero() != null) {
			ret = HeroDesigner.getActiveHero().getRules().getAPPerEND();
			if (getXMLID().equals("STR")) {
				ret = HeroDesigner.getActiveHero().getRules().getSTRAPPerEND();
			}
		}
		if (apperend >= 0) {
			ret = apperend; // set in template, use this value
		}
		if (usesEND()) {
			return ret;
		} else {
			return 0;
		}
	}

	/**
	 * Returns a Vector of Adder objects representing the assigned Adders for
	 * this object. Each Adder object should be checked for a selected state, as
	 * some parents may be added without being selected in order to properly
	 * account for child Adders.
	 * 
	 * @return
	 */
	public ArrayList<Adder> getAssignedAdders() {
		if (assignedAdders == null) {
			assignedAdders = new ArrayList<Adder>();
		}
		if (availableAdders == null) {
			availableAdders = new ArrayList<Adder>();
		}
		for (Adder ad : availableAdders) {
			if (ad.isRequired()) {
				if (GenericObject.findObjectByID(assignedAdders, ad.getXMLID()) == null) {
					ad = (Adder) ad.clone();
					ad.setSelected(true);
					assignedAdders.add(ad);
				}
			}
		}
		return assignedAdders;
	}

	/**
	 * Returns a Vector of Modifier objects representing the Modifiers assigned
	 * to this object.
	 * 
	 * @return
	 */
	public ArrayList<Modifier> getAssignedModifiers() {
		if (assignedModifiers == null) {
			assignedModifiers = new ArrayList<Modifier>();
		}
		return assignedModifiers;
	}

	/**
	 * Returns a Vector of Adder objects that are currently available to this
	 * object.
	 * 
	 * @return
	 */
	public ArrayList<Adder> getAvailableAdders() {
		if (availableAdders == null) {
			availableAdders = new ArrayList<Adder>();
		}
		ArrayList<Adder> ret = new ArrayList<Adder>();
		ret.addAll(availableAdders);
		if (getSelectedOption() != null) {
			ret.addAll(getSelectedOption().getAvailableAdders());
		}
		if (getTarget() != null
				&& getTarget().equals("HEX")
				&& getDuration() != null
				&& (getDuration().equals("CONSTANT")
						|| getDuration().equals("PERSISTENT") || getDuration()
						.equals("INHERENT"))) {
			if (findObjectByID(assignedAdders, "ALTERABLESIZE") == null && findObjectByID(ret, "ALTERABLESIZE") == null) {
				Adder a = new Adder();
				a.setXMLID("ALTERABLESIZE");
				a.setDisplay("Alterable Size");
				a.setFixedValue(true);
				a.setExclusive(true);
				a.setBaseCost(5);

				ret.add(a);
			}
		}
		return ret;
	}

	/**
	 * Returns a Vector of Modifier objects that are currently available to this
	 * object. Does not take Modifier Intelligence into account.
	 * 
	 * @return
	 */
	public ArrayList<Modifier> getAvailableModifiers() {
		if (availableModifiers == null) {
			availableModifiers = new ArrayList<Modifier>();
		}
		ArrayList<Modifier> ret = new ArrayList<Modifier>();
		ret.addAll(availableModifiers);
		for (Modifier m : ret) {
			m.setParent(this);
		}
		if (getSelectedOption() != null) {
			for (Modifier m : getSelectedOption().getAvailableModifiers()) {
				if (GenericObject.findObjectByID(ret, m.getXMLID()) != null) {
					continue;
				}
				m.setParent(this);
				ret.add(m);
			}
		}
		if (this instanceof Modifier) {
			return ret;
		}
		return ret;
	}

	public double getBaseCost() {
		return baseCost;
	}

	public double getOrigBaseCost() {
		return origBaseCost;
	}

	/**
	 * The main output for the first column of the purchase list. This is
	 * typically the cost of the object.
	 * 
	 * @return
	 */
	public String getColumn1Output() {
		String ret = "" + Rounder.roundUp(getRealCost());
		if (isEquipment() && getPrice() != 0) {
			BigDecimal bd = new BigDecimal(getTotalPrice());
			bd = bd.setScale(HeroDesigner.getActiveHero().getRules()
					.getEquipmentCostDecimalPlaces(), bd.ROUND_HALF_UP);
			ret = "" + bd.toString();
			if (HeroDesigner.getActiveHero().getRules()
					.isEquipmentUnitsPrefix()) {
				ret = HeroDesigner.getActiveHero().getRules()
						.getEquipmentCostUnits()
						+ ret;
			} else {
				ret += HeroDesigner.getActiveHero().getRules()
						.getEquipmentCostUnits();
			}
		} else if (isEquipment()) {
			ret = "";
		}

		ret += getColumn1Suffix();

		return ret;
	}

	public String getColumn1Suffix() {
		if (parent != null) {
			return parent.getColumn1Suffix(this);
		} else {
			return "";
		}
	}

	/**
	 * The main output for the object in the purchase list. This method is often
	 * overridden to provide Writers' Guidelines-specific displays.
	 * 
	 * @return
	 */
	public String getColumn2Output() {
		return getDisplay();
	}

	/**
	 * The main output for the third column of the purchase list. This is
	 * typically the END usage of the object (when present).
	 * 
	 * @return
	 */
	public String getColumn3Output() {
		if (getEndUsage() > 0) {
			return "" + getEndUsage();
		} else {
			return "";
		}
	}

	/**
	 * The applicable defense for the object. This method takes into account
	 * Modifier assigned directly to the object (but not Modifiers placed on a
	 * parent List).
	 * 
	 * @return A String representing the defense against the object (NONE,
	 *         NORMAL, MENTAL, POWER, SPECIAL)
	 */
	public String getDefense() {
		String ret = defense;
		if (GenericObject.findObjectByID(getAllAssignedModifiers(),
				"BASEDONCON") != null) {
			ret = "NORMAL";
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "BOECV") != null) {
			GenericObject mod = GenericObject.findObjectByID(
					getAllAssignedModifiers(), "BOECV");
			if (mod.getSelectedOption() != null
					&& mod.getSelectedOption().getXMLID().equals("STANDARD")) {
				ret = "NORMAL";
			} else {
				ret = "MENTAL";
			}
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "UOO") != null) {
			ret = "POWER";
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "NND") != null) {
			ret = "SPECIAL";
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "AVLD") != null) {
			ret = "SPECIAL";
		}
		return ret;
	}

	/**
	 * The applicable defense for the object. This method takes into account
	 * Modifiers assigned directly to the object (but not Modifiers placed on a
	 * parent List). Any Modifier with the specified XMLID will be ignored. This
	 * is useful in many Modifier Intelligence checks.
	 * 
	 * @return A String representing the defense against the object (NONE,
	 *         NORMAL, MENTAL, POWER, SPECIAL)
	 */
	public String getDefense(String ignoreID) {
		String ret = defense;
		if (!ignoreID.equals("BASEDONCON")
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"BASEDONCON") != null) {
			ret = "NORMAL";
		}
		if (!ignoreID.equals("BOECV")
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"BOECV") != null) {
			GenericObject mod = GenericObject.findObjectByID(
					getAllAssignedModifiers(), "BOECV");
			if (mod.getSelectedOption() != null
					&& mod.getSelectedOption().getXMLID().equals("STANDARD")) {
				ret = "NORMAL";
			} else {
				ret = "MENTAL";
			}
		}
		if (!ignoreID.equals("UOO")
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"UOO") != null) {
			ret = "POWER";
		}
		if (!ignoreID.equals("NND")
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"NND") != null) {
			ret = "SPECIAL";
		}
		if (!ignoreID.equals("AVLD")
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"AVLD") != null) {
			ret = "SPECIAL";
		}
		return ret;
	}

	public String getDefinition() {
		return definition;
	}

	/**
	 * This returns the UI dialog for the object.
	 * 
	 * @param isNew
	 * @param isPower
	 * @return
	 */
	public GenericDialog getDialog(boolean isNew, boolean isPower) {
		return null;
	}

	public String getDisplay() {
		if (display == null || display.trim().length() == 0) {
			display = "";
		}
		String ret = display;
		/*
		 * if (HeroDesigner.getInstance().getPrefs().useAbbreviations() &&
		 * abbreviation != null && abbreviation.trim().length() > 0) ret =
		 * getAbbr();
		 */
		if (ret.toUpperCase().indexOf("[LVL]") >= 0) {
			String part1 = ret.substring(0, ret.toUpperCase().indexOf("[LVL]"));
			String part2 = ret.substring(
					ret.toUpperCase().indexOf("[LVL]") + 5, ret.length());
			String lvlString = ""
					+ NumberFormat.getInstance().format(getLevels());
			if (levelPower != 1) {
				lvlString = ""
						+ NumberFormat.getInstance().format(
								(long) Math.pow(levelPower, getLevels()));
			}
			return part1 + lvlString + part2;
		} else {
			return ret;
		}
	}

	/**
	 * Whether the Active Cost should be displayed in the output.
	 * 
	 * @return
	 */
	public boolean getDisplayActiveCost() {
		return displayActiveCost;
	}

	/**
	 * The duration of the Power ("INSTANT", "NONPERSISTENT", "INHERENT",
	 * "PERSISTENT", "CONTINUOUS")
	 * 
	 * @return
	 */
	public String getDuration() {
		if (duration == null) {
			duration = "";
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "INSTANT") != null) {
			return "INSTANT";
		}
		if (HeroDesigner.getActiveTemplate().is6E()) {
			if (duration.equals("PERSISTENT") || duration.equals("INHERENT")) {
				if (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"NONPERSISTENT") != null) {
					if (GenericObject.findObjectByID(getAllAssignedModifiers(),
							"INSTANT") != null) {
						return "INSTANT";
					} else
						return "CONSTANT";
				}
				if (usesEND()) {
					if (findObjectByID(getAssignedModifiers(),
							"COSTSENDTOMAINTAIN") != null) {
						return duration;
					} else {
						return "CONSTANT";
					}
				} else {
					return duration;
				}
			} else if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"INHERENT") != null) {
				if (usesEND()) {
					if (findObjectByID(getAssignedModifiers(),
							"COSTSENDTOMAINTAIN") != null) {
						return "INHERENT";
					} else {
						return "CONSTANT";
					}
				} else {
					return "INHERENT";
				}
			} else if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"PERSISTENT") != null) {
				if (usesEND()) {
					if (findObjectByID(getAssignedModifiers(),
							"COSTSENDTOMAINTAIN") != null) {
						return "PERSISTENT";
					} else {
						return "CONSTANT";
					}
				} else {
					return "PERSISTENT";
				}
			} else if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"CONTINUOUS") != null) {
				return "CONSTANT";
			} else if (duration.equals("CONSTANT")) {
				if (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"INSTANT") != null) {
					return "INSTANT";
				} else {
					return "CONSTANT";
				}
			} else {
				return "INSTANT";
			}
		} else {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"NONPERSISTENT") != null
					|| (duration.equals("PERSISTENT") || duration
							.equals("INHERENT")) && usesEND()) {
				return "CONSTANT";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"INHERENT") != null) {
				return "INHERENT";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"PERSISTENT") != null) {
				return "PERSISTENT";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"CONTINUOUS") != null) {
				return "CONSTANT";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"NONPERSISTENT") != null) {
				return "CONSTANT";
			}
			return duration.trim().toUpperCase();
		}
	}

	public String getOrigDuration() {
		return duration;
	}

	/**
	 * Returns the END usage for the object. Takes into account all applicable
	 * Modifiers (both those assigned directly to the object as well as those
	 * assigned to any parent List/Framework).
	 * 
	 * @return
	 */
	public int getEndUsage() {
		int denom = getAPPerEnd();
		double ret = 0;
		double active = getActiveCost();
		double mult = 1;
		ArrayList<Modifier> vec = new ArrayList<Modifier>();
		vec.addAll(getAssignedModifiers());
		List parent = getParentList();
		if (getMainPower() != null) {
			parent = getMainPower().getParentList();
		}
		if (parent != null) {
			ArrayList<Modifier> par = parent.getAssignedModifiers();
			for (Modifier mod : par) {
				if (getParentList() instanceof ElementalControl) {
					vec.add(mod);
				} else {
					vec.add(mod);
				}
			}
		}

		/*
		 * if (findObjectByID(vec, "UOO") != null) { denom =
		 * HeroDesigner.getActiveHero().getRules().getAPPerEND(); }
		 */
		if (GenericObject.findObjectByID(vec, "CHARGES") != null) {
			denom = 0;
		}
		if (GenericObject.findObjectByID(vec, "COSTSEND") != null) {
			denom = HeroDesigner.getActiveHero().getRules().getAPPerEND();
			GenericObject o = findObjectByID(vec, "COSTSEND");
			if (o.getSelectedOption() != null
					&& o.getSelectedOption().getXMLID().equals("HALFEND")) {
				mult = .5;
			}
		}
		if (GenericObject.findObjectByID(vec, "REDUCEDEND") != null) {
			GenericObject mod = GenericObject.findObjectByID(vec, "REDUCEDEND");
			if (mod.getSelectedOption().getXMLID().equals("HALFEND")) {
				mult = .5;
			} else {
				denom = 0;
			}
			active = getActiveCost(mod.getXMLID());
		}
		if (GenericObject.findObjectByID(vec, "COSTSENDONLYTOACTIVATE") != null) {
			GenericObject mod = GenericObject.findObjectByID(vec,
					"COSTSENDONLYTOACTIVATE");
			active = getActiveCost(mod.getXMLID());
		}
		if (GenericObject.findObjectByID(vec, "INCREASEDEND") != null) {
			GenericObject mod = GenericObject.findObjectByID(vec,
					"INCREASEDEND");
			if (GenericObject.findObjectByID(mod.getAssignedAdders(),
					"CIRCUMSTANCE") == null) {
				mult = Rounder.roundHalfUp(mod.getSelectedOption()
						.getLevelValue());
			}
		}
		if (getTypes().contains("DEFENSE")
				&& HeroDesigner.getActiveHero() != null) {
			if (GenericObject.findObjectByID(HeroDesigner.getActiveHero()
					.getPowers(), "AUTOMATON") != null) {
				GenericObject auto = GenericObject.findObjectByID(HeroDesigner
						.getActiveHero().getPowers(), "AUTOMATON");
				if (auto.getSelectedOption().getXMLID().toUpperCase()
						.startsWith("NOSTUN")) {
					active = active
							/ ((Automaton) auto).getDefenseCostMultiplier();
				}
			}
		}

		if (denom != 0) {
			ret = active / denom;
		}
		if (Rounder.roundHalfDown(ret) == 0 && active > 0 && denom != 0) {
			ret = 1;
		}
		ret = Rounder.roundHalfDown(ret);
		ret = ret * mult;
		if (Rounder.roundHalfDown(ret) == 0 && active > 0 && denom != 0) {
			ret = 1;
		}
		if (ret < 0) {
			ret = 0;
		}
		return (int) Rounder.roundHalfDown(ret);
	}

	/**
	 * Whether a Skill/Perk Enhancer has been applied to the object.
	 * 
	 * @return
	 */
	public Enhancer getEnhancerApplied() {
		return enhancerApplied;
	}

	/**
	 * Returns a Vector of Strings for example values for the input field.
	 * 
	 * @return
	 */
	public ArrayList<String> getExamples() {
		return examples;
	}

	public void setTextOutput(String text) {
		String check = getTextOutput();
		String check2 = getColumn2Output();
		if (check2.equals(text)) {
			textOutput = null;
			return;
		}
		textOutput = text;
		if (!check.equals(getTextOutput())) {
			HeroDesigner.getActiveHero().setDirty(true);
		}
	}

	public String getTextOutput() {
		if (textOutput == null || textOutput.trim().length() == 0) {
			return getColumn2Output();
		} else
			return textOutput;
	}

	/**
	 * The basic save XML for all objects is constructed here.
	 * 
	 * @return
	 */
	public final Element getGeneralSaveXML() {
		Element root = new Element("GENERIC_OBJECT");
		root.setAttribute("XMLID", getXMLID());
		root.setAttribute("ID", "" + getID());
		root.setAttribute("BASECOST", "" + getBaseCost());
		root.setAttribute("LEVELS", "" + getLevels());
		root.setAttribute("ALIAS", "" + getAlias());
		if (textOutput != null && textOutput.trim().length() > 0) {
			root.setAttribute("TEXT", "" + getTextOutput());
		}
		root.setAttribute("POSITION", "" + getPosition());
		root.setAttribute("MULTIPLIER", "" + multiplier);
		root.setAttribute("GRAPHIC", getGraphic());
		root.setAttribute("COLOR", getColor());
		root.setAttribute("SFX", getSFX());
		if (getUseENDReserve()) {
			root.setAttribute("USE_END_RESERVE", "Yes");
		}
		root.setAttribute("SHOW_ACTIVE_COST",
				(displayActiveCost ? "Yes" : "No"));
		if (isEquipment()) {
			root.setAttribute("PRICE", "" + price);
			root.setAttribute("WEIGHT", "" + weight);
			root.setAttribute("CARRIED", carried ? "Yes" : "No");
		}
		if (getSelectedOption() != null) {
			root.setAttribute("OPTION", getSelectedOption().getXMLID());
			root.setAttribute("OPTIONID", getSelectedOption().getXMLID());
			root.setAttribute("OPTION_ALIAS", getSelectedOption().getAlias());
		}
		root.setAttribute("LEVELS", "" + getLevels());
		Element notes = new Element("NOTES");
		notes.setText(getNotes());
		root.addContent(notes);
		root.setAttribute("INCLUDE_NOTES_IN_PRINTOUT",
				(isIncludeNotesInPrintout() ? "Yes" : "No"));
		if (getParentID() > 0) {
			root.setAttribute("PARENTID", "" + getParentID());
			if (getParentList() instanceof Multipower) {
				root.setAttribute("ULTRA_SLOT", isUltra() ? "Yes" : "No");
			}
		}
		if (getName() != null) {
			root.setAttribute("NAME", getName());
		}
		if (getInput() != null && getInput().trim().length() > 0) {
			root.setAttribute("INPUT", getInput() == null ? "" : getInput());
		}
		getAdderSaveXML(root);
		getModifierSaveXML(root);
		return root;
	}

	/**
	 * Returns the id of the object. This id should always be unique.
	 * 
	 * @return
	 */
	public long getID() {
		return id;
	}

	/**
	 * User input.
	 * 
	 * @return
	 */
	public String getInput() {
		return input;
	}

	public String getInputLabel() {
		return inputLabel;
	}

	public double getLevelCost() {
		return levelCost;
	}

	public int getLevelMultiplier() {
		return levelMultiplier;
	}

	public int getLevelPower() {
		return levelPower;
	}

	public int getLevels() {
		int ret = levels;
		if (ret < getMinimumLevel()) {
			ret = getMinimumLevel();
		}
		return ret;
	}

	public String getLevelsLabel() {
		if (levelsLbl == null || levelsLbl.trim().length() == 0) {
			levelsLbl = "Levels";
		}
		return levelsLbl;
	}

	public double getLevelValue() {
		return levelValue;
	}

	/**
	 * If the object is assigned to a Compound Power, this will return a
	 * reference to the main CompoundPower. Null otherwise.
	 * 
	 * @return
	 */
	public CompoundPower getMainPower() {
		return mainPower;
	}

	public double getMaxCost() {
		return maxCost;
	}

	public int getMaxLevel() {
		return maxLevel;
	}

	public double getMinimumCost() {
		return minimumCost;
	}

	public int getMinimumLevel() {
		return minimumLevel;
	}

	/**
	 * Internal utility to just get the assignedModifiers vector...no other
	 * tinkering. Subclasses typically override getAssignedModifiers to provide
	 * weird functionality...
	 * 
	 * @return
	 */
	protected ArrayList<Modifier> getModifiers() {
		if (assignedModifiers == null) {
			assignedModifiers = new ArrayList<Modifier>();
		}
		return assignedModifiers;
	}

	protected void getModifierSaveXML(Element root) {
		for (int i = 0; i < getModifiers().size(); i++) {
			GenericObject mod = getModifiers().get(i);
			root.addContent(mod.getSaveXML());
		}
	}

	public String getModifierString() {
		String ret = "";
		ArrayList<Modifier> vec = new ArrayList<Modifier>();
		vec.addAll(getAssignedModifiers());
		if (getParentList() != null
				&& HeroDesigner.getInstance().getPrefs()
						.showCommonLimitations()) {
			ArrayList<Modifier> parentMods = getParentList()
					.getAssignedModifiers();
			LOOP: for (int i = 0; i < parentMods.size(); i++) {
				Modifier mod = parentMods.get(i);
				if (mod.getTypes().contains("VPP")) {
					continue LOOP;
				}
				if (mod.getXMLID().equals("CHARGES")
						&& getParentList() instanceof Multipower) {
					continue LOOP;
				}
				if ((mod.getTotalValue() < 0 || getParentList() instanceof VariablePowerPool)
						&& (GenericObject.findObjectByID(assignedModifiers,
								mod.getXMLID()) == null
								|| mod.getXMLID().equals("GENERIC_OBJECT")
								|| mod.getXMLID().equals("CUSTOM_MODIFIER") || mod
								.getXMLID().equals("MODIFIER"))) {
					vec.add(mod);
				}
			}
		}
		Collections.sort(vec, new Comparator() {
			public int compare(Object o1, Object o2) {
				if (o1 instanceof Modifier && o2 instanceof Modifier) {
					Modifier mod1 = (Modifier) o1;
					Modifier mod2 = (Modifier) o2;
					return (int) ((mod1.getTotalValue() - mod2.getTotalValue()) * 100d);
				} else {
					return -1;
				}
			}
		});
		for (Modifier m : vec) {
			if (m.getTotalValue() >= 0 && m.displayInString()
					&& !m.isLimitation()) {
				ret += ", ";
				ret += m.getColumn2Output();
			}
		}
		if (displayActiveCost
				&& (getActiveCost() != getTotalCost() || getRealCost() != getTotalCost())) {
			ret += " (" + Rounder.roundUp(getActiveCost()) + " Active Points)";
		}
		int numNegative = 0;
		for (Modifier m : vec) {
			if ((m.getTotalValue() < 0 || m.isLimitation())
					&& m.displayInString()) {
				numNegative++;
				if (numNegative == 1) {
					ret += "; ";
				} else {
					ret += ", ";
				}
				ret += m.getColumn2Output();
			}
		}
		return ret;
	}

	public double getMultiplier() {
		double ret = multiplier;
		if (ret == 1 && getMainPower() != null
				&& getMainPower().getMultiplier() != 1) {
			ret = getMainPower().getMultiplier();
		} else if (ret == 1 && getParentList() != null) {
			ret = getParentList().getMultiplier();
		}
		return ret;
	}

	public String getName() {
		if (name == null) {
			name = "";
		}
		return name;
	}

	/**
	 * Returns the main text display without any name attribute. This is useful
	 * during export, where the name needs to be formatted differently,
	 * depending on the format of the export.
	 * 
	 * @return
	 */
	public String getNamelessColumn2Output() {
		String holder = getName();
		setName("");
		String ret = getTextOutput();
		setName(holder);
		return ret;
	}

	public String getOutputNotes() {
		if (getQuantity() == 1)
			return notes;
		if (notes == null || notes.trim().length() == 0) {
			return "(x" + getQuantity() + " number of items)";
		} else {
			return "(x" + getQuantity() + " number of items)\n\n" + notes;
		}
	}

	public String getNotes() {
		return notes;
	}

	public String getOptionLabel() {
		if (optionLbl == null || optionLbl.trim().length() == 0) {
			optionLbl = "Options";
		}
		return optionLbl;
	}

	/**
	 * Returns a Vector of Adder objects representing any options available to
	 * this object.
	 * 
	 * @return
	 */
	public ArrayList<Adder> getOptions() {
		if (options == null) {
			options = new ArrayList<Adder>();
		}
		return options;
	}

	/**
	 * Returns the original (unmodified) AP per END setting.
	 * 
	 * @return
	 */
	public int getOrigAPPerEnd() {
		int ret = 10;
		if (HeroDesigner.getActiveHero() != null) {
			ret = HeroDesigner.getActiveHero().getRules().getAPPerEND();
			if (getXMLID().equals("STR")) {
				ret = HeroDesigner.getActiveHero().getRules().getSTRAPPerEND();
			}
		}
		if (!usesEND) {
			ret = 0;
		}
		if (apperend >= 0) {
			ret = apperend;
		}
		return ret;
	}

	/**
	 * If the object is in a List, this returns the List's ID. -1 if the object
	 * is not in a List.
	 * 
	 * @return
	 */
	public long getParentID() {
		return parentID;
	}

	/**
	 * If the object is in a List, this returns the object's parent. Null
	 * otherwise.
	 * 
	 * @return
	 */
	public List getParentList() {
		return parent;
	}

	/**
	 * Getter for the position of the element within its list.
	 * 
	 * @return an int representing the position of the element in its
	 *         (appropriate) list.
	 */
	public int getPosition() {
		return position;
	}

	/**
	 * Returns the price in the units specified in the campaign rules.
	 * 
	 * @return
	 */
	public double getPrice() {
		if (HeroDesigner.getActiveHero() == null) {
			return price;
		} else {
			return price
					/ HeroDesigner.getActiveHero().getRules()
							.getEquipmentCostConversion();
		}
	}

	public double getTotalPrice() {
		return getQuantity() * getPrice();
	}

	/**
	 * Returns a String ("Yes" or "No") defining whether the Power is Ranged by
	 * default.
	 * 
	 * @return
	 */
	public String getRange() {
		return range;
	}

	/**
	 * Returns a display value for the Range of the Power. Takes into account
	 * Modifiers assigned directly to the Power (not common Modifiers from a
	 * List/Framework)
	 * 
	 * @return
	 */
	public String getRangeDisplay() {
		if (getRangeValue() < 0) {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"EYECONTACTREQUIRED") != null) {
				return "varies";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"NORANGE") != null) {
				return "0";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"LIMITEDRANGE") != null) {
				return "var.";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"RANGEBASEDONSTR") != null) {
				return "var.";
			}
			return "LOS";
		} else if (getRangeValue() == 0) {
			return "";
		} else {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"NORANGE") != null) {
				return "0";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"LIMITEDRANGE") != null) {
				return "var.";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"RANGEBASEDONSTR") != null) {
				return "var.";
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"RANGED") != null) {
				GenericObject ranged = GenericObject.findObjectByID(
						getAllAssignedModifiers(), "RANGED");
				if (ranged.getSelectedOption() != null
						&& !(ranged.getSelectedOption().getXMLID()
								.equals("RANGED") || ranged.getSelectedOption()
								.getXMLID().equals("RANGE"))) {
					return "var.";
				}
			}
			NumberFormat format = NumberFormat.getInstance();
			if (HeroDesigner.getActiveTemplate().is6E()) {
				return format.format(getRangeValue()) + "m";
			} else {
				return format.format(getRangeValue()) + "\"";
			}
		}
	}

	/**
	 * 0 if no range, val if ranged, -1 if LOS
	 * 
	 * @return the range
	 */
	public int getRangeValue() {

		if (range == null) {
			range = "No";
		}
		if ((range.trim().toUpperCase().equals("YES") || GenericObject
				.findObjectByID(getAllAssignedModifiers(), "BASEDONCON") != null)
				&& (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"MOBILE") == null || HeroDesigner.getActiveTemplate()
						.is6E())) {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"NORANGE") != null) {
				return 0;
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"DAMAGESHIELD") != null) {
				return 0;
			}
			if (GenericObject
					.findObjectByID(getAllAssignedModifiers(), "BOECV") != null
					&& GenericObject.findObjectByID(getAllAssignedModifiers(),
							"NORMALRANGE") == null) {
				return -1;
			}
			if (GenericObject.findObjectByID(getAssignedModifiers(),
					"INCREASEDMAXRANGE") != null) {
				Modifier inc = (Modifier) GenericObject.findObjectByID(
						getAssignedModifiers(), "INCREASEDMAXRANGE");
				int lvls = inc.getLevels();
				if (lvls > 0) {
					double val = inc.getLevelValue();
					int power = inc.getLevelPower();
					double value = Math.pow(power, (lvls * val));
					if (value > 0) {
						getAssignedModifiers().remove(inc);
						double modVal = lvls * inc.getLevelCost();
						int realVal = getRangeValue();
						// don't include this mod in the total
						getAssignedModifiers().add(inc);
						return (int) Rounder.roundHalfDown((value * realVal));
					}
				}
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(), "LOS") != null) {
				return -1;
			}

			int ret = (int) Rounder.roundHalfUp(this.getActiveCost() * 5);
			if (HeroDesigner.getActiveTemplate().is6E()) {
				double tot = getTotalCost();
				for (Adder ad : getAssignedAdders()) {
					if (!ad.includeInBase() && !ad.isCustom()) {
						tot -= ad.getTotalCost();
					}
				}

				ret = (int) Rounder.roundHalfUp(tot * 10);
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(), "UOO") != null) {
				GenericObject mod = GenericObject.findObjectByID(
						getAllAssignedModifiers(), "UOO");
				if (mod.getSelectedOption() != null
						&& mod.getSelectedOption().equals("UAA")) {
					if (GenericObject.findObjectByID(getAllAssignedModifiers(),
							"LOS") != null) {
						return -1;
					} else if (GenericObject.findObjectByID(
							getAllAssignedModifiers(), "RANGED") != null) {
						return ret;
					} else {
						return 0;
					}
				} else {
					return ret;
				}
			}
			return ret;
		} else if (range.trim().toUpperCase().equals("LOS")
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"BASEDONCON") == null
				&& (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"MOBILE") == null || HeroDesigner.getActiveTemplate()
						.is6E())) {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"DAMAGESHIELD") != null) {
				return 0;
			}
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"NORMALRANGE") != null) {
				if (GenericObject.findObjectByID(getAssignedModifiers(),
						"INCREASEDMAXRANGE") != null) {
					Modifier inc = (Modifier) GenericObject.findObjectByID(
							getAssignedModifiers(), "INCREASEDMAXRANGE");
					int lvls = inc.getLevels();
					if (lvls > 0) {
						double val = inc.getLevelValue();
						int power = inc.getLevelPower();
						double value = Math.pow(power, (lvls * val));
						if (value > 0) {
							getAssignedModifiers().remove(inc);
							double modVal = lvls * inc.getLevelCost();
							int realVal = getRangeValue();
							// don't include this mod in the total
							getAssignedModifiers().add(inc);
							return (int) Rounder
									.roundHalfDown((value * realVal));
						}
					}
				}
				if (HeroDesigner.getActiveTemplate().is6E()) {
					double tot = getTotalCost();
					for (Adder ad : getAssignedAdders()) {
						if (!ad.includeInBase() && !ad.isCustom()) {
							tot -= ad.getTotalCost();
						}
					}

					return (int) Rounder.roundHalfUp(tot * 10);

				} else {
					return (int) Rounder
							.roundHalfDown(this.getActiveCost() * 5);
				}
			} else if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"SKINCONTACTREQUIRED") != null) {
				return 0;
			} else if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"UOO") != null) {
				GenericObject mod = GenericObject.findObjectByID(
						getAllAssignedModifiers(), "UOO");
				if (mod.getSelectedOption() != null
						&& mod.getSelectedOption().equals("UAA")) {
					if (GenericObject.findObjectByID(getAllAssignedModifiers(),
							"LOS") != null) {
						return -1;
					} else if (GenericObject.findObjectByID(
							getAllAssignedModifiers(), "RANGED") != null) {
						if (HeroDesigner.getActiveTemplate().is6E()) {
							double tot = getTotalCost();
							for (Adder ad : getAssignedAdders()) {
								if (!ad.includeInBase() && !ad.isCustom()) {
									tot -= ad.getTotalCost();
								}
							}
							return (int) Rounder.roundHalfDown(tot * 10);
						} else {
							return (int) Rounder.roundHalfDown(this
									.getActiveCost() * 5);
						}
					} else {
						return 0;
					}
				} else {
					return -1;
				}
			} else {
				return -1;
			}
		} else {
			if (GenericObject.findObjectByID(getAllAssignedModifiers(),
					"RANGED") != null
					|| GenericObject.findObjectByID(getAllAssignedModifiers(),
							"RANGEDRECOMBINATION") != null) {
				if (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"BOECV") != null) {
					return -1;
				}
				if (GenericObject.findObjectByID(getAllAssignedModifiers(),
						"LOS") != null) {
					return -1;
				}
				if (GenericObject.findObjectByID(getAssignedModifiers(),
						"INCREASEDMAXRANGE") != null) {
					Modifier inc = (Modifier) GenericObject.findObjectByID(
							getAssignedModifiers(), "INCREASEDMAXRANGE");
					int lvls = inc.getLevels();
					if (lvls > 0) {
						double val = inc.getLevelValue();
						int power = inc.getLevelPower();
						double value = Math.pow(power, (lvls * val));
						if (value > 0) {
							getAssignedModifiers().remove(inc);
							double modVal = lvls * inc.getLevelCost();
							int realVal = getRangeValue();
							// don't include this mod in the total
							getAssignedModifiers().add(inc);
							return (int) Rounder
									.roundHalfDown((value * realVal));
						}
					}
				}
				if (HeroDesigner.getActiveTemplate().is6E()) {
					double tot = getTotalCost();
					for (Adder ad : getAssignedAdders()) {
						if (!ad.includeInBase() && !ad.isCustom()) {
							tot -= ad.getTotalCost();
						}
					}
					return (int) Rounder.roundHalfDown(tot * 10);
				} else {
					return (int) Rounder
							.roundHalfDown(this.getActiveCost() * 5);
				}
			}
			return 0;
		}
	}

	/**
	 * Returns the Real/actual cost for the object. This takes into account any
	 * discounts from a List/Framework.
	 * 
	 * @return
	 */
	public double getRealCost() {
		if (parent != null) {
			return parent.getRealCostForChild(this);
		} else {
			return getRealCostPreList();
		}
	}

	/**
	 * Returns the Real/actual cost for the object before any discounts for
	 * being in a List/Framework are taken into account.
	 * 
	 * @return
	 */
	public double getRealCostPreList() {
		enhancerApplied = null;
		boolean negativeCustomAddersApplied = false;
		double active = getActiveCost();
		boolean limitationsApplied = false;
		double limitationTotal = 0d;
		if (this instanceof MentalAwareness) {
			negativeCustomAddersApplied = true;
		} else if (this instanceof TransportFamiliarity) {
			negativeCustomAddersApplied = getAssignedAdders().size() > 0;
		} else if (this instanceof WeaponFamiliarity) {
			negativeCustomAddersApplied = true;
		} else if (this instanceof WeaponElement) {
			negativeCustomAddersApplied = true;
		}

		for (int i = 0; i < getAssignedModifiers().size(); i++) {
			Modifier mod = getAssignedModifiers().get(i);
			if (mod.getTotalValue() < 0) {
				limitationTotal += mod.getTotalValue();
				limitationsApplied = true;
			}
		}
		if (!negativeCustomAddersApplied) {
			ArrayList<Adder> availAds = getAvailableAdders();
			for (int i = 0; i < getAssignedAdders().size(); i++) {
				Adder ad = getAssignedAdders().get(i);
				if ((GenericObject.findObjectByID(availAds, ad.getXMLID()) == null || ad
						.getXMLID().equalsIgnoreCase("GENERIC_OBJECT"))
						&& ad.getTotalCost() <= 0) {
					// custom adder
					negativeCustomAddersApplied = true;
					break;
				}
			}
		}
		List parent = getParentList();
		if (getMainPower() != null) {
			parent = getMainPower().getParentList();
		}
		if (parent != null) {
			ArrayList<Modifier> parentMods = parent.getAssignedModifiers();
			LOOP: for (int i = 0; i < parentMods.size(); i++) {
				Modifier mod = parentMods.get(i);
				if (mod.getTypes().contains("VPP")) {
					continue LOOP;
				}
				if (mod.getXMLID().equals("CHARGES")
						&& getParentList() instanceof Multipower) {
					continue LOOP;
				}
				if (mod.getTotalValue() < 0
						&& (GenericObject.findObjectByID(assignedModifiers,
								mod.getXMLID()) == null
								|| mod.getXMLID().equals("GENERIC_OBJECT")
								|| mod.getXMLID().equals("CUSTOM_MODIFIER") || mod
								.getXMLID().equals("MODIFIER"))) {
					limitationTotal += mod.getTotalValue();
					limitationsApplied = true;
				}
			}
		}
		double ret = active / (1d + Math.abs(limitationTotal));
		if (limitationsApplied) {
			ret = Rounder.roundHalfDown(ret);
		}
		if (getTypes() != null && getTypes().size() > 0
				&& !(this instanceof Enhancer)
				&& HeroDesigner.getActiveHero() != null
				&& !(this instanceof Skill && ((Skill) this).levelsOnly())) {
			ArrayList<GenericObject> enhancers = HeroDesigner.getActiveHero()
					.getSkills();
			OUTER: for (int i = 0; i < enhancers.size(); i++) {
				if (enhancers.get(i) instanceof Enhancer) {
					Enhancer enhancer = (Enhancer) enhancers.get(i);
					for (String type : getTypes()) {
						if (enhancer.appliesToType(type)) {
							enhancerApplied = enhancer;
							if (!enhancer.getObjects().contains(this)) {
								enhancer.getObjects().add(this);
							}
							// just take the first match and leave it at
							// that...
							if (ret > enhancer.getCostSavings()) {
								ret -= enhancer.getCostSavings();
							} else if (ret > 0) {
								ret = 1;
							}
							break OUTER;
						}
					}
				}
			}
			enhancers = HeroDesigner.getActiveHero().getPerks();
			OUTER: for (int i = 0; i < enhancers.size(); i++) {
				if (enhancers.get(i) instanceof Enhancer) {
					Enhancer enhancer = (Enhancer) enhancers.get(i);
					for (String type : getTypes()) {
						if (enhancer.appliesToType(type)) {
							enhancerApplied = enhancer;
							// just take the first match and leave it at
							// that...
							if (ret > enhancer.getCostSavings()) {
								ret -= enhancer.getCostSavings();
							} else if (ret > 0) {
								ret = 1;
							}
							break OUTER;
						}
					}
				}
			}
		}
		if (!(this instanceof Adder) && !(this instanceof Modifier)
				&& !(this instanceof Disadvantage)
				&& !(this instanceof EnduranceReserveRecovery)) {
			if (ret < 1
					&& (active > 0 || (getLevels() > 0
							&& getAssignedAdders().size() == 0 && getBaseCost() >= 0))
					&& !negativeCustomAddersApplied) {
				ret = 1;
			}
			if (ret < 1 && !negativeCustomAddersApplied) {
				ret = 1;
			}
		}
		if (HeroDesigner.getActiveHero() != null
				&& HeroDesigner.getActiveHero().getRules().multiplierAllowed()
				&& getMultiplier() != 1) {
			ret = ret * getMultiplier();
			ret = Rounder.roundHalfDown(ret);
		} else if (HeroDesigner.getActiveHero() != null
				&& HeroDesigner.getActiveHero().getRules().multiplierAllowed()
				&& parent != null && parent.getMultiplier() != 1) {
			ret = ret * parent.getMultiplier();
			ret = Rounder.roundHalfDown(ret);
		}
		if (getQuantity() > 1) {
			double q = (double) getQuantity();
			int doublings = 0;
			while (q > 1d) {
				doublings += 1;
				q = q / 2d;
			}
			ret += doublings * 5;
		}
		return ret;
	}

	public Element getSaveXML() {
		Element root = new Element("GENERIC_OBJECT");
		return root;
	}

	public Adder getSelectedOption() {
		if (selectedOption == null && options != null && options.size() > 0) {
			selectedOption = options.get(0);
		}
		return selectedOption;
	}

	/**
	 * By default, this simply returns the toString() representation of the
	 * object.
	 * 
	 * @return
	 */
	public String getSortingValue() {
		return toString();
	}

	/**
	 * This is used internally to HeroDesigner to produce an HTML-formatted
	 * display for the second column of the purchase list. The integer that is
	 * passed in is the current width of the second column of the purchase list
	 * (in pixels).
	 * 
	 * @param width
	 * @return
	 */
	public String getTableColumn2Output(int width) {
		if (this instanceof CompoundPower) {
			CompoundPower cp = (CompoundPower) this;
			for (GenericObject o : cp.getPowers()) {
				if (o.appAdjusted) {
					appAdjusted = true;
					break;
				}
			}
		}
		String color = "";
		if (appAdjusted) {
			color = " color=\"red\" ";
		} else if (!(getTextOutput().equals(getColumn2Output()))) {
			color = " color=\"blue\" ";
		}
		String ret = "<html><table width=" + width
				+ " cellpadding=0 cellspacing=0 border=0><tr>";
		String col2 = getTextOutput();
		if (getParentList() != null) {
			ret += "<td width=\"35\" valign=\"top\" align=\"right\"" + color
					+ ">&nbsp;" + getParentList().getColumn2Prefix(this).trim()
					+ "&nbsp;&nbsp;</td>";
			ret += "<td valign=\"top\" align=\"left\" colspan=2 width="
					+ (width - 35) + color + ">" + col2
					+ getParentList().getColumn2Suffix(this) + "</td></tr>";
		} else {
			ret += "<td valign=\"top\" align=\"left\" colspan=3 width=" + width
					+ color + ">" + col2 + "</td></tr>";
		}
		if (getNotes() != null && getNotes().trim().length() > 0
				&& isIncludeNotesInPrintout()) {
			String temp = getOutputNotes();
			temp = temp.replaceAll("<", "&lt;");
			temp = temp.replaceAll(">", "&gt;");
			ret += "<tr><td></td><td width=\"10\"></td><td valign=\"top\" align=\"left\" width="
					+ (getParentList() != null ? width - 45 : width - 10)
					+ color + "><b>Notes:&nbsp;</b>" + temp + "</td></tr>";
		} else if (getQuantity() > 1) {
			String temp = "(x" + getQuantity() + " number of items)";
			ret += "<tr><td></td><td width=\"10\"></td><td valign=\"top\" align=\"left\" width="
					+ (getParentList() != null ? width - 45 : width - 10)
					+ color + "><b>Notes:&nbsp;</b>" + temp + "</td></tr>";
		}
		ret += "</table></html>";
		return ret;
	}

	/**
	 * Returns the target of the object. This method takes into account
	 * Modifiers assigned directly to the object, but not Modifier that are on a
	 * parent List/Framework.
	 * 
	 * @return The target of the object (DCV, ECV, HEX, SELFONLY);
	 */
	public String getTarget() {
		String ret = target;
		ArrayList<Modifier> all = getAllAssignedModifiers();
		if (GenericObject.findObjectByID(all, "BASEDONCON") != null) {
			ret = "DCV";
		}
		if (GenericObject.findObjectByID(all, "UOO") != null) {
			ret = "DCV";
		}
		if (GenericObject.findObjectByID(all, "BOECV") != null) {
			ret = "ECV";
		}
		if (GenericObject.findObjectByID(all, "AOE") != null) {
			ret = "HEX";
		}
		if (GenericObject.findObjectByID(all, "EXPLOSION") != null) {
			ret = "HEX";
		}
		if (GenericObject.findObjectByID(all, "SELFONLY") != null
				&& !(this instanceof MentalIllusions)) {
			ret = "SELFONLY";
		}
		return ret;
	}

	/**
	 * Returns the total cost of the object, before any Modifiers are applied.
	 * 
	 * @return
	 */
	public double getTotalCost() {
		double total = getBaseCost();
		ArrayList<Adder> availAds = getAvailableAdders();
		if (getLevelValue() != 0) {
			double lvls = Math.floor(getLevels() / getLevelValue());
			if (getLevels() % getLevelValue() != 0 && getLevelValue() > 1) {
				lvls += 1;
			}
			total += lvls * getLevelCost();
			if (getLevelCost() < getLevelValue()) {
				if (total > 0 && total < 1) {
					total = 1;
				} else {
					total = Rounder.roundHalfDown(total);
				}
			}
		}
		for (Adder ad : getAssignedAdders()) {
			if (ad.isRequired()) {
				total += ad.getRealCost();
			}
		}
		for (Adder ad : getAssignedAdders()) {
			if (!ad.isRequired()
					&& GenericObject.findObjectByID(availAds, ad.getXMLID()) != null) {
				total += ad.getRealCost();
			}
		}
		if (total < getMinimumCost() && isMinSet()) {
			total = getMinimumCost();
		} else if (total > getMaxCost() && isMaxSet()) {
			total = getMaxCost();
		}
		for (Adder ad : getAssignedAdders()) {

			if (!ad.isRequired()
					&& GenericObject.findObjectByID(availAds, ad.getXMLID()) == null) {
				total += ad.getRealCost();

			}
		}
		if (getTypes().contains("DEFENSE")
				&& HeroDesigner.getActiveHero() != null) {
			if (GenericObject.findObjectByID(HeroDesigner.getActiveHero()
					.getPowers(), "AUTOMATON") != null) {
				Automaton auto = (Automaton) GenericObject.findObjectByID(
						HeroDesigner.getActiveHero().getPowers(), "AUTOMATON");
				if (auto.getSelectedOption().getXMLID().toUpperCase()
						.startsWith("NOSTUN")) {
					total = total * auto.getDefenseCostMultiplier();
				}
			}
		}
		return total;
	}

	/**
	 * Returns a Vector of Strings containing the types that the object belongs
	 * to.
	 * 
	 * @return
	 */
	public ArrayList<String> getTypes() {
		if (types == null) {
			types = new ArrayList<String>();
		}
		ArrayList<String> ret = (ArrayList<String>) types.clone();
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "UOO") != null) {
			GenericObject uoo = GenericObject.findObjectByID(
					getAllAssignedModifiers(), "UOO");
			if (uoo.getSelectedOption() != null
					&& uoo.getSelectedOption().getXMLID().equals("UAA")) {
				ret.add("ATTACK");
			}
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "BOECV") != null) {
			ret.add("MENTAL");
		}
		if (GenericObject.findObjectByID(getAllAssignedModifiers(), "ABSORPTIONASDEFENSE") != null) {
			ret.add("DEFENSE");
		}
		return ret;
	}

	/**
	 * Whether to use an Endurance Reserve or Personal END.
	 * 
	 * @return
	 */
	public boolean getUseENDReserve() {
		if (HeroDesigner.getActiveHero() == null) {
			return false;
		}
		if (GenericObject.findObjectByID(HeroDesigner.getActiveHero()
				.getPowers(), "ENDURANCERESERVE") == null
				&& GenericObject.findObjectByID(HeroDesigner.getActiveHero()
						.getEquipment(), "ENDURANCERESERVE") == null) {
			return false;
		}
		if (getEndUsage() == 0) {
			return false;
		} else {
			return useENDReserve;
		}
	}

	/**
	 * Returns the weight of the object in the format currently selected under
	 * AppPrefs.
	 * 
	 * @see com.hero.AppPrefs
	 * @return
	 */
	public double getWeight() {
		if (!HeroDesigner.getInstance().getPrefs().isMetric()) {
			return weight;
		} else {
			return weight * 453.5924 / 1000; // lbs -> kg
		}
	}

	public double getTotalWeight() {
		return getQuantity() * getWeight();
	}

	/**
	 * Returns the XMLID of the object. This method will translate the id from
	 * old v1 values as necessary.
	 * 
	 * @return
	 */
	public String getXMLID() {
		if (xmlID == null) {
			xmlID = "GENERIC_OBJECT";
		}
		Hashtable<String, String> translate = GenericObject.getIDTranslations();
		if (translate.get(xmlID) != null) {
			return translate.get(xmlID);
		} else {
			return xmlID;
		}
	}

	/**
	 * Returns true of the object is included in the active template. False
	 * otherwise.
	 * 
	 * @return
	 */
	public boolean includedInTemplate() {
		if (!includedInTemplate) {
			return false;
		} else if (HeroDesigner.getInstance().getPrefs().getSources().size() > 0) {
			for (String s : HeroDesigner.getInstance().getPrefs().getSources()) {
				if (sources.contains(s)) {
					return true;
				}
			}
			return false;
		} else {
			return true;
		}
	}

	public void setContinuingEffect(boolean val) {
		continuingEffect = val;
	}

	public boolean continuingEffect() {
		return continuingEffect;
	}

	public void setCostsENDToMaintain(boolean val) {
		costsENDToMaintain = val;
	}

	public boolean costsENDToMaintain() {
		if (findObjectByID(getAssignedModifiers(), "COSTSENDTOMAINTAIN") != null) {
			return true;
		}
		if (!getDuration().equals("INSTANT")) {
			if (findObjectByID(getAssignedModifiers(), "COSTSEND") != null) {
				Modifier mod = (Modifier) findObjectByID(
						getAssignedModifiers(), "COSTSEND");
				if (mod.getSelectedOption() != null
						&& mod.getSelectedOption().getXMLID()
								.equals("ACTIVATE")) {
					return false;
				}
			}
			if (findObjectByID(getAssignedModifiers(), "COSTSENDONLYTOACTIVATE") != null) {
				return false;
			}
			if (getEndUsage() <= 0) {
				return false;
			} else
				return costsENDToMaintain;
		} else if (findObjectByID(getAssignedModifiers(), "COSTSENDTOMAINTAIN") != null) {
			return true;
		} else
			return false;
	}

	/**
	 * Initializes the object.
	 * 
	 * @param element
	 */
	protected void init(Element element) {
		GenericObject.idCount++;
		// this is a first....I was getting many duplicate ids without this :-(
		isPrefab = false;

		availableModifiers = new ArrayList<Modifier>();
		assignedModifiers = new ArrayList<Modifier>();
		availableAdders = new ArrayList<Adder>();
		assignedAdders = new ArrayList<Adder>();
		types = new ArrayList<String>();
		examples = new ArrayList<String>();
		exclusive = true;
		definition = "N/A";
		display = "";
		origDisplay = "";
		abbreviation = "";
		alias = null;
		baseCost = 0;
		levelCost = 0;
		levelValue = -1;
		levelPower = 1;
		levelMultiplier = 1;
		minimumCost = -9999;
		multiplier = 1;
		minimumLevel = 0;
		maxLevel = 9999;
		maxCost = 9999;
		range = "SELF";
		showOption = true;
		levels = 0;
		notes = "";
		includeNotesInPrintout = true;
		parentID = 0;
		parent = null;
		position = -1;
		price = 0d;
		weight = 0d;
		carried = true;
		dynamicDisplay = false;
		enhancerApplied = null;
		options = new ArrayList<Adder>();
		fixedValue = true;
		minSet = false;
		maxSet = false;
		name = "";
		ultra = true;
		input = "";
		allowsOtherModifiers = true;
		allowsOtherAdders = true;
		displayActiveCost = HeroDesigner.getInstance().getPrefs()
				.displayActivePoints();
		defense = "NONE";
		doesBODY = false;
		killing = false;
		visible = false;
		usesEND = false;
		doesDamage = false;
		doesKnockback = false;
		target = "N/A";
		stopSign = false;
		warnSign = false;
		showBuildDialog = true;
		sources = new ArrayList<String>();
		String exCheck = XMLUtility.getValue(element, "EXCLUSIVE");
		if (exCheck != null && exCheck.toUpperCase().trim().startsWith("N")) {
			exclusive = false;
		} else {
			exclusive = true;
		}
		String check = XMLUtility.getValue(element, "DEFINITION");
		if (check != null) {
			definition = check;
		}

		check = XMLUtility.getValue(element, "COSTSENDTOMAINTAIN");
		if (check != null && check.trim().toUpperCase().startsWith("N")) {
			costsENDToMaintain = false;
		} else {
			costsENDToMaintain = true;
		}

		check = XMLUtility.getValue(element, "CONTINUINGEFFECT");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			continuingEffect = true;
		} else {
			continuingEffect = false;
		}

		check = XMLUtility.getValue(element, "DURATION");
		if (check != null && check.trim().length() > 0) {
			duration = check;
		}

		check = XMLUtility.getValue(element, "SHOWDIALOG");
		if (check != null && check.trim().toUpperCase().startsWith("N")) {
			showBuildDialog = false;
		}
		check = XMLUtility.getValue(element, "DISPLAY");
		if (check != null) {
			display = check;
			origDisplay = check;
		}
		check = XMLUtility.getValue(element, "TARGET");
		if (check != null && check.trim().length() > 0) {
			target = check.trim().toUpperCase();
		}
		check = XMLUtility.getValue(element, "ABBREVIATION");
		if (check != null && element.getAttribute("ABBREVIATION") != null) {
			abbreviation = check;
		} else {
			abbreviation = display;
		}
		check = XMLUtility.getValue(element, "WGABBREVIATION");
		if (check != null && element.getAttribute("WGABBREVIATION") != null) {
			wgAbbreviation = check;
		} else {
			wgAbbreviation = display;
		}

		check = XMLUtility.getValue(element, "XMLID");
		if (check != null && check.trim().length() > 0) {
			xmlID = check;
		}
		check = XMLUtility.getValue(element, "ALIAS");
		if (check != null && element.getAttribute("ALIAS") != null) {
			alias = check;
			if (abbreviation.equals(display)) {
				abbreviation = check;
			}
			if (wgAbbreviation.equals(display)) {
				wgAbbreviation = check;
			}
		} else {

			if (HeroDesigner.getInstance().getPrefs().useWG()
					&& HeroDesigner.getInstance().getPrefs().useAbbreviations()
					&& wgAbbreviation != null) {
				alias = wgAbbreviation;
			} else if (!HeroDesigner.getInstance().getPrefs().useWG()
					&& HeroDesigner.getInstance().getPrefs().useAbbreviations()
					&& abbreviation != null) {
				alias = abbreviation;
			} else {
				alias = display;
			}
		}
		check = XMLUtility.getValue(element, "DEFENSE");
		if (check != null && check.trim().length() > 0) {
			defense = check;
		}

		check = XMLUtility.getValue(element, "RANGE");
		if (check != null && check.trim().length() > 0) {
			range = check;
		}
		check = XMLUtility.getValue(element, "STOPSIGN");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			stopSign = true;
		}
		check = XMLUtility.getValue(element, "SHOWOPTION");
		if (check != null && check.trim().toUpperCase().startsWith("N")) {
			showOption = false;
		}
		check = XMLUtility.getValue(element, "WARNSIGN");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			warnSign = true;
		}
		check = XMLUtility.getValue(element, "DOESBODY");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			doesBODY = true;
		}
		check = XMLUtility.getValue(element, "KILLING");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			killing = true;
		}
		check = XMLUtility.getValue(element, "VISIBLE");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			visible = true;
		}
		check = XMLUtility.getValue(element, "USESEND");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			usesEND = true;
		}
		check = XMLUtility.getValue(element, "DOESDAMAGE");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			doesDamage = true;
		}
		check = XMLUtility.getValue(element, "DOESKNOCKBACK");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			doesKnockback = true;
		}
		check = XMLUtility.getValue(element, "FIXEDVALUE");
		if (check != null && check.trim().toUpperCase().startsWith("N")) {
			fixedValue = false;
		} else {
			fixedValue = true;
		}
		check = XMLUtility.getValue(element, "PRICE");
		if (check != null) {
			try {
				price = Double.parseDouble(check);
			} catch (Exception exp) {
			}
		}
		check = XMLUtility.getValue(element, "WEIGHT");
		if (check != null) {
			try {
				weight = Double.parseDouble(check);
			} catch (Exception exp) {
			}
		}
		check = XMLUtility.getValue(element, "CARRIED");
		if (check != null && check.trim().toUpperCase().startsWith("N")) {
			carried = false;
		} else {
			carried = true;
		}
		String bc = XMLUtility.getValue(element, "BASECOST");
		if (bc != null) {
			try {
				baseCost = Double.parseDouble(bc);
				origBaseCost = baseCost;
			} catch (Exception exp) {
			}
		}
		String lc = XMLUtility.getValue(element, "LVLCOST");
		if (lc != null) {
			try {
				levelCost = Double.parseDouble(lc);
			} catch (Exception exp) {
			}
		}
		String lv = XMLUtility.getValue(element, "LVLVAL");
		if (lv != null) {
			try {
				levelValue = Double.parseDouble(lv);
			} catch (Exception exp) {
			}
		}
		String lp = XMLUtility.getValue(element, "LVLPOWER");
		if (lp != null) {
			try {
				levelPower = Integer.parseInt(lp);
			} catch (Exception exp) {
			}
		}
		String lm = XMLUtility.getValue(element, "LVLMULTIPLIER");
		if (lm != null) {
			try {
				levelMultiplier = Integer.parseInt(lm);
			} catch (Exception exp) {
			}
		}
		String ls = XMLUtility.getValue(element, "LEVELSTART");
		if (ls != null) {
			try {
				levels += Integer.parseInt(ls);
			} catch (Exception exp) {
			}
		}
		String mc = XMLUtility.getValue(element, "MINCOST");
		if (mc != null) {
			try {
				minimumCost = Double.parseDouble(mc);
				minSet = true;
			} catch (Exception exp) {
				minimumCost = baseCost;
				minSet = false;
			}
		} else {
			minimumCost = baseCost;
			minSet = false;
		}
		mc = XMLUtility.getValue(element, "MAXCOST");
		if (mc != null) {
			try {
				maxCost = Double.parseDouble(mc);
				maxSet = true;
			} catch (Exception exp) {
				maxCost = baseCost;
				maxSet = false;
			}
		} else {
			maxCost = baseCost;
			maxSet = false;
		}
		String mv = XMLUtility.getValue(element, "MINVAL");
		if (mv != null) {
			try {
				minimumLevel = Integer.parseInt(mv);
			} catch (Exception exp) {
			}
		}
		mv = XMLUtility.getValue(element, "MAXVAL");
		if (mv != null) {
			try {
				maxLevel = Integer.parseInt(mv);
			} catch (Exception exp) {
			}
		}
		java.util.List list = element.getChildren("ADDER");
		Iterator iterator = list.iterator();
		while (iterator.hasNext()) {
			Element adder = (Element) iterator.next();
			Adder ad = new Adder(adder);
			if (ad.includedInTemplate()) {
				availableAdders.add(ad);
			}
		}

		list = element.getChildren("SOURCE");
		iterator = list.iterator();
		while (iterator.hasNext()) {
			Element s = (Element) iterator.next();
			String source = s.getTextTrim();
			if (!sources.contains(source)) {
				sources.add(source);
			}
			if (!GenericObject.allSources.contains(source)) {
				GenericObject.allSources.add(source);
			}
		}
		if (sources.size() == 0) {
			sources.add("Hero System Fifth Edition Rule Book");
		}
		list = element.getChildren("MODIFIER");
		iterator = list.iterator();
		while (iterator.hasNext()) {
			Element modifier = (Element) iterator.next();
			Modifier mod = Modifier.getInstance(modifier);
			if (mod != null && mod.includedInTemplate()) {
				availableModifiers.add(mod);
			}
		}
		java.util.List typeList = element.getChildren("TYPE");
		iterator = typeList.iterator();
		types = new ArrayList<String>();
		while (iterator.hasNext()) {
			Element type = (Element) iterator.next();
			types.add(type.getText());
		}
		String testPosition = XMLUtility.getValue(element, "POSITION");
		if (testPosition != null && testPosition.trim().length() > 0) {
			try {
				position = Integer.parseInt(testPosition);
			} catch (Exception exp) {
			}
		}
		check = XMLUtility.getValue(element, "APPEREND");
		if (check != null && check.trim().length() > 0) {
			try {
				apperend = Integer.parseInt(check);
			} catch (NumberFormatException ex) {
			}
		} else {
			apperend = -1;
		}
		check = XMLUtility.getValue(element, "ALLOWSOTHERMODIFIERS");
		if (check != null && check.trim().length() > 0) {
			allowsOtherModifiers = !check.trim().toUpperCase().startsWith("N");
		} else {
			allowsOtherModifiers = true;
		}
		check = XMLUtility.getValue(element, "ALLOWSOTHERADDERS");
		if (check != null && check.trim().length() > 0) {
			allowsOtherAdders = !check.trim().toUpperCase().startsWith("N");
		} else {
			allowsOtherAdders = true;
		}
		list = element.getChildren("EXAMPLE");
		iterator = list.iterator();
		examples = new ArrayList<String>();
		while (iterator.hasNext()) {
			Element ex = (Element) iterator.next();
			boolean add = true;
			if (ex.getChildren("SOURCE") != null) {
				add = false;
				Iterator iter = ex.getChildren("SOURCE").iterator();
				int count = 0;
				INNER: while (iter.hasNext()) {
					count++;
					Element s = (Element) iter.next();
					if (!GenericObject.getAllSources()
							.contains(s.getTextTrim())) {
						GenericObject.getAllSources().add(s.getTextTrim());
					}
					if (HeroDesigner.getInstance().getPrefs().getSources()
							.contains(s.getTextTrim())) {
						add = true;
						break INNER;
					}
				}
				if (count == 0) {
					add = HeroDesigner.getInstance().getPrefs().getSources()
							.contains("Hero System Fifth Edition Rule Book");
				}
			}
			if (add) {
				examples.add(ex.getText().trim());
			}
		}
		if (examples.size() > 0) {
			Collections.sort(examples);
			input = examples.get(0).toString();
		}
		check = XMLUtility.getValue(element, "OPTIONLABEL");
		if (check != null && check.trim().length() > 0) {
			optionLbl = check;
		}
		check = XMLUtility.getValue(element, "LEVELSLABEL");
		if (check != null && check.trim().length() > 0) {
			levelsLbl = check;
		}
		check = XMLUtility.getValue(element, "INPUTLABEL");
		if (check != null && check.trim().length() > 0) {
			inputLabel = check;
			userInput = true;
		} else {
			inputLabel = null;
			userInput = false;
		}
		check = XMLUtility.getValue(element, "OTHERINPUT");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			otherInputAllowed = true;
		} else {
			otherInputAllowed = false;
		}
		setDisplay(display);
		list = element.getChildren("OPTION");
		iterator = list.iterator();
		while (iterator.hasNext()) {
			Element option = (Element) iterator.next();
			Adder opt = new Adder(option);
			if (opt.includedInTemplate()) {
				options.add(opt);
			}
		}
		if (options.size() > 0) {
			setSelectedOption(options.get(0));
		}
		if (alias == null || alias.trim().length() == 0) {
			alias = getDisplay();
		}
	}

	public ArrayList<String> getSources() {
		if (sources.size() == 0) {
			sources.add("Hero System Fifth Edition Rule Book");
		}
		return sources;
	}

	public static ArrayList<String> getAllSources() {
		if (!GenericObject.allSources
				.contains("Hero System Fifth Edition Rule Book")) {
			GenericObject.allSources.add("Hero System Fifth Edition Rule Book");
		}
		return GenericObject.allSources;
	}

	public boolean isCarried() {
		return carried;
	}

	public boolean isDynamicDisplay() {
		return dynamicDisplay;
	}

	public boolean isEquipment() {
		if (getMainPower() != null) {
			return false;
		}
		return isEquipment;
	}

	public boolean isExclusive() {
		return exclusive;
	}

	public boolean isFixedValue() {
		return fixedValue;
	}

	public boolean isIncludeNotesInPrintout() {
		return includeNotesInPrintout;
	}

	public boolean isKilling() {
		boolean ret = killing;
		return ret;
	}

	public boolean isMaxSet() {
		return maxSet;
	}

	public boolean isMinSet() {
		return minSet;
	}

	public boolean isOtherInputAllowed() {
		return otherInputAllowed;
	}

	public boolean isPower() {
		if (isEquipment()) {
			return true;
		}
		if (getMainPower() != null) {
			return getMainPower().isPower();
		}
		return isPower;
	}

	public boolean isStopSign() {
		return stopSign;
	}

	public boolean isUltra() {
		return ultra;
	}

	public boolean isUserInput() {
		return userInput;
	}

	/**
	 * Returns whether the object is Visible or not. Takes into account
	 * Modifiers assigned directly to the object, but not common Modifiers from
	 * any parent List/Framework.
	 * 
	 * @return
	 */
	public boolean isVisible() {
		boolean ret = visible;
		if (GenericObject.findObjectByID(getAllAssignedModifiers(),
				"BASEDONCON") != null) {
			ret = true;
		}
		if (!ret
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"COSTSEND") != null) {
			ret = true;
		}
		if (!ret
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"VISIBLE") != null) {
			ret = true;
		}
		if (ret
				&& GenericObject.findObjectByID(getAllAssignedModifiers(),
						"INVISIBLE") != null) {
			ret = false;
		}
		return ret;
	}

	public boolean isWarnSign() {
		return warnSign;
	}

	public boolean otherInputAllowed() {
		return otherInputAllowed;
	}

	public void resetID() {
		id = System.currentTimeMillis() + GenericObject.idCount++;
	}

	public void restoreFromSave(Element root) {
		String check = XMLUtility.getValue(root, "ALIAS");
		if (check != null) {
			alias = check;
			abbreviation = check;
			wgAbbreviation = check;
		} else if (alias == null) {
			alias = display;
		}
		check = XMLUtility.getValue(root, "TEXT");
		if (check != null) {
			textOutput = check.trim();
		} else {
			textOutput = null;
		}
		check = XMLUtility.getValue(root, "SFX");
		if (check != null) {
			sfx = check;
			check = XMLUtility.getValue(root, "GRAPHIC");
			if (check != null) {
				graphic = check;
			} else {
				graphic = GenericObject.getGraphicForSFX(sfx);
			}
			check = XMLUtility.getValue(root, "COLOR");
			if (check != null) {
				color = check;
			} else {
				color = GenericObject.getColorForSFX(sfx);
			}
		} else {
			check = XMLUtility.getValue(root, "GRAPHIC");
			if (check != null) {
				graphic = check;
			}
			check = XMLUtility.getValue(root, "COLOR");
			if (check != null) {
				color = check;
			}
		}
		check = XMLUtility.getValue(root, "NOTES");
		if (check != null) {
			notes = check;
		} else {
			notes = "";
		}
		String testLevel = XMLUtility.getValue(root, "LEVELS");
		if (testLevel != null && testLevel.trim().length() > 0) {
			try {
				levels = Integer.parseInt(testLevel);
			} catch (Exception exp) {
			}
		}
		String base = XMLUtility.getValue(root, "BASECOST");
		if (base != null && base.trim().length() > 0) {
			try {
				baseCost = Double.parseDouble(base);
			} catch (Exception exp) {
			}
		}
		String mult = XMLUtility.getValue(root, "MULTIPLIER");
		if (mult != null && mult.trim().length() > 0) {
			try {
				multiplier = Double.parseDouble(mult);
			} catch (Exception exp) {
				multiplier = 1;
			}
		} else {
			multiplier = 1;
		}
		String testID = XMLUtility.getValue(root, "ID");
		if (testID != null && testID.trim().length() > 0) {
			try {
				id = Long.parseLong(testID);
			} catch (Exception exp) {
			}
		}
		testID = XMLUtility.getValue(root, "PARENTID");
		if (testID != null && testID.trim().length() > 0) {
			try {
				parentID = Long.parseLong(testID);
			} catch (Exception exp) {
			}
		}
		String testPosition = XMLUtility.getValue(root, "POSITION");
		if (testPosition != null && testPosition.trim().length() > 0) {
			try {
				position = Integer.parseInt(testPosition);
			} catch (Exception exp) {
			}
		}
		check = XMLUtility.getValue(root, "NAME");
		if (check != null && check.trim().length() > 0) {
			name = check;
		}
		check = XMLUtility.getValue(root, "USE_END_RESERVE");
		if (check != null && check.trim().length() > 0) {
			useENDReserve = check.trim().toUpperCase().startsWith("Y");
		}
		check = XMLUtility.getValue(root, "PRICE");
		if (check != null) {
			try {
				price = Double.parseDouble(check);
			} catch (Exception exp) {
			}
		}
		check = XMLUtility.getValue(root, "WEIGHT");
		if (check != null) {
			try {
				weight = Double.parseDouble(check);
			} catch (Exception exp) {
			}
		}
		check = XMLUtility.getValue(root, "CARRIED");
		if (check != null && check.trim().toUpperCase().startsWith("N")) {
			carried = false;
		} else {
			carried = true;
		}
		check = XMLUtility.getValue(root, "ULTRA_SLOT");
		if (check != null && check.trim().length() > 0) {
			ultra = check.trim().toUpperCase().startsWith("Y");
		}
		check = XMLUtility.getValue(root, "SHOW_ACTIVE_COST");
		if (check != null && check.trim().length() > 0) {
			displayActiveCost = check.trim().toUpperCase().startsWith("Y");
		}
		check = XMLUtility.getValue(root, "INCLUDE_NOTES_IN_PRINTOUT");
		if (check != null && check.trim().toUpperCase().startsWith("Y")) {
			includeNotesInPrintout = true;
		} else {
			includeNotesInPrintout = false;
		}
		// first try to set the option by the XMLID...if that fails, then go
		// for the old "OPTION" attribute.
		check = XMLUtility.getValue(root, "OPTIONID");
		if (check != null && check.trim().length() > 0) {
			for (Adder ad : options) {
				if (ad.getXMLID().equals(check)) {
					ad = (Adder) ad.clone();
					check = XMLUtility.getValue(root, "OPTION_ALIAS");
					if (check != null) {
						ad.setAlias(check);
					}
					setSelectedOption(ad);
					break;
				}
			}
		} else { // it failed...go for the old/original "option" attribute
			// which
			// matches the DISPLAY value.
			check = XMLUtility.getValue(root, "OPTION");
			if (check != null && check.trim().length() > 0) {
				for (Adder ad : options) {
					if (ad.getDisplay().equals(check)
							|| ad.getXMLID().equals(check)) {
						ad = (Adder) ad.clone();
						check = XMLUtility.getValue(root, "OPTION_ALIAS");
						if (check != null && check.trim().length() > 0) {
							ad.setAlias(check);
						}
						setSelectedOption(ad);
						break;
					}
				}
			}
		}

		java.util.List list = root.getChildren("ADDER");
		Iterator iterator = list.iterator();
		while (iterator.hasNext()) {
			Element adder = (Element) iterator.next();
			Adder ad = new Adder(adder);
			ad.setAvailableCheck(true);
			boolean matched = false;
			ArrayList<Adder> avail = getAvailableAdders();
			LOOP: for (Adder comp : avail) {
				if (comp.getXMLID().equals(ad.getXMLID()) || comp.equals(ad)) {
					ad.setAvailableCheck(false);
					comp.setAvailableCheck(false);
					ad = (Adder) comp.clone();
					matched = true;
					break LOOP;
				}
			}
			if (!matched) {
				ad.setExclusive(false);
			}
			ad.setAvailableCheck(false);
			ad.restoreFromSave(adder);
			if (ad.isExclusive()) {
				if (GenericObject.findObjectByID(assignedAdders, ad.getXMLID()) != null) {
					assignedAdders
							.set(assignedAdders.indexOf(GenericObject
									.findObjectByID(assignedAdders,
											ad.getXMLID())), ad);
				} else {
					assignedAdders.add(ad);
				}
			} else {
				assignedAdders.add(ad);
			}
		}
		list = root.getChildren("MODIFIER");
		iterator = list.iterator();
		while (iterator.hasNext()) {
			Element modifier = (Element) iterator.next();
			String idCheck = XMLUtility.getValue(modifier, "XMLID");
			Modifier mod = null;
			if (idCheck != null && idCheck.trim().length() > 0) {
				LOOP: for (Modifier comp : getAvailableModifiers()) {
					if (comp.getXMLID().equals(idCheck)) {
						mod = comp.clone();
						break LOOP;
					}
				}
				if (mod == null) {
					LOOP: for (GenericObject comp : HeroDesigner
							.getActiveTemplate().getModifiers()) {
						if (comp.getXMLID().equals(idCheck)) {
							mod = (Modifier) comp.clone();
							break LOOP;
						}
					}
				}
			}
			if (mod == null) {
				mod = Modifier.getInstance(modifier);
			}
			if (mod != null) {
				mod.setParent(this);
				mod.restoreFromSave(modifier);
				if (mod.getDisplay() == null
						|| mod.getDisplay().trim().length() == 0) {
					mod.setDisplay(mod.getAlias());
				}
				assignedModifiers.add(mod);
			}
		}
		String testInput = XMLUtility.getValue(root, "INPUT");
		if (testInput != null) {
			input = testInput;
		}
		setAlias(alias);
	}

	public void setAlias(String val) {
		if (HeroDesigner.getInstance().getPrefs().useAbbreviations()
				&& abbreviation != null && abbreviation.trim().length() > 0) {
			abbreviation = val;
		}
		if (HeroDesigner.getInstance().getPrefs().useAbbreviations()
				&& wgAbbreviation != null && wgAbbreviation.trim().length() > 0) {
			wgAbbreviation = val;
		}
		alias = val;
		if (val == null) {
			return;
		}
		if (display.indexOf("[LVL]") < 0) {
			return;
		}
		String lvlString = ""
				+ NumberFormat.getInstance().format(
						(getLevels() * getLevelMultiplier()));
		if (levelPower != 1) {
			lvlString = ""
					+ NumberFormat.getInstance().format(
							(long) (getLevelMultiplier() * Math.pow(levelPower,
									getLevels())));
		}
		int index1 = -1;
		int index2 = -1;
		for (int i = 0; i < alias.length(); i++) {
			char ch = alias.charAt(i);
			if (Character.isDigit(ch) && index1 == -1) {
				index1 = i;
			} else if (!Character.isDigit(ch) && ch != ',') {
				if (index1 >= 0 && index2 == -1) {
					index2 = i;
					break;
				}
			}
		}
		if (index1 >= 0) {
			if (index2 <= index1) {
				index2 = alias.length();
			}
			String check = alias.substring(index1, index2);
			if (check.equals(lvlString)) {
				adjustAliasWithLevels = true;
			} else {
				adjustAliasWithLevels = false;
			}
		}
	}

	public void setAppAdjusted(boolean val) {
		appAdjusted = val;
	}

	public void setAssignedAdders(ArrayList<Adder> assignedAdders) {
		this.assignedAdders = assignedAdders;
	}

	public void setAssignedModifiers(ArrayList<Modifier> assignedModifiers) {
		this.assignedModifiers = assignedModifiers;
	}

	public void setAvailableAdders(ArrayList<Adder> availableAdders) {
		this.availableAdders = availableAdders;
	}

	public void setAvailableModifiers(ArrayList<Modifier> availableModifiers) {
		this.availableModifiers = availableModifiers;
	}

	public void setBaseCost(double val) {
		baseCost = val;
	}

	public void setCarried(boolean val) {
		carried = val;
	}

	public void setDefinition(String val) {
		definition = val;
	}

	public void setDisplay(String val) {
		display = val;
		if (display != null && display.indexOf("[LVL]") >= 0) {
			dynamicDisplay = true;
		} else {
			dynamicDisplay = false;
		}
	}

	public void setDisplayActiveCost(boolean val) {
		displayActiveCost = val;
	}

	public void setExamples(ArrayList<String> examples) {
		this.examples = examples;
	}

	public void setExclusive(boolean val) {
		exclusive = val;
	}

	public void setFixedValue(boolean val) {
		fixedValue = val;
	}

	public void setID(long val) {
		id = val;
	}

	public void setIncludeNotesInPrintout(boolean includeNotesInPrintout) {
		this.includeNotesInPrintout = includeNotesInPrintout;
	}

	public void setInput(String val) {
		input = val;
	}

	public void setInputExamples(ArrayList<String> val) {
		examples = val;
	}

	public void setIsEquipment(boolean val) {
		isEquipment = val;
	}

	public void setIsPrefab(boolean val) {
		isPrefab = val;
	}

	public boolean getIsPrefab() {
		return isPrefab;
	}

	public void setLevelCost(double val) {
		levelCost = val;
	}

	public void setLevelMultiplier(int levelMultiplier) {
		this.levelMultiplier = levelMultiplier;
	}

	public void setLevelPower(int levelPower) {
		this.levelPower = levelPower;
	}

	public void setLevels(int val) {
		if (val < minimumLevel) {
			levels = minimumLevel;
		} else if (val > maxLevel) {
			levels = maxLevel;
		} else {
			levels = val;
		}
	}

	public void setLevelValue(double val) {
		levelValue = val;
	}

	/**
	 * Sets the CompoundPower that contains this object, null if none. This must
	 * be used in conjunction with any adjustment to the Powers Vector on a
	 * CompoundPower
	 * 
	 * @param main
	 * @see com.hero.objects.powers.CompoundPower
	 */
	public void setMainPower(CompoundPower main) {
		mainPower = main;
	}

	public void setMaxCost(double maxCost) {
		this.maxCost = maxCost;
		maxSet = true;
	}

	public void setMaxLevel(int val) {
		maxLevel = val;
	}

	public void setMinimumCost(double val) {
		minimumCost = val;
	}

	public void setMinimumLevel(int val) {
		minimumLevel = val;
	}

	public void setMultiplier(double val) {
		multiplier = val;
	}

	public void setName(String val) {
		name = val;
	}

	public void setNotes(String notes) {
		this.notes = notes;
	}

	public void setOptions(ArrayList<Adder> val) {
		options = val;
	}

	/**
	 * Sets the parent List/Framework that contains this object. Null if none.
	 * 
	 * @param parent
	 */
	public void setParent(List parent) {
		this.parent = parent;
		if (parent != null) {
			parentID = parent.getID();
		} else {
			parentID = -1;
		}
	}

	/**
	 * Sets the object's position within the purchase list.
	 * 
	 * @param val
	 */
	public void setPosition(int val) {
		position = val;
	}

	/**
	 * Sets whether or not the object is a Power/purchased on the Powers list.
	 * 
	 * @param val
	 */
	public void setPower(boolean val) {
		isPower = val;
	}

	public void setPrice(double val) {
		if (HeroDesigner.getActiveHero() == null) {
			return;
		}
		price = val
				* HeroDesigner.getActiveHero().getRules()
						.getEquipmentCostConversion();
	}

	/**
	 * Sets the selected option (if any). Various attributes of the base object
	 * may be altered as a result of the selection, depending on the selected
	 * option's construction.
	 * 
	 * @param option
	 */
	public void setSelectedOption(Adder option) {
		if (option != null) {
			long id = option.getID();
			option = (Adder) option.clone();
			option.setID(id);
		}
		Adder oldOption = selectedOption;
		selectedOption = option;
		if (oldOption != null && selectedOption != null
				&& oldOption.getXMLID().equals(selectedOption.getXMLID())
				&& !oldOption.isFixedValue()) {
			selectedOption.setBaseCost(oldOption.getBaseCost());
		}
		if (options != null) {
			for (Adder o : options) {
				if (o.equals(option)) {
					o.setSelected(true);
				} else {
					o.setSelected(false);
				}
			}
		}
		if (option == null) {
			return;
		}
		selectedOption.setSelected(true);
		setBaseCost(option.getBaseCost());
		if (option.isMinSet()) {
			setMinimumCost(option.getMinimumCost());
			minSet = true;
		}
		if (option.isMaxSet()) {
			setMaxCost(option.getMaxCost());
			maxSet = true;
		}
		if (option.getLevelCost() > 0) {
			setLevelCost(option.getLevelCost());
		}
		if (option.getLevelMultiplier() > 0) {
			setLevelMultiplier(option.getLevelMultiplier());
		}
		if (option.getLevelPower() > 1) {
			setLevelPower(option.getLevelPower());
		}
		if (option.getLevelValue() != 0) {
			setLevelValue(option.getLevelValue());
		}
		if (option.getMinimumLevel() != 0) {
			setMinimumLevel(option.getMinimumLevel());
		}
		if (option.getDefinition() != null
				&& option.getDefinition().trim().length() > 0) {
			setDefinition(option.getDefinition());
		}
		if (option.getTypes() != null && option.getTypes().size() > 0) {
			setTypes(option.getTypes());
		}
		if (option.getExamples() != null && option.getExamples().size() > 0) {
			setExamples(option.getExamples());
		}
		allowsOtherModifiers = option.allowsOtherModifiers();
		allowsOtherAdders = option.allowsOtherAdders();
		OUTER: for (Adder ad : option.getAvailableAdders()) {
			for (Adder comp : getAssignedAdders()) {
				if (comp.getXMLID().equals(ad.getXMLID())) {
					comp.setBaseCost(ad.getBaseCost());
					continue OUTER;
				}
			}
		}
		if (oldOption != null) {
			OUTER: for (int i = getAssignedAdders().size() - 1; i >= 0; i--) {
				Adder ad = getAssignedAdders().get(i);
				if (GenericObject.findObjectByID(
						oldOption.getAvailableAdders(), ad.getXMLID()) == null) {
					continue OUTER;
				}
				for (Adder comp : option.getAvailableAdders()) {
					if (comp.getXMLID().equals(ad.getXMLID())) {
						comp.setBaseCost(ad.getBaseCost());
						continue OUTER;
					}
				}
				getAssignedAdders().remove(ad);
			}
		}
	}

	public void setTypes(ArrayList<String> types) {
		this.types = types;
	}

	public void setUltra(boolean val) {
		ultra = val;
	}

	/**
	 * Whether to use an Endurance Reserve or Personal END.
	 * 
	 * @param val
	 */
	public void setUseENDReserve(boolean val) {
		useENDReserve = val;
	}

	public void setUserInput(boolean val) {
		userInput = val;
	}

	public void setVisible(boolean val) {
		visible = val;
	}

	/**
	 * Sets the weight of the object. Value should be in the units specified in
	 * AppPrefs
	 * 
	 * @see com.hero.AppPrefs
	 * @param val
	 */
	public void setWeight(double val) {
		double set = val;
		if (HeroDesigner.getInstance().getPrefs().isMetric()) {
			set = val * 1000d / 453.5924; // g -> lbs
		}
		if (weight == set) {
			return;
		}
		weight = set;
	}

	public void setXMLID(String val) {
		xmlID = val;
	}

	/**
	 * Specifies whether or not to show a construction/edit dialog when the
	 * object is selected from an available list.
	 * 
	 * @return
	 */
	public boolean showBuildDialog() {
		if (isPower()
				|| !HeroDesigner.getInstance().getPrefs().useQuickAssign()) {
			return true;
		}
		return showBuildDialog;
	}

	public String toString() {
		String ret = "";
		boolean holder = HeroDesigner.getInstance().getPrefs()
				.useAbbreviations();
		HeroDesigner.getInstance().getPrefs().setUseAbbreviations(false);
		ret = getDisplay();
		if (isPrefab) {
			// ret = "<html>"+((getName() != null &&
			// getName().trim().length()>0)?("<i>"+getName()+": </i>"):"")
			// + getAlias();
			ret = "<html>" + getTextOutput() + "<br><b>Real Cost: "
					+ Rounder.roundUp(getRealCost()) + "</b></html>";
		}
		HeroDesigner.getInstance().getPrefs().setUseAbbreviations(holder);
		return ret;
	}

	/**
	 * Whether or not the object uses END. This takes into account all assigned
	 * Modifiers, both those directly assigned to the object and any common
	 * Modifiers from the objects parent List/Framework (if available).
	 * 
	 * @return
	 */
	public boolean usesEND() {
		boolean ret = usesEND;
		if (apperend != -1) {
			usesEND = apperend > 0;
		}
		ArrayList<Modifier> vec = new ArrayList<Modifier>();
		vec.addAll(getAssignedModifiers());
		List parent = getParentList();
		if (getMainPower() != null) {
			parent = getMainPower().getParentList();
		}
		if (parent != null) {
			ArrayList<Modifier> par = parent.getAssignedModifiers();
			vec.addAll(par);
		}
		/*
		 * if (GenericObject.findObjectByID(vec, "UOO") != null) { ret = true; }
		 */
		if (GenericObject.findObjectByID(vec, "CHARGES") != null) {
			ret = false;
		}
		if (GenericObject.findObjectByID(vec, "COSTSEND") != null) {
			ret = true;
		}
		if (GenericObject.findObjectByID(vec, "REDUCEDEND") != null) {
			Modifier mod = (Modifier) GenericObject.findObjectByID(vec,
					"REDUCEDEND");
			if (mod.getSelectedOption().getXMLID().equals("HALFEND")) {
				ret = true;
			} else {
				ret = false;
			}
		}
		return ret;
	}

	/**
	 * Verifies the Modifier Intelligence for all Modifiers assigned to this
	 * object. May edit the object based on the preferences set in AppPrefs.
	 * 
	 * @return
	 * @see com.hero.AppPrefs
	 */
	public boolean verifyModifiers() {
		HeroDesigner.lastEdit = System.currentTimeMillis();
		if (this instanceof CompoundPower) {
			CompoundPower cp = (CompoundPower) this;
			for (GenericObject obj : cp.getPowers()) {
				boolean check = obj.verifyModifiers();
				if (!check) {
					appAdjusted = true;
					return false;
				}
			}
		}

		// need to ensure that everyone recalculates values...
		boolean allOK = true;
		if (!HeroDesigner.getInstance().getPrefs().checkModsDuringEdit()
				|| this instanceof Disadvantage) {
			return allOK;
		}
		boolean remove = HeroDesigner.getInstance().getPrefs()
				.removeIllegalMods();
		ArrayList<Modifier> assigned = getAssignedModifiers();
		String charName = "";
		if (!HeroDesigner.getAppFrame().isShowing()) {
			charName = "Invalid modifier found on "
					+ HeroDesigner.getActiveHero().getCharacterName() + ":\n\n";
		}
		if (this instanceof List && ((List) this).getObjects().size() > 0) {
			boolean maneuversInvolved = false;
			ArrayList<Modifier> listMods = assigned;
			ArrayList<GenericObject> slots = ((List) this).getObjects();
			OUTER: for (GenericObject slot : slots) {
				if (slot instanceof Maneuver) {
					maneuversInvolved = true;
				}
				if (slot instanceof CompoundPower) {
					CompoundPower cp = (CompoundPower) slot;
					cp.setParent(null);
					for (int k = 0; k < cp.getPowers().size(); k++) {
						slot = cp.getPowers().get(k);
						slot.setParent(null);
						ArrayList<Modifier> orig = slot.getAssignedModifiers();
						ArrayList<Modifier> slotMods = new ArrayList<Modifier>();
						slotMods.addAll(orig);
						ArrayList<Modifier> addFromList = new ArrayList<Modifier>();
						for (Modifier mod : listMods) {
							if (GenericObject.findObjectByID(orig,
									mod.getXMLID()) == null) {
								addFromList.add(mod);
							}
						}
						slotMods.addAll(addFromList);
						slot.setAssignedModifiers(slotMods);

						INNER: for (int j = addFromList.size() - 1; j >= 0; j--) {
							Modifier mod = addFromList.get(j);
							slotMods.remove(mod);
							assigned.remove(mod);
							slot.setAssignedModifiers(slotMods);
							if (!slot.allowsOtherModifiers()) {
								String message = charName
										+ "The Common Modifier "
										+ mod.getAlias()
										+ " is not a legal modifier on the slot "
										+ (slot.getName().trim().length() > 0 ? slot
												.getName()
												+ ": "
												+ slot.getAlias() : slot
												.getAlias())
										+ ".  Reason:\n\n"
										+ slot.getAlias()
										+ " does not allow modifiers with its current configuration.\n\n"
										+ (remove ? mod.getAlias()
												+ " will be removed from the List "
												+ getAlias() + "."
												: "");
								JOptionPane.showMessageDialog(
										HeroDesigner.getAppFrame(), message,
										"Modifier not allowed on ability",
										JOptionPane.WARNING_MESSAGE);
								appAdjusted = true;
								if (remove) {
									continue INNER;
								}
							} else {
								slot.setListModCheck(true);
								String check = mod.included(slot);
								slot.setListModCheck(false);
								if (check.trim().length() > 0) {
									slot.setListModCheck(true);
									check = mod.included(slot);
									slot.setListModCheck(false);
									String message = charName
											+ "The Common Modifier "
											+ mod.getAlias()
											+ " is not a legal modifier on "
											+ (getName().trim().length() > 0 ? getName()
													+ ": " + getAlias()
													: getAlias())
											+ ".  Reason:\n\n"
											+ check
											+ "\n\n"
											+ (remove ? mod.getAlias()
													+ " will be removed from the List "
													+ getAlias() + "."
													: "");
									JOptionPane.showMessageDialog(
											HeroDesigner.getAppFrame(),
											message,
											"Modifier not allowed on ability",
											JOptionPane.WARNING_MESSAGE);
									appAdjusted = true;
									if (remove) {
										allOK = false;
										continue INNER;
									}
								}
							}
							slotMods.add(j, mod);
							if (mod.getXMLID().equals("MODIFIER")
									|| !mod.isExclusive()
									|| GenericObject.findObjectByID(assigned,
											mod.getXMLID()) == null) {
								assigned.add(j, mod);
							}
						}

						slot.setAssignedModifiers(orig);
						slot.setParent((List) this);
						setAssignedModifiers(assigned);
					}
					cp.setParent((List) this);
					continue OUTER;
				}

				ArrayList<Modifier> orig = slot.getAssignedModifiers();
				ArrayList<Modifier> slotMods = new ArrayList<Modifier>();
				slotMods.addAll(orig);
				ArrayList<Modifier> addFromList = new ArrayList<Modifier>();
				for (Modifier mod : listMods) {
					if (GenericObject.findObjectByID(orig, mod.getXMLID()) == null) {
						addFromList.add(mod);
					}
				}
				slotMods.addAll(addFromList);
				slot.setAssignedModifiers(slotMods);
				slot.setParent(null);

				INNER: for (int j = addFromList.size() - 1; j >= 0; j--) {
					Modifier mod = addFromList.get(j);
					slotMods.remove(mod);
					assigned.remove(mod);
					slot.setAssignedModifiers(slotMods);
					if (!slot.allowsOtherModifiers()) {
						String message = charName
								+ "The Common Modifier "
								+ mod.getAlias()
								+ " is not a legal modifier on the slot "
								+ (slot.getName().trim().length() > 0 ? slot
										.getName() + ": " + slot.getAlias()
										: slot.getAlias())
								+ ".  Reason:\n\n"
								+ slot.getAlias()
								+ " does not allow modifiers with its current configuration.\n\n"
								+ (remove ? mod.getAlias()
										+ " will be removed from the List "
										+ getAlias() + "." : "");
						JOptionPane.showMessageDialog(
								HeroDesigner.getAppFrame(), message,
								"Modifier not allowed on ability",
								JOptionPane.WARNING_MESSAGE);
						appAdjusted = true;
						if (remove) {
							continue INNER;
						}
					} else {
						slot.setListModCheck(true);
						String check = mod.included(slot);
						slot.setListModCheck(false);
						if (check.trim().length() > 0) {
							String message = charName
									+ "The Common Modifier "
									+ mod.getAlias()
									+ " is not a legal modifier on "
									+ (getName().trim().length() > 0 ? getName()
											+ ": " + getAlias()
											: getAlias())
									+ ".  Reason:\n\n"
									+ check
									+ "\n\n"
									+ (remove ? mod.getAlias()
											+ " will be removed from the List "
											+ getAlias() + "." : "");
							JOptionPane.showMessageDialog(
									HeroDesigner.getAppFrame(), message,
									"Modifier not allowed on ability",
									JOptionPane.WARNING_MESSAGE);
							appAdjusted = true;
							if (remove) {
								allOK = false;
								continue INNER;
							}
						}
					}
					slotMods.add(j, mod);
					if (mod.getXMLID().equals("MODIFIER")
							|| !mod.isExclusive()
							|| GenericObject.findObjectByID(assigned,
									mod.getXMLID()) == null) {
						assigned.add(j, mod);
					}
				}

				slot.setAssignedModifiers(orig);
				slot.setParent((List) this);
				setAssignedModifiers(assigned);
			}
			if (allOK && maneuversInvolved) {
				for (Modifier mod : assigned) {
					String check = mod.included(this);
					if (check.trim().length() > 0) {
						String message = charName
								+ "The Common Modifier "
								+ mod.getAlias()
								+ " is not a legal modifier on "
								+ (getName().trim().length() > 0 ? getName()
										+ ": " + getAlias() : getAlias())
								+ ".  Reason:\n\n"
								+ check
								+ "\n\n"
								+ (remove ? mod.getAlias()
										+ " will be removed from the List "
										+ getAlias() + "." : "");
						JOptionPane.showMessageDialog(
								HeroDesigner.getAppFrame(), message,
								"Modifier not allowed on ability",
								JOptionPane.WARNING_MESSAGE);
						appAdjusted = true;
						if (remove) {
							allOK = false;
						}
					}
				}
			}
			return remove ? allOK : true;
		}
		for (int i = assigned.size() - 1; i >= 0; i--) {
			Modifier mod = assigned.get(i);
			if (!mod.isPrivate() && !mod.isLimitation()
					&& this instanceof NakedModifier) {
				continue;
			}
			assigned.remove(i);
			setAssignedModifiers(assigned);
			if (!allowsOtherModifiers()) {
				String message = charName
						+ mod.getAlias()
						+ " is not a legal modifier on "
						+ (getName().trim().length() > 0 ? getName() + ": "
								+ getAlias() : getAlias())
						+ ".  Reason:\n\n"
						+ getAlias()
						+ " does not allow modifiers with its current configuration.\n\n"
						+ (remove ? mod.getAlias() + " will be removed from "
								+ getAlias() + "." : "");
				JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(),
						message, "Modifier not allowed on ability",
						JOptionPane.WARNING_MESSAGE);
				appAdjusted = true;
				if (remove) {
					continue;
				}
			} else {
				String check = mod.included(this);
				if (this instanceof Characteristic) {
					Characteristic ch = (Characteristic) this;
					if (ch.addModifiersToBase()) {
						ch = (Characteristic) ch.clone();
						INNER: for (int j = 0; j < HeroDesigner.getActiveHero()
								.getCharacteristics().size(); j++) {
							Characteristic c = (Characteristic) HeroDesigner
									.getActiveHero().getCharacteristics()
									.get(j);
							if (c.getXMLID().equals(ch.getXMLID())) {
								ch.setLevels(ch.getLevels()
										+ (int) c.getCharacteristicValue());
								break INNER;
							}
						}
						check = mod.included(ch);
					}
				}

				if (check.trim().length() > 0) {
					String message = charName
							+ mod.getAlias()
							+ " is not a legal modifier on "
							+ (getName().trim().length() > 0 ? getName() + ": "
									+ getAlias() : getAlias())
							+ ".  Reason:\n\n"
							+ check
							+ "\n\n"
							+ (remove ? mod.getAlias()
									+ " will be removed from " + getAlias()
									+ "." : "");
					JOptionPane.showMessageDialog(HeroDesigner.getAppFrame(),
							message, "Modifier not allowed on ability",
							JOptionPane.WARNING_MESSAGE);
					appAdjusted = true;
					if (remove) {
						allOK = false;
						continue;
					}
				}
			}

			if (mod.getXMLID().equals("MODIFIER")
					|| !mod.isExclusive()
					|| GenericObject.findObjectByID(assigned, mod.getXMLID()) == null) {
				assigned.add(i, mod);
			}
		}
		setAssignedModifiers(assigned);
		return remove ? allOK : true;
	}
}