/*
* AbilityUtilities.java
* Copyright 2001 (C) Bryan McRoberts <merton_monk@yahoo.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 Aug 25, 2005
* Refactored from PlayerCharacter, created on April 21, 2001, 2:15 PM
*
*
*/
package pcgen.core;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import pcgen.cdom.base.ChooseInformation;
import pcgen.cdom.base.TransitionChoice;
import pcgen.cdom.base.UserSelection;
import pcgen.cdom.content.CNAbility;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.helper.CNAbilitySelection;
import pcgen.core.analysis.AddObjectActions;
import pcgen.core.chooser.ChoiceManagerList;
import pcgen.core.chooser.ChooserUtilities;
import pcgen.core.utils.CoreUtility;
import pcgen.core.utils.LastGroupSeparator;
import pcgen.core.utils.LastGroupSeparator.GroupingMismatchException;
/**
* General utilities related to the Ability class.
*
* @author Bryan McRoberts <merton_monk@users.sourceforge.net>
*/
public final class AbilityUtilities
{
private AbilityUtilities ()
{
// private constructor, do nothing
}
public static void finaliseAbility(PlayerCharacter aPC, CNAbilitySelection cnas)
{
CNAbility cna = cnas.getCNAbility();
Ability ability = cna.getAbility();
/*
* This modifyChoice method is a bit like mod choices, but it uses a
* different tag to set the chooser string.
*/
TransitionChoice<CNAbility> mc = ability.get(ObjectKey.MODIFY_CHOICE);
if (mc != null)
{
mc.act(mc.driveChoice(aPC), ability, aPC);
}
for (TransitionChoice<Kit> kit : ability
.getSafeListFor(ListKey.KIT_CHOICE))
{
kit.act(kit.driveChoice(aPC), ability, aPC);
}
aPC.adjustMoveRates();
AddObjectActions.globalChecks(ability, aPC);
/*
* Protection for CODE-1240. Note the better solution is when facets
* are association aware and thus trigger a change when an
* association is added. - thpr
*/
aPC.calcActiveBonuses();
}
public static void adjustPool(final Ability ability,
final PlayerCharacter aPC, final boolean addIt)
{
double abilityCount =
ability.getSafe(ObjectKey.SELECTION_COST).doubleValue();
if (addIt)
{
abilityCount *= -1;
}
aPC.adjustAbilities(AbilityCategory.FEAT, BigDecimal.valueOf(abilityCount));
}
/**
* Extracts the choiceless form of a name, for example, with all choices removed
*
* @param name
*
* @return the name with sub-choices stripped from it
*/
public static String removeChoicesFromName(String name)
{
LastGroupSeparator lgs = new LastGroupSeparator(name);
lgs.process();
return lgs.getRoot().trim();
}
/**
* Takes a string of the form "foo (bar, baz)", populates the array with ["bar", "baz"]
* and returns foo. All strings returned by this function have had leading.trailing
* whitespace removed.
*
* @param name The full name with stuff in parenthesis
* @param specifics a list which will contain the specifics after the operation has
* completed
*
* @return the name with sub-choices stripped from it
* @throws GroupingMismatchException If there are mismatched brackets
*/
public static String getUndecoratedName(
final String name,
final Collection<String> specifics) throws GroupingMismatchException
{
LastGroupSeparator lgs = new LastGroupSeparator(name);
String subName = lgs.process();
String altName = lgs.getRoot();
specifics.clear();
// we want what is inside the outermost parenthesis.
if (subName != null)
{
specifics.addAll(CoreUtility.split(subName, ','));
}
return altName.trim();
}
/**
* Whether an association has already been selected for this PC.
* to the associated list of this
*
* @return true if the association has already been selected
*/
public static boolean alreadySelected(PlayerCharacter pc, Ability ability,
String selection, boolean allowStack)
{
Collection<CNAbility> cnAbilities = pc.getMatchingCNAbilities(ability);
if (cnAbilities.isEmpty())
{
//Don't have any form of it
return false;
}
if (!ability.getSafe(ObjectKey.MULTIPLE_ALLOWED))
{
//Based on key name / category match
return true;
}
if (allowStack && ability.getSafe(ObjectKey.STACKS))
{
//Must allow it because it stacks
return false;
}
ChooseInformation<?> info = ability.get(ObjectKey.CHOOSE_INFO);
Object decoded =
info.decodeChoice(Globals.getContext(), selection);
for (CNAbility cna : cnAbilities)
{
List<?> oldSelections = pc.getDetailedAssociations(cna);
if ((oldSelections != null) && oldSelections.contains(decoded))
{
return true;
}
}
return false;
}
/**
* Identify if the object passed in is a feat.
* @param obj The object to be checked.
* @return true if this is a feat, false if not.
*/
public static boolean isFeat(Object obj)
{
if (!(obj instanceof Ability))
{
return false;
}
Ability ability = (Ability) obj;
if (ability.getCDOMCategory() == null)
{
return false;
}
return ability.getCDOMCategory() == AbilityCategory.FEAT
|| (ability.getCDOMCategory().getParentCategory() == AbilityCategory.FEAT);
}
public static Ability validateCNAList(List<CNAbility> list)
{
Ability a = null;
for (CNAbility cna : list)
{
if (a == null)
{
a = cna.getAbility();
}
else
{
if (!cna.getAbility().getKeyName().equals(a.getKeyName())
|| !a.getCDOMCategory().equals(
cna.getAbilityCategory().getParentCategory()))
{
throw new IllegalArgumentException(
"CNAbility list must be a consistent list of Abilities (same object)");
}
}
}
return a;
}
public static void driveChooseAndAdd(CNAbility cna, PlayerCharacter pc,
boolean toAdd)
{
Ability ability = cna.getAbility();
if (!ability.getSafe(ObjectKey.MULTIPLE_ALLOWED))
{
CNAbilitySelection cnas = new CNAbilitySelection(cna);
if (toAdd)
{
pc.addAbility(cnas, UserSelection.getInstance(),
UserSelection.getInstance());
}
else
{
pc.removeAbility(cnas, UserSelection.getInstance(),
UserSelection.getInstance());
}
}
AbilityCategory category = (AbilityCategory) cna.getAbilityCategory();
// how many sub-choices to make
ArrayList<String> reservedList = new ArrayList<>();
ChoiceManagerList<?> aMan =
ChooserUtilities.getConfiguredController(cna, pc, category,
reservedList);
if (aMan != null)
{
processSelection(pc, cna, aMan, toAdd);
return;
}
//TODO Log error? (or MULT:NO?)
}
private static <T> void processSelection(
PlayerCharacter pc, CNAbility cna, ChoiceManagerList<T> aMan, boolean toAdd)
{
ArrayList<T> availableList = new ArrayList<>();
ArrayList<T> selectedList = new ArrayList<>();
aMan.getChoices(pc, availableList, selectedList);
if (availableList.isEmpty() && selectedList.isEmpty())
{
//TODO Log error? (ignored choice?)
return;
}
List<T> origSelections = new ArrayList<>(selectedList);
List<T> removedSelections = new ArrayList<>(selectedList);
ArrayList<String> reservedList = new ArrayList<>();
List<T> newSelections;
if (toAdd)
{
newSelections =
aMan.doChooser(pc, availableList, selectedList,
reservedList);
}
else
{
newSelections =
aMan.doChooserRemove(pc, availableList, selectedList,
reservedList);
}
//Need to use only the new ones
for (T obj : newSelections)
{
removedSelections.remove(obj);
}
//removedSelections.removeAll(newSelections);
for (T obj : origSelections)
{
newSelections.remove(obj);
}
//newSelections.removeAll(origSelections);
for (T sel : newSelections)
{
String selection = aMan.encodeChoice(sel);
CNAbilitySelection cnas = new CNAbilitySelection(cna, selection);
pc.addAbility(cnas, UserSelection.getInstance(),
UserSelection.getInstance());
}
for (T sel : removedSelections)
{
String selection = aMan.encodeChoice(sel);
CNAbilitySelection cnas = new CNAbilitySelection(cna, selection);
pc.removeAbility(cnas, UserSelection.getInstance(),
UserSelection.getInstance());
}
}
}