/*
* EquipmentModifier.java
* Copyright 2001 (C) Greg Bingleman <byngl@hotmail.com>
*
* 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
*
* Created on November 19, 2001, 4:28 PM
*
* Current Ver: $Revision$
*
*/
package pcgen.core;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import pcgen.base.formula.Formula;
import pcgen.base.lang.StringUtil;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.base.Constants;
import pcgen.cdom.base.FormulaFactory;
import pcgen.cdom.content.SpellResistance;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.enumeration.Type;
import pcgen.core.analysis.BonusCalc;
import pcgen.core.bonus.Bonus;
import pcgen.core.bonus.BonusObj;
import pcgen.facade.core.EquipModFacade;
import pcgen.core.prereq.PrereqHandler;
import pcgen.core.prereq.Prerequisite;
import pcgen.core.utils.MessageType;
import pcgen.core.utils.ShowMessageDelegate;
import pcgen.util.Delta;
/**
* Definition and games rules for an equipment modifier.
*
* @author Greg Bingleman <byngl@hotmail.com>
*/
public final class EquipmentModifier extends PObject implements Comparable<Object>, EquipModFacade
{
private static final String PERCENT_CHOICE_PATTERN = Pattern
.quote(Constants.LST_PERCENT_CHOICE);
private static final Formula CHOICE_FORMULA = FormulaFactory.getFormulaFor("%CHOICE");
/**
* returns all BonusObjs that are "active", for example, ones that pass all
* prerequisite tests and should be applied.
*
* @param caller The object that will be used to test prerequisites
* against to determine if a bonus should be applied.
* @param aPC The PC that the prerequisites will be applied against to
* determine if a bonus is active
*
* @return returns all BonusObjs that are "active"
*/
public List<BonusObj> getActiveBonuses(final Equipment caller, final PlayerCharacter aPC)
{
final List<BonusObj> aList = new ArrayList<>();
for (BonusObj bonus : getBonusList(caller))
{
if (PrereqHandler.passesAll(bonus.getPrerequisiteList(), caller,
aPC))
{
aPC.setApplied(bonus, true);
aList.add(bonus);
}
}
return aList;
}
/**
* This is prohibited since the associations are stored on the Equipment.
* Thankfully, bonuses are usually exported through the Equipment, via
* getBonusList(Equipment) or via getActiveBonuses(Equipment, PC), not
* as a stand-alone behavior.
*/
@Override
public List<BonusObj> getBonusList(PlayerCharacter pc)
{
throw new UnsupportedOperationException(
"Cannot resolve bonuses on EqMod via PlayerCharacter - requires Equipment");
}
/**
* This method assumes that there can only be one bonus in any given
* Equipment modifier that uses %CHOICE. It retrieves the list of bonuses
* using the super classes getBonusList() and then examines each of them in
* turn. If it finds that one of the bonuses contains %CHOICE, it replaces
* it with a one new bonus object for every entry in "associated".
*
* @param e a PObject that has the associated bonuses
*
* @return a complete list of bonus objects with %CHOICE expanded to
* include one entry for each associated choice.
*/
@Override
public List<BonusObj> getBonusList(Equipment e)
{
return getBonusList(super.getBonusList(e), e.getAssociationList(this));
}
private List<BonusObj> getBonusList(List<BonusObj> bonusList,
List<String> associations)
{
ArrayList<BonusObj> myBonusList = new ArrayList<>(bonusList);
for (int i = myBonusList.size() - 1; i > -1; i--)
{
final BonusObj aBonus = myBonusList.get(i);
final String aString = aBonus.toString();
final int idx = aString.indexOf("%CHOICE");
if (idx >= 0)
{
// Add an entry for each of the associated list entries
for (String assoc : associations)
{
final BonusObj newBonus = Bonus.newBonus(Globals.getContext(), aString
.replaceAll(PERCENT_CHOICE_PATTERN, assoc));
if (aBonus.hasPrerequisites())
{
newBonus.clearPrerequisiteList();
for (Prerequisite prereq : aBonus.getPrerequisiteList())
{
try
{
newBonus.addPrerequisite(prereq.specify(assoc));
}
catch (CloneNotSupportedException e)
{
// TODO Handle this?
}
}
}
myBonusList.add(newBonus);
}
myBonusList.remove(aBonus);
}
}
return myBonusList;
}
/**
* Does this Equipment Modifier add aType to the equipment it is applied
* to? If aType begins with an " (Exclamation Mark) the " will
* be removed before checking the type.
*
* @param aType the type string to check for.
* @return Whether the item is of this type
*/
public boolean isIType(final String aType)
{
for (String s : getSafeListFor(ListKey.ITEM_TYPES))
{
if (aType.equalsIgnoreCase(s))
{
return true;
}
}
return false;
}
/**
* A list of Special properties tailored to the PC and the piece of
* equipment passed as arguments.
*
* @param caller The Equipment this modifier is applied to.
* @param pc The Pc that the Special Property will be tailored for
*
* @return a list of strings representing Special properties to be
* applied to the Equipment
*/
public List<String> getSpecialProperties(final Equipment caller, final PlayerCharacter pc)
{
final List<String> retList = new ArrayList<>();
for (SpecialProperty sp : getSafeListFor(ListKey.SPECIAL_PROPERTIES))
{
String propName = sp.getParsedText(pc, caller, this);
// TODO WTF is this loop doing? how many times does it expect "%CHOICE" to
// appear in the special property?
for (String assoc : caller.getAssociationList(this))
{
propName = propName.replaceFirst("%CHOICE", assoc);
}
if ((propName != null) && !propName.equals(""))
{
retList.add(propName);
}
}
return retList;
}
/**
* Get the bonus to
* @param aPC a Player Character object
* @param aType
* @param aName
* @param obj
* @return bonus
*/
public double bonusTo(
final PlayerCharacter aPC,
final String aType,
final String aName,
final Equipment obj)
{
return BonusCalc.bonusTo(this, aType, aName, obj, getBonusList(obj), aPC);
}
/**
* Clone an EquipmentModifier
*
* @return a clone of the EquipmentModifier
*/
@Override
public EquipmentModifier clone()
{
EquipmentModifier aObj = null;
try
{
aObj = (EquipmentModifier) super.clone();
}
catch (CloneNotSupportedException exc)
{
ShowMessageDelegate.showMessageDialog(
exc.getMessage(),
Constants.APPLICATION_NAME,
MessageType.ERROR);
}
return aObj;
}
/* TODO: This needs to call getEquipNamePortion until after 5.10, when it can
* be changed to a programmer useful string as per normal.
*/
/**
* Return a string representation of the EquipmentModifier.
*
* @return a String representation of the EquipmentModifier
*/
@Override
public String toString()
{
return getDisplayName();
}
public int getSR(Equipment parent, PlayerCharacter aPC)
{
SpellResistance sr = get(ObjectKey.SR);
if (sr == null)
{
return 0;
}
if (sr.getReduction().equals(CHOICE_FORMULA)&& parent.hasAssociations(this))
{
return Delta.parseInt(parent.getFirstAssociation(this));
}
return sr.getReduction().resolve(parent, true, aPC, getQualifiedKey()).intValue();
}
/**
* lets this object compare to others.
*
* @param o The object to compare to
*
* @return -1, 0 or 1 as per Comparator
*/
@Override
public int compareTo(final Object o)
{
if (o instanceof EquipmentModifier)
{
return getKeyName().compareTo(((CDOMObject) o).getKeyName());
}
return getKeyName().compareTo(o.toString());
}
@Override
public String getDisplayType()
{
List<Type> trueTypeList = getTrueTypeList(true);
return StringUtil.join(trueTypeList, ".");
}
}