/*
* KitAbilities.java
* Copyright 2005 (C) Andrew Wilson <nuance@sourceforge.net>
*
* 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 10 September 2005
*
* $Id$
*/
package pcgen.core.kit;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import pcgen.cdom.base.CDOMReference;
import pcgen.cdom.base.UserSelection;
import pcgen.cdom.content.CNAbility;
import pcgen.cdom.content.CNAbilityFactory;
import pcgen.cdom.enumeration.Nature;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.helper.CNAbilitySelection;
import pcgen.cdom.reference.CDOMSingleRef;
import pcgen.cdom.reference.ReferenceUtilities;
import pcgen.core.Ability;
import pcgen.core.AbilityCategory;
import pcgen.core.Globals;
import pcgen.core.Kit;
import pcgen.core.PlayerCharacter;
/**
* {@code KitAbiltiies}.
*
* @author Andrew Wilson <nuance@sourceforge.net>
*/
public final class KitAbilities extends BaseKit
{
private Boolean free = null;
private Integer choiceCount;
private List<CDOMReference<Ability>> abilities =
new ArrayList<>();
// These members store the state of an instance of this class. They are
// not cloned.
private transient List<CNAbilitySelection> abilitiesToAdd = null;
private CDOMSingleRef<AbilityCategory> catRef;
/**
* Set whether the kit is free.
*
* @param argFree true if the kit is free
*/
public void setFree(Boolean argFree)
{
free = argFree;
}
/**
* Returns a string representation of the object.
*
* @return the string representation of the object
*
* @see Object#toString()
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
if ((choiceCount != null) || (abilities.size() != 1))
{
sb.append(getSafeCount()).append(" of ");
}
boolean firstDone = false;
for (CDOMReference<Ability> ref : abilities)
{
if (firstDone)
{
sb.append("; ");
}
firstDone = true;
String choice = ref.getChoice();
for (Ability a : ref.getContainedObjects())
{
if (a != null)
{
sb.append(a.getKeyName());
if (choice != null)
{
sb.append(" (");
sb.append(choice);
sb.append(')');
}
}
}
}
if (isFree())
{
sb.append(" (free)");
}
return sb.toString();
}
@Override
public boolean testApply(Kit aKit, PlayerCharacter aPC,
List<String> warnings)
{
abilitiesToAdd = new ArrayList<>();
double minCost = Double.MAX_VALUE;
List<AbilitySelection> available = new ArrayList<>();
for (CDOMReference<Ability> ref : abilities)
{
String choice = ref.getChoice();
for (Ability a : ref.getContainedObjects())
{
if (a == null)
{
warnings.add("ABILITY: " + ref + " could not be found.");
minCost = 0;
continue;
}
if (a.getCost() < minCost)
{
minCost = a.getCost();
}
if ((choice == null) && a.getSafe(ObjectKey.MULTIPLE_ALLOWED))
{
available.add(new AbilitySelection(a, ""));
}
else
{
available.add(new AbilitySelection(a, choice));
}
}
}
int numberOfChoices = getSafeCount();
// Can't choose more entries than there are...
// TODO this fails if SELECT != 1
if (numberOfChoices > available.size())
{
numberOfChoices = available.size();
}
/*
* this section needs to be rewritten once we determine how
* the new Ability Pools are going to work
*/
AbilityCategory category = catRef.get();
boolean tooManyAbilities = false;
// Don't allow choosing of more than allotted number of abilities
int maxChoices =
minCost > 0.0d ? aPC.getAvailableAbilityPool(category)
.divide(new BigDecimal(minCost)).intValue()
: numberOfChoices;
if (!isFree() && numberOfChoices > maxChoices)
{
numberOfChoices = maxChoices;
tooManyAbilities = true;
}
if (!isFree() && numberOfChoices == 0)
{
warnings.add("ABILITY: Not enough " + category.getPluralName()
+ " available to take \"" + this + "\"");
return false;
}
List<AbilitySelection> selected;
if (numberOfChoices == available.size())
{
selected = available;
}
else
{
selected = new ArrayList<>();
// Force user to make enough selections
while (true)
{
selected =
Globals.getChoiceFromList("Choose abilities",
available, new ArrayList<>(),
numberOfChoices, aPC);
if (!selected.isEmpty())
{
break;
}
}
}
// Add to list of things to add to the character
for (AbilitySelection as : selected)
{
Ability ability = as.ability;
if (isFree())
{
// Need to pay for it first
if (free)
{
aPC.adjustAbilities(category, new BigDecimal(1));
}
}
if (ability.getCost() > aPC.getAvailableAbilityPool(category).doubleValue())
{
tooManyAbilities = true;
}
else
{
CNAbility cna = CNAbilityFactory.getCNAbility(category, Nature.NORMAL, ability);
CNAbilitySelection cnas = new CNAbilitySelection(cna, as.selection);
abilitiesToAdd.add(cnas);
aPC.addAbility(cnas, UserSelection.getInstance(), this);
}
}
if (tooManyAbilities)
{
warnings
.add("ABILITY: Some Abilities were not granted -- not enough remaining feats");
return false;
}
return true;
}
@Override
public void apply(PlayerCharacter aPC)
{
for (CNAbilitySelection cnas : abilitiesToAdd)
{
aPC.addAbility(cnas, UserSelection.getInstance(), UserSelection.getInstance());
if (isFree())
{
AbilityCategory category = catRef.get();
aPC.adjustAbilities(category, new BigDecimal(1));
}
}
}
/**
* Returns if the skill will be purchased for free.
* @return <code>true</code> if the skill will be free
*/
public boolean isFree()
{
return free != null && free;
}
@Override
public String getObjectName()
{
return "Abilities";
}
public Boolean getFree()
{
return free;
}
public void setCount(Integer quan)
{
choiceCount = quan;
}
public Integer getCount()
{
return choiceCount;
}
public int getSafeCount()
{
return choiceCount == null ? 1 : choiceCount;
}
public void addAbility(CDOMReference<Ability> ref)
{
abilities.add(ref);
}
public Collection<CDOMReference<Ability>> getAbilityKeys()
{
Set<CDOMReference<Ability>> wc = new TreeSet<>(
ReferenceUtilities.REFERENCE_SORTER);
wc.addAll(abilities);
return wc;
}
private static class AbilitySelection implements Comparable<AbilitySelection>
{
public final Ability ability;
public final String selection;
public AbilitySelection(Ability a, String sel)
{
ability = a;
selection = sel;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append(ability.getDisplayName());
if (selection != null)
{
sb.append(" (").append(selection).append(')');
}
return sb.toString();
}
@Override
public int compareTo(AbilitySelection o)
{
int base = ability.compareTo(o.ability);
if (base != 0)
{
return base;
}
if (selection == null)
{
return o.selection == null ? 0 : -1;
}
return o.selection == null ? 1 : selection
.compareToIgnoreCase(o.selection);
}
}
public void setCategory(CDOMSingleRef<AbilityCategory> ac)
{
catRef = ac;
}
public CDOMSingleRef<AbilityCategory> getCategory()
{
return catRef;
}
}