/*
* KitGear.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 September 23, 2002, 8:58 PM
*
* $Id$
*/
package pcgen.core.kit;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import pcgen.base.formula.Formula;
import pcgen.base.util.NamedFormula;
import pcgen.cdom.base.CDOMReference;
import pcgen.cdom.base.Constants;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.helper.EqModRef;
import pcgen.cdom.reference.CDOMSingleRef;
import pcgen.core.Equipment;
import pcgen.core.EquipmentModifier;
import pcgen.core.EquipmentUtilities;
import pcgen.core.Globals;
import pcgen.core.Kit;
import pcgen.core.PlayerCharacter;
import pcgen.core.SizeAdjustment;
import pcgen.core.character.EquipSet;
/**
* {@code KitGear}.
*
* @author Greg Bingleman <byngl@hotmail.com>
*/
public final class KitGear extends BaseKit
{
private Formula quantity;
private Integer maxCost;
private CDOMReference<Equipment> equip;
private List<EqModRef> mods;
private String theLocationStr = null;
private Boolean sizeToPC;
private CDOMSingleRef<SizeAdjustment> size;
// These members store the state of an instance of this class. They are
// not cloned.
private transient Formula actingQuantity;
private transient Integer actingCost;
private transient List<EqModRef> actingMods;
private transient String actingLocation;
private transient SizeAdjustment actingSize;
private transient Equipment theEquipment = null;
private transient int theQty = 0;
private transient String theLocation = "";
private transient BigDecimal theCost = BigDecimal.ZERO;
/**
* Set the location of the gear
* @param aLocation
*/
public void setLocation(final String aLocation)
{
theLocationStr = aLocation;
}
/**
* Get the location of the gear
* @return location of the gear
*/
public String getLocation()
{
return theLocationStr;
}
@Override
public String toString()
{
final StringBuilder info = new StringBuilder(100);
if (quantity != null)
{
String qtyStr = String.valueOf(quantity);
if (!"1".equals(qtyStr))
{
info.append(quantity).append('x');
}
}
info.append(equip == null ? "null" : equip.getLSTformat(false));
if (mods != null)
{
info.append(" (");
boolean needsSlash = false;
for (EqModRef modRef : mods)
{
if (needsSlash)
{
info.append('/');
}
needsSlash = true;
info.append(modRef.getRef().getLSTformat(false));
for (String s : modRef.getChoices())
{
info.append(Constants.PIPE).append(s);
}
}
info.append(')');
}
return info.toString();
}
private void processLookups(Kit aKit, PlayerCharacter aPC)
{
Collection<NamedFormula> lookups = getLookups();
if (lookups == null)
{
return;
}
for (NamedFormula lookup : lookups)
{
KitTable kt = aKit.getTable(lookup.getName());
KitGear gear =
kt.getEntry(aPC, lookup.getFormula().resolve(aPC, "")
.intValue());
gear.processLookups(aKit, aPC);
overlayGear(gear);
}
}
private void overlayGear(KitGear gear)
{
if (gear.quantity != null)
{
actingQuantity = gear.quantity;
}
if (gear.maxCost != null)
{
actingCost = gear.maxCost;
}
if (gear.mods != null)
{
actingMods.addAll(gear.mods);
}
if (gear.theLocationStr != null)
{
actingLocation = gear.theLocationStr;
}
if (gear.size != null)
{
actingSize = gear.size.get();
}
}
@Override
public boolean testApply(Kit aKit, PlayerCharacter aPC,
List<String> warnings)
{
actingQuantity = quantity;
actingCost = maxCost;
actingMods = mods == null ? null : new ArrayList<>(mods);
actingLocation = theLocationStr;
if (size != null)
{
actingSize = size.get();
}
theEquipment = null;
theQty = 0;
theLocation = "";
theCost = BigDecimal.ZERO;
processLookups(aKit, aPC);
int aBuyRate = aKit.getBuyRate(aPC);
final BigDecimal pcGold = aPC.getGold();
final BigDecimal fixedTotalCost = aKit.getTotalCost(aPC);
if (fixedTotalCost != null)
{
// We are going to charge fr the kit once, rather than for every piece of gear
aBuyRate = 0;
}
List<Equipment> eqList =
new ArrayList<>(equip.getContainedObjects());
if (actingCost != null)
{
final BigDecimal bdMaxCost =
new BigDecimal(Integer.toString(actingCost));
for (Iterator<Equipment> i = eqList.iterator(); i.hasNext();)
{
if (i.next().getCost(aPC).compareTo(bdMaxCost) > 0)
{
i.remove();
}
}
}
if (eqList.size() == 1)
{
theEquipment = eqList.get(0);
}
else
{
List<Equipment> selected = new ArrayList<>(1);
selected = Globals.getChoiceFromList("Choose equipment", eqList, selected, 1, aPC);
if (selected.size() == 1)
{
theEquipment = selected.get(0);
}
}
//
// TODO: Check to see if the user has selected an item that
// requires modification (MOD:R)
theEquipment = theEquipment.clone();
//
// Resize item for character--never resize weapons or ammo, unless it's a
// natural (weapon)
boolean tryResize = false;
SizeAdjustment sizeToSet = aPC.getSizeAdjustment();
if (actingSize == null)
{
if (theEquipment.isType("Natural") || (sizeToPC != null && sizeToPC)
|| (!theEquipment.isWeapon() && !theEquipment.isAmmunition()))
{
tryResize =
Globals.canResizeHaveEffect(theEquipment, null);
}
}
else
{
if (sizeToPC != null && sizeToPC)
{
tryResize =
Globals.canResizeHaveEffect(theEquipment, null);
}
else
{
sizeToSet = actingSize;
tryResize = true;
}
}
if (tryResize)
{
theEquipment.resizeItem(aPC, sizeToSet);
}
else
{
// We need setBase() called. The only way to do that is to resize.
// We will set the size to itself.
theEquipment.resizeItem(aPC, theEquipment.getSafe(ObjectKey.SIZE).get());
}
//
// Find and add any equipment modifiers
//
if (actingMods != null)
{
for (EqModRef modref : actingMods)
{
/*
* Going to do this the long way for now to avoid ugly entanglements
*/
StringBuilder sb = new StringBuilder(50);
EquipmentModifier eqMod = modref.getRef().get();
sb.append(eqMod.getKeyName());
for (String assoc : modref.getChoices())
{
sb.append(Constants.PIPE).append(eval(aPC, assoc));
}
theEquipment.addEqModifiers(sb.toString(), true);
}
}
if (tryResize || (actingMods != null))
{
theEquipment.nameItemFromModifiers(aPC);
}
if (actingQuantity == null)
{
theQty = 1;
}
else
{
theQty = actingQuantity.resolve(aPC, "").intValue();
}
int origQty = theQty;
final BigDecimal eqCost = theEquipment.getCost(aPC);
if (aBuyRate != 0)
{
if (fixedTotalCost == null)
{
final BigDecimal bdBuyRate =
new BigDecimal(Integer.toString(aBuyRate))
.multiply(new BigDecimal("0.01"));
// Check to see if the PC can afford to buy this equipment. If
// not, then decrement the quantity and try again.
theCost =
eqCost.multiply(new BigDecimal(Integer.toString(theQty)))
.multiply(bdBuyRate);
while (theQty > 0)
{
if (theCost.compareTo(pcGold) <= 0) // PC has enough?
{
break;
}
theCost =
eqCost.multiply(
new BigDecimal(Integer.toString(--theQty)))
.multiply(bdBuyRate);
}
}
aPC.setGold(aPC.getGold().subtract(theCost));
}
boolean outOfFunds = false;
if (theQty != origQty)
{
outOfFunds = true;
}
if (outOfFunds)
{
warnings.add("GEAR: Could not purchase " + (origQty - theQty) + " "
+ theEquipment.getName() + ". Not enough funds.");
}
//
// Can't buy none
//
if (theQty == 0)
{
return false;
}
Equipment testApplyEquipment = theEquipment.clone();
// Temporarily add the equipment so we can see if we can equip it.
testApplyEquipment.setQty(new Float(theQty));
aPC.addEquipment(testApplyEquipment);
Equipment theTarget = null;
if (actingLocation != null)
{
theLocation = actingLocation;
if (!theLocation.equalsIgnoreCase("DEFAULT")
&& !theLocation.equalsIgnoreCase(Constants.EQUIP_LOCATION_CARRIED)
&& !theLocation.equalsIgnoreCase(Constants.EQUIP_LOCATION_NOTCARRIED)
&& !theLocation.equalsIgnoreCase(Constants.EQUIP_LOCATION_EQUIPPED))
{
theTarget =
EquipmentUtilities.findEquipmentByBaseKey(
aPC.getEquipmentMasterList(), theLocation);
}
else if (theLocation.equalsIgnoreCase("DEFAULT"))
{
theLocation = "";
}
EquipSet eSet = null;
if (theTarget != null)
{
eSet = aPC.getEquipSetForItem(aPC.getEquipSetByIdPath(EquipSet.DEFAULT_SET_PATH),
theTarget);
}
if (eSet == null)
{
eSet = aPC.getEquipSetByIdPath(EquipSet.DEFAULT_SET_PATH);
}
if (eSet == null)
{
warnings.add("GEAR: Could not find location " + theLocation
+ " for gear " + testApplyEquipment.getName() + ".");
return false;
}
else
{
EquipSet eqSet = aPC.addEquipToTarget(eSet, theTarget, theLocation,
testApplyEquipment, new Float(-1.0f));
if (eqSet == null)
{
warnings.add("GEAR: Could not equip " + testApplyEquipment.getName()
+ " to " + theLocation);
}
}
}
return true;
}
@Override
public void apply(PlayerCharacter aPC)
{
final Equipment existing =
aPC.getEquipmentNamed(theEquipment.getName());
if (existing == null)
{
theEquipment.setQty(new Float(theQty));
aPC.addEquipment(theEquipment);
Globals.getContext().getReferenceContext().importObject(theEquipment);
}
else
{
existing.setQty(existing.qty() + theQty);
}
// If the target is null, try and grab it incase it is there now
Equipment theTarget = null;
EquipSet eSet;
if (!theLocation.equalsIgnoreCase(Constants.EQUIP_LOCATION_CARRIED)
&& !theLocation.equalsIgnoreCase(Constants.EQUIP_LOCATION_NOTCARRIED)
&& !theLocation.equalsIgnoreCase(Constants.EQUIP_LOCATION_EQUIPPED))
{
theTarget =
EquipmentUtilities.findEquipmentByBaseKey(
aPC.getEquipmentMasterList(), theLocation);
if (theTarget == null)
{
theLocation = Constants.EQUIP_LOCATION_CARRIED;
}
}
if (theTarget == null)
{
eSet = aPC.getEquipSetByIdPath(EquipSet.DEFAULT_SET_PATH);
}
else
{
eSet =
aPC.getEquipSetForItem(aPC.getEquipSetByIdPath(EquipSet.DEFAULT_SET_PATH),
theTarget);
}
//
// Equip the item to the default EquipSet.
//
aPC.addEquipToTarget(eSet, theTarget, theLocation, theEquipment,
new Float(theQty));
aPC.setGold(aPC.getGold().subtract(theCost));
}
@Override
public String getObjectName()
{
return "Gear";
}
public void setQuantity(Formula formula)
{
quantity = formula;
}
public Formula getQuantity()
{
return quantity;
}
public void setMaxCost(Integer quan)
{
maxCost = quan;
}
public Integer getMaxCost()
{
return maxCost;
}
public void setEquipment(CDOMReference<Equipment> reference)
{
equip = reference;
}
public CDOMReference<Equipment> getEquipment()
{
return equip;
}
public void setSizeToPC(Boolean b)
{
sizeToPC = b;
}
public Boolean getSizeToPC()
{
return sizeToPC;
}
public void setSize(CDOMSingleRef<SizeAdjustment> sa)
{
size = sa;
}
public CDOMSingleRef<SizeAdjustment> getSize()
{
return size;
}
private List<NamedFormula> lookupList;
public void loadLookup(String tableEntry, Formula f)
{
if (lookupList == null)
{
lookupList = new LinkedList<>();
}
lookupList.add(new NamedFormula(tableEntry, f));
}
public Collection<NamedFormula> getLookups()
{
return lookupList;
}
public void addModRef(EqModRef modRef)
{
if (mods == null)
{
mods = new LinkedList<>();
}
mods.add(modRef);
}
public boolean hasEqMods()
{
return mods != null && !mods.isEmpty();
}
public List<EqModRef> getEqMods()
{
return Collections.unmodifiableList(mods);
}
}