/*
* EquipSet.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* @author Jayme Cox <jaymecox@users.sourceforge.net>
* Created on April 29th, 2002, 11:26 PM
*
* Current Ver: $Revision$
*
*/
package pcgen.core.character;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.StringTokenizer;
import pcgen.cdom.base.Constants;
import pcgen.cdom.enumeration.EquipmentLocation;
import pcgen.core.BonusManager;
import pcgen.core.Equipment;
import pcgen.core.PlayerCharacter;
import pcgen.core.bonus.BonusObj;
import pcgen.core.utils.MessageType;
import pcgen.core.utils.ShowMessageDelegate;
import pcgen.util.Logging;
/**
* Every item of equipment that is contained in an EquipSet has an ID with the following
* structure. X.Y, where X is the parent ID (and may also be a period separated list)
* and Y is this equipment's id. All EquipSets have 0 as their ultimate parent.
*
* This means that the ID for a "root" EquipSet looks like: 0.1
* 0 is the parent ID,
* 1 the equipset ID
*
* Each piece of equipment that is part of this EquipSet has a parent ID of 0.1
*
* The ID path for an Equipset that is the root of an Equipset tree
* id_path for a "root" EquipSet looks like: 0.1
* where
* 0 == my parent (none)
* 1 == my Id
*
* a Child id_path looks like this: 0.1.3
* where
* 0 == root
* 1 == my parent
* 3 == my Id
*
*/
/*
* the Structure of each EQUIPSET is as follows.
*
* EQUIPSET: id_path : name : value : item
*
* id_path = a . delimited string that denotes parent/child relationship
* name = name of EquipSet or item this represents
(and is used to define uniquiness for compareTo)
* value = Name of the Equipment stored in this item
* item = Equipment item stored (optional)
* qty = number of items this equipset contains (all same item)
*
*/
/**
* {@code EquipSet.java}
* @author Jayme Cox <jaymecox@excite.com>
*/
public final class EquipSet implements Comparable<EquipSet>, Cloneable
{
/** The root path of the default equipment set. */
public static String DEFAULT_SET_PATH = "0.1";
private Equipment eq_item;
private Float qty = new Float(1);
private Map<BonusObj, BonusManager.TempBonusInfo> tempBonusBySource = new IdentityHashMap<>();
private String id_path = Constants.EMPTY_STRING;
private String name = Constants.EMPTY_STRING;
private String note = Constants.EMPTY_STRING;
private String value = Constants.EMPTY_STRING;
private boolean useTempBonuses = true;
/**
* Retrieve the id from a path, that is the last number in the sequence.
* e.g. The id of the path 0.1.17 is 17.
* @param path The path to be interpreted.
* @return The numeric id
*/
public static int getIdFromPath(String path)
{
int id = 0;
try
{
final StringTokenizer aTok =
new StringTokenizer(path,
Constants.EQUIP_SET_PATH_SEPARATOR, false);
while (aTok.hasMoreTokens())
{
id = Integer.parseInt(aTok.nextToken());
}
}
catch (NullPointerException e)
{
Logging.errorPrint("Error in EquipSet.getId " + path, e);
}
return id;
}
/**
* Retrieve the parent path of this path, that is the sequence without
* the last number.
* e.g. The parent of the path 0.1.17 is 0.1
* @param path The path to be interpreted.
* @return The parent path.
*/
public static String getParentPath(String path)
{
int idx = path.lastIndexOf(Constants.EQUIP_SET_PATH_SEPARATOR);
if (idx < 0)
{
return "";
}
return path.substring(0, idx);
}
/**
* Retrieve the depth from a path, that is the number of numbers in the sequence.
* e.g. The depth of the path 0.1.17 is 3.
* @param path The path to be interpreted.
* @return The numeric depth
*/
public static int getPathDepth(String path)
{
try
{
final StringTokenizer aTok =
new StringTokenizer(path,
Constants.EQUIP_SET_PATH_SEPARATOR, false);
return aTok.countTokens();
}
catch (NullPointerException e)
{
Logging.errorPrint("Error in EquipSet.getPathDepth", e);
}
return 0;
}
/**
* Constructor
* @param id
* @param aName
*/
public EquipSet(final String id, final String aName)
{
id_path = id;
name = aName;
}
/**
* Constructor
* @param id
* @param aName
* @param aValue
* @param item
*/
public EquipSet(final String id, final String aName, final String aValue, final Equipment item)
{
id_path = id;
name = aName;
value = aValue;
eq_item = item;
}
/**
* our Id is the last number on the id_path
* if id_path is "0.2.8.15", our id is 15
* @return id
**/
public int getId()
{
return EquipSet.getIdFromPath(id_path);
}
/**
* Set ID Path
* @param x
*/
public void setIdPath(final String x)
{
id_path = x;
}
/**
* Get Id Path
* @return id_path
*/
public String getIdPath()
{
return id_path;
}
/**
* Set Item
* @param item
*/
public void setItem(final Equipment item)
{
eq_item = item;
}
/**
* Get item
* @return eq_item
*/
public Equipment getItem()
{
return eq_item;
}
/**
* Set name
* @param x
*/
public void setName(final String x)
{
name = x;
}
/**
* name is our EquipSet name if we are a root node
* or it is the name of the location for the equipment we are holding
* @return name
**/
public String getName()
{
return name;
}
/**
* Sets the player added note to aString
* @param aString
**/
public void setNote(final String aString)
{
note = aString;
}
/**
* Get note
* @return note
*/
public String getNote()
{
return note;
}
/**
* the Parent Id Path is everything except our Id
* if id_path is "0.2.8.15", our Parent Id is "0.2.8"
* @return parent id path
**/
public String getParentIdPath()
{
final StringBuilder buf = new StringBuilder(50);
// get all tokens and include the delimiter
try
{
final StringTokenizer aTok = new StringTokenizer(id_path, Constants.EQUIP_SET_PATH_SEPARATOR, true);
// get all tokens (and delimiters) except last two
for (int i = aTok.countTokens() - 2; i > 0; i--)
{
buf.append(aTok.nextToken());
}
}
catch (NullPointerException e)
{
Logging.errorPrint("Error in EquipSet.getParentIdPath", e);
}
return buf.toString();
}
/**
* Set's the number of items in this equipset
* @param x
**/
public void setQty(final Float x)
{
qty = x;
}
/**
* Get quantity
* @return quantity
*/
public Float getQty()
{
return qty;
}
/**
* return the root id of the EquipSet
* If our id_path is "0.2.8.15", the root would be "0.2"
* @return root id path
**/
public String getRootIdPath()
{
final StringBuilder buf = new StringBuilder(50);
final StringTokenizer aTok = new StringTokenizer(id_path, Constants.EQUIP_SET_PATH_SEPARATOR, false);
final String result;
if (aTok.countTokens() < 2)
{
result = Constants.EMPTY_STRING;
}
else
{
// get first two tokens and delimiter
buf.append(aTok.nextToken());
buf.append('.');
buf.append(aTok.nextToken());
result = buf.toString();
}
return result;
}
/**
* Set temp bonus list
* @param aList
*/
public void setTempBonusList(final Map<BonusObj, BonusManager.TempBonusInfo> aList)
{
tempBonusBySource = aList;
}
/**
* a List of BonusObj's
* @return temp bonus list
**/
public Map<BonusObj, BonusManager.TempBonusInfo> getTempBonusMap()
{
return tempBonusBySource;
}
/**
* Should apply temporary bonuses to this equipset?
* @param aBool
**/
public void setUseTempMods(final boolean aBool)
{
useTempBonuses = aBool;
}
/**
* Return TRUE if using temp mods
* @return TRUE if using temp mods
*/
public boolean getUseTempMods()
{
return useTempBonuses;
}
/**
* Set value
* @param x
*/
public void setValue(final String x)
{
value = x;
}
/**
* value is null for root nodes or
* it is the name of the piece of equipment we are holding
* @return value
**/
public String getValue()
{
return value;
}
/**
* Clear the temp bonus list
*/
public void clearTempBonusList()
{
tempBonusBySource.clear();
}
/**
* Creates a duplicate of this equip set. Note that this is
* a deep clone - all equipment associated with this EquipSet
* will also be cloned.
*
* @return A new equip set, identical to this one.
*/
@Override
public Object clone()
{
EquipSet eqSet = null;
try
{
eqSet = (EquipSet) super.clone();
if (eq_item != null)
{
eqSet.eq_item = eq_item.clone();
}
if (qty != null)
{
eqSet.qty = new Float(qty.floatValue());
}
}
catch (CloneNotSupportedException exc)
{
ShowMessageDelegate.showMessageDialog(
exc.getMessage(), Constants.APPLICATION_NAME, MessageType.ERROR);
}
return eqSet;
}
/**
* Compares the path ids of each object to determine relative order.
*
* @param obj The EquipSet to compare with.
*
* @return a negative integer, zero, or a positive integer as this EquipSet
* is less than, equal to, or greater than the specified EquipSet.
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(final EquipSet obj)
{
return id_path.compareToIgnoreCase(obj.id_path);
}
/**
* Returns the EquipSet name.
*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return name;
}
/**
* true if temp bonus list is not empty.
* @return true if temp bonus list is not empty
*/
public boolean useTempBonusList()
{
return !tempBonusBySource.isEmpty();
}
/**
* Apply this EquipSet to a PlayerCharacter object.
* @param aPC the PC to equip the item on
*/
public void equipItem(PlayerCharacter aPC)
{
final StringTokenizer aTok = new StringTokenizer(getIdPath(), Constants.EQUIP_SET_PATH_SEPARATOR);
// if the eSet.getIdPath() is longer than 3
// it's inside a container, don't try to equip
if (aTok.countTokens() > Constants.ID_PATH_LENGTH_FOR_NON_CONTAINED)
{
// Get back to carried/equipped/not carried to determine correct location
StringBuilder rootPath = new StringBuilder(40);
for (int i = 0; i < Constants.ID_PATH_LENGTH_FOR_NON_CONTAINED; i++)
{
if (i > 0)
{
rootPath.append(".");
}
rootPath.append(aTok.nextToken());
}
EquipSet rootSet = aPC.getEquipSetByIdPath(rootPath.toString());
if (rootSet != null && rootSet.name.startsWith(Constants.EQUIP_LOCATION_CARRIED))
{
eq_item.addEquipmentToLocation(qty, EquipmentLocation.CARRIED_NEITHER, false, aPC);
}
else if (rootSet != null && rootSet.name.startsWith(Constants.EQUIP_LOCATION_NOTCARRIED))
{
eq_item.addEquipmentToLocation(qty, EquipmentLocation.NOT_CARRIED, false, aPC);
}
else if (rootSet != null && rootSet.name.startsWith(Constants.EQUIP_LOCATION_EQUIPPED))
{
eq_item.addEquipmentToLocation(qty, EquipmentLocation.EQUIPPED_NEITHER, false, aPC);
}
else
{
eq_item.addEquipmentToLocation(qty, EquipmentLocation.CONTAINED, false, aPC);
}
}
else if (name.startsWith(Constants.EQUIP_LOCATION_CARRIED))
{
eq_item.addEquipmentToLocation(qty, EquipmentLocation.CARRIED_NEITHER, false, aPC);
}
else if (name.startsWith(Constants.EQUIP_LOCATION_NOTCARRIED))
{
eq_item.addEquipmentToLocation(qty, EquipmentLocation.NOT_CARRIED, false, aPC);
}
else if (eq_item.isWeapon())
{
if (name.equals(Constants.EQUIP_LOCATION_PRIMARY)
|| name.equals(Constants.EQUIP_LOCATION_NATURAL_PRIMARY))
{
eq_item.addWeaponToLocation(qty, EquipmentLocation.EQUIPPED_PRIMARY, aPC);
}
else if (name.startsWith(Constants.EQUIP_LOCATION_SECONDARY)
|| name.equals(Constants.EQUIP_LOCATION_NATURAL_SECONDARY))
{
eq_item.addWeaponToLocation(qty, EquipmentLocation.EQUIPPED_SECONDARY, aPC);
}
else if (name.equals(Constants.EQUIP_LOCATION_BOTH))
{
eq_item.addWeaponToLocation(qty, EquipmentLocation.EQUIPPED_BOTH, aPC);
}
else if (name.equals(Constants.EQUIP_LOCATION_DOUBLE))
{
eq_item.addWeaponToLocation(qty, EquipmentLocation.EQUIPPED_TWO_HANDS, aPC);
}
else if (name.equals(Constants.EQUIP_LOCATION_UNARMED))
{
eq_item.addWeaponToLocation(qty, EquipmentLocation.EQUIPPED_NEITHER, aPC);
}
else if (name.equals(Constants.EQUIP_LOCATION_TWOWEAPONS))
{
Float quantity = (qty.doubleValue() < 2.0) ? 2.0f : qty;
setQty(quantity);
eq_item.addWeaponToLocation(quantity, EquipmentLocation.EQUIPPED_TWO_HANDS, aPC);
}
else if (name.equals(Constants.EQUIP_LOCATION_SHIELD))
{
eq_item.addWeaponToLocation(qty, EquipmentLocation.EQUIPPED_NEITHER, aPC);
}
}
else
{
eq_item.addEquipmentToLocation(qty, EquipmentLocation.EQUIPPED_NEITHER, true, aPC);
}
}
/**
* If there is a note set in this Equipment set, then add it to the contained equipment.
*/
public void addNoteToItem()
{
final String aNote = getNote();
if ((aNote != null) && (!aNote.isEmpty()))
{
getItem().setNote(aNote);
}
}
/**
* Is this EquipSet part of the Equipment set located at rootId.
*
* @param rootId The id to test
* @return true if eqSet is a child of rootId
*/
public boolean isPartOf(String rootId)
{
// rootId = 0.1.
// parentIdPath = 0.10.
// OR
// rootId = 0.10.
// parentIdPath = 0.1.
final String abCalcId = rootId + Constants.EQUIP_SET_PATH_SEPARATOR;
final String abParentId = getParentIdPath() + Constants.EQUIP_SET_PATH_SEPARATOR;
return abParentId.startsWith(abCalcId);
}
}