/*
* CharacterFacadeImpl.java
* Copyright 2009 (C) James Dempsey
*
* 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 12/05/2009 6:43:46 PM
*
* $Id$
*/
package pcgen.gui2.facade;
import java.awt.Rectangle;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.swing.undo.UndoManager;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import pcgen.cdom.base.AssociatedPrereqObject;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.base.CDOMReference;
import pcgen.cdom.base.ChooseDriver;
import pcgen.cdom.base.Constants;
import pcgen.cdom.content.CNAbility;
import pcgen.cdom.content.VarModifier;
import pcgen.cdom.enumeration.BiographyField;
import pcgen.cdom.enumeration.CharID;
import pcgen.cdom.enumeration.EquipmentLocation;
import pcgen.cdom.enumeration.Gender;
import pcgen.cdom.enumeration.Handed;
import pcgen.cdom.enumeration.IntegerKey;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.Nature;
import pcgen.cdom.enumeration.NumericPCAttribute;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.enumeration.PCAttribute;
import pcgen.cdom.enumeration.PCStringKey;
import pcgen.cdom.enumeration.SkillFilter;
import pcgen.cdom.enumeration.StringKey;
import pcgen.cdom.enumeration.Type;
import pcgen.cdom.facet.AutoEquipmentFacet;
import pcgen.cdom.facet.FacetLibrary;
import pcgen.cdom.facet.event.DataFacetChangeEvent;
import pcgen.cdom.facet.event.DataFacetChangeListener;
import pcgen.cdom.facet.fact.XPFacet;
import pcgen.cdom.facet.model.LanguageFacet;
import pcgen.cdom.facet.model.TemplateFacet;
import pcgen.cdom.helper.ClassSource;
import pcgen.cdom.inst.EquipmentHead;
import pcgen.cdom.inst.PCClassLevel;
import pcgen.cdom.meta.CorePerspective;
import pcgen.cdom.reference.CDOMDirectSingleRef;
import pcgen.cdom.reference.CDOMSingleRef;
import pcgen.core.Ability;
import pcgen.core.AbilityCategory;
import pcgen.core.AgeSet;
import pcgen.core.BonusManager.TempBonusInfo;
import pcgen.core.Deity;
import pcgen.core.Domain;
import pcgen.core.Equipment;
import pcgen.core.EquipmentModifier;
import pcgen.core.GameMode;
import pcgen.core.GearBuySellScheme;
import pcgen.core.Globals;
import pcgen.core.Kit;
import pcgen.core.Language;
import pcgen.core.PCAlignment;
import pcgen.core.PCClass;
import pcgen.core.PCStat;
import pcgen.core.PCTemplate;
import pcgen.core.PObject;
import pcgen.core.PlayerCharacter;
import pcgen.core.QualifiedObject;
import pcgen.core.Race;
import pcgen.core.RollingMethods;
import pcgen.core.RuleConstants;
import pcgen.core.SettingsHandler;
import pcgen.core.SimpleFacadeImpl;
import pcgen.core.SizeAdjustment;
import pcgen.core.Skill;
import pcgen.core.VariableProcessor;
import pcgen.core.analysis.DomainApplication;
import pcgen.core.analysis.SkillRankControl;
import pcgen.core.analysis.SpellCountCalc;
import pcgen.core.character.CharacterSpell;
import pcgen.core.character.EquipSet;
import pcgen.core.character.Follower;
import pcgen.core.chooser.ChoiceManagerList;
import pcgen.core.chooser.ChooserUtilities;
import pcgen.core.display.BonusDisplay;
import pcgen.core.display.CharacterDisplay;
import pcgen.core.kit.BaseKit;
import pcgen.core.pclevelinfo.PCLevelInfo;
import pcgen.core.prereq.PrereqHandler;
import pcgen.core.spell.Spell;
import pcgen.core.utils.CoreUtility;
import pcgen.core.utils.MessageType;
import pcgen.core.utils.ShowMessageDelegate;
import pcgen.facade.core.AbilityCategoryFacade;
import pcgen.facade.core.AbilityFacade;
import pcgen.facade.core.AlignmentFacade;
import pcgen.facade.core.CampaignFacade;
import pcgen.facade.core.CharacterFacade;
import pcgen.facade.core.CharacterLevelFacade;
import pcgen.facade.core.CharacterLevelsFacade;
import pcgen.facade.core.CharacterLevelsFacade.CharacterLevelEvent;
import pcgen.facade.core.CharacterLevelsFacade.HitPointListener;
import pcgen.facade.core.CharacterStubFacade;
import pcgen.facade.core.ClassFacade;
import pcgen.facade.core.CompanionSupportFacade;
import pcgen.facade.core.CoreViewNodeFacade;
import pcgen.facade.core.DataSetFacade;
import pcgen.facade.core.DeityFacade;
import pcgen.facade.core.DescriptionFacade;
import pcgen.facade.core.DomainFacade;
import pcgen.facade.core.EquipModFacade;
import pcgen.facade.core.EquipmentFacade;
import pcgen.facade.core.EquipmentListFacade;
import pcgen.facade.core.EquipmentListFacade.EquipmentListEvent;
import pcgen.facade.core.EquipmentListFacade.EquipmentListListener;
import pcgen.facade.core.EquipmentSetFacade;
import pcgen.facade.core.GearBuySellFacade;
import pcgen.facade.core.GenderFacade;
import pcgen.facade.core.HandedFacade;
import pcgen.facade.core.InfoFacade;
import pcgen.facade.core.InfoFactory;
import pcgen.facade.core.KitFacade;
import pcgen.facade.core.LanguageChooserFacade;
import pcgen.facade.core.LanguageFacade;
import pcgen.facade.core.RaceFacade;
import pcgen.facade.core.SimpleFacade;
import pcgen.facade.core.SkillFacade;
import pcgen.facade.core.SpellFacade;
import pcgen.facade.core.SpellSupportFacade;
import pcgen.facade.core.StatFacade;
import pcgen.facade.core.TempBonusFacade;
import pcgen.facade.core.TemplateFacade;
import pcgen.facade.core.TodoFacade;
import pcgen.facade.core.UIDelegate;
import pcgen.facade.core.UIDelegate.CustomEquipResult;
import pcgen.facade.util.DefaultListFacade;
import pcgen.facade.util.DefaultReferenceFacade;
import pcgen.facade.util.ListFacade;
import pcgen.facade.util.ListFacades;
import pcgen.facade.util.ReferenceFacade;
import pcgen.facade.util.WriteableReferenceFacade;
import pcgen.facade.util.event.ChangeListener;
import pcgen.facade.util.event.ListEvent;
import pcgen.facade.util.event.ListListener;
import pcgen.gui2.UIPropertyContext;
import pcgen.gui2.util.HtmlInfoBuilder;
import pcgen.io.ExportException;
import pcgen.io.ExportHandler;
import pcgen.io.PCGIOHandler;
import pcgen.output.channel.ChannelCompatibility;
import pcgen.pluginmgr.PluginManager;
import pcgen.pluginmgr.messages.PlayerCharacterWasClosedMessage;
import pcgen.system.CharacterManager;
import pcgen.system.LanguageBundle;
import pcgen.system.PCGenSettings;
import pcgen.util.Logging;
import pcgen.util.enumeration.Load;
import pcgen.util.enumeration.Tab;
import pcgen.util.enumeration.View;
/**
* The Class {@code CharacterFacadeImpl} is an implementation of
* the {@link CharacterFacade} interface for the new user interface. It is
* intended to provide a full implementation of the new ui/core
* interaction layer.
* <p>
* <b>Issues needing resolution:</b>
* <ul>
* <li>Who is responsible for undo management and how will it work?</li>
* </ul>
* <br>
*
* @author James Dempsey <jdempsey@users.sourceforge.net>
*/
public class CharacterFacadeImpl implements CharacterFacade, EquipmentListListener, ListListener<EquipmentFacade>,
HitPointListener
{
private static PlayerCharacter DUMMY_PC = new PlayerCharacter();
private List<ClassFacade> pcClasses;
private DefaultListFacade<TempBonusFacade> appliedTempBonuses;
private DefaultListFacade<TempBonusFacade> availTempBonuses;
private DefaultReferenceFacade<AlignmentFacade> alignment;
private DefaultListFacade<EquipmentSetFacade> equipmentSets;
private DefaultReferenceFacade<GenderFacade> gender;
private DefaultListFacade<CharacterLevelFacade> pcClassLevels;
private DefaultListFacade<HandedFacade> availHands;
private DefaultListFacade<GenderFacade> availGenders;
private Map<StatFacade, WriteableReferenceFacade<Number>> statScoreMap;
private UndoManager undoManager;
private DelegatingDataSet dataSet;
private DefaultReferenceFacade<RaceFacade> race;
private DefaultReferenceFacade<DeityFacade> deity;
private DefaultReferenceFacade<String> tabName;
private DefaultReferenceFacade<String> name;
private DefaultReferenceFacade<String> playersName;
private PlayerCharacter theCharacter;
private CharacterDisplay charDisplay;
private DefaultReferenceFacade<EquipmentSetFacade> equipSet;
private DefaultListFacade<LanguageFacade> languages;
private EquipmentListFacadeImpl purchasedEquip;
private DefaultReferenceFacade<File> file;
private DefaultReferenceFacade<HandedFacade> handedness;
private UIDelegate delegate;
private Set<Language> autoLanguagesCache;
private CharacterLevelsFacadeImpl charLevelsFacade;
private DefaultReferenceFacade<Integer> currentXP;
private DefaultReferenceFacade<Integer> xpForNextlevel;
private DefaultReferenceFacade<String> xpTableName;
private DefaultReferenceFacade<String> characterType;
private DefaultReferenceFacade<String> previewSheet;
private DefaultReferenceFacade<SkillFilter> skillFilter;
private DefaultReferenceFacade<Integer> age;
private DefaultReferenceFacade<SimpleFacade> ageCategory;
private DefaultListFacade<SimpleFacade> ageCategoryList;
private DefaultReferenceFacade<String> poolPointText;
private DefaultReferenceFacade<String> statTotalLabelText;
private DefaultReferenceFacade<String> statTotalText;
private DefaultReferenceFacade<String> modTotalLabelText;
private DefaultReferenceFacade<String> modTotalText;
private DefaultReferenceFacade<Integer> numBonusLang;
private DefaultReferenceFacade<Integer> numSkillLang;
private DefaultReferenceFacade<Integer> hpRef;
private DefaultReferenceFacade<Integer> rollMethodRef;
private DefaultReferenceFacade<String> carriedWeightRef;
private DefaultReferenceFacade<String> loadRef;
private DefaultReferenceFacade<String> weightLimitRef;
private DefaultListFacade<DomainFacade> domains;
private DefaultListFacade<DomainFacade> availDomains;
private DefaultReferenceFacade<Integer> maxDomains;
private DefaultReferenceFacade<Integer> remainingDomains;
private DefaultListFacade<TemplateFacade> templates;
private DefaultListFacade<RaceFacade> raceList;
private DefaultListFacade<KitFacade> kitList;
private DefaultReferenceFacade<File> portrait;
private RectangleReference cropRect;
private String selectedGender;
private List<Language> currBonusLangs;
private DefaultReferenceFacade<String> skinColor;
private DefaultReferenceFacade<String> hairColor;
private DefaultReferenceFacade<String> eyeColor;
private DefaultReferenceFacade<Integer> heightRef;
private DefaultReferenceFacade<Integer> weightRef;
private DefaultReferenceFacade<BigDecimal> fundsRef;
private DefaultReferenceFacade<BigDecimal> wealthRef;
private DefaultReferenceFacade<GearBuySellFacade> gearBuySellSchemeRef;
private Gui2InfoFactory infoFactory;
private CharacterAbilities characterAbilities;
private DescriptionFacade descriptionFacade;
private SpellSupportFacadeImpl spellSupportFacade;
private CompanionSupportFacadeImpl companionSupportFacade;
private TodoManager todoManager;
private boolean allowDebt;
private int lastExportCharSerial = 0;
private PlayerCharacter lastExportChar = null;
private LanguageListener langListener;
private TemplateListener templateListener;
private XPListener xpListener;
private AutoEquipListener autoEquipListener;
/**
* Create a new character facade for an existing character.
*
* @param pc The character to be represented
* @param delegate the UIDelegate for this CharacterFacade
* @param dataSetFacade The data set in use for the character
*/
public CharacterFacadeImpl(PlayerCharacter pc, UIDelegate delegate,
DataSetFacade dataSetFacade)
{
this.delegate = delegate;
theCharacter = pc;
charDisplay = pc.getDisplay();
dataSet = new DelegatingDataSet(dataSetFacade);
buildAgeCategories();
initForCharacter();
undoManager = new UndoManager();
}
@Override
public void closeCharacter()
{
FacetLibrary.getFacet(LanguageFacet.class)
.removeDataFacetChangeListener(langListener);
FacetLibrary.getFacet(TemplateFacet.class)
.removeDataFacetChangeListener(templateListener);
FacetLibrary.getFacet(XPFacet.class).removeDataFacetChangeListener(
xpListener);
FacetLibrary.getFacet(AutoEquipmentFacet.class).removeDataFacetChangeListener(autoEquipListener);
characterAbilities.closeCharacter();
charLevelsFacade.closeCharacter();
companionSupportFacade.closeCharacter();
PluginManager
.getInstance()
.getPostbox()
.handleMessage(
new PlayerCharacterWasClosedMessage(this, theCharacter));
Globals.getPCList().remove(theCharacter);
lastExportChar = null;
/*
* Unfortunately, a dummy rather than null is necessary because the UI
* does model swaps and such that do not pause events in the UI so that
* it is trying to update things that do not exist
*/
theCharacter = DUMMY_PC;
charDisplay = null;
dataSet.detachDelegates();
}
/**
*
*/
private void initForCharacter()
{
// Calculate any active bonuses
theCharacter.preparePCForOutput();
todoManager = new TodoManager();
infoFactory = new Gui2InfoFactory(theCharacter);
characterAbilities = new CharacterAbilities(theCharacter, delegate, dataSet, todoManager);
descriptionFacade = new DescriptionFacadeImpl(theCharacter);
spellSupportFacade = new SpellSupportFacadeImpl(theCharacter, delegate, dataSet, todoManager, this);
name = new DefaultReferenceFacade<>(charDisplay.getName());
file = new DefaultReferenceFacade<>(new File(charDisplay.getFileName()));
companionSupportFacade = new CompanionSupportFacadeImpl(theCharacter, todoManager, name, file, this);
availTempBonuses = new DefaultListFacade<>();
refreshAvailableTempBonuses();
appliedTempBonuses = new DefaultListFacade<>();
buildAppliedTempBonusList();
kitList = new DefaultListFacade<>();
refreshKitList();
statScoreMap = new HashMap<>();
for (StatFacade stat : dataSet.getStats())
{
if (stat instanceof PCStat)
{
statScoreMap.put(stat, ChannelCompatibility.getStatScore(theCharacter.getCharID(), (PCStat) stat));
}
else
{
statScoreMap.put(stat, new DefaultReferenceFacade<>());
}
}
File portraitFile = null;
if (!StringUtils.isEmpty(charDisplay.getPortraitPath()))
{
portraitFile = new File(charDisplay.getPortraitPath());
}
portrait = new DefaultReferenceFacade<>(portraitFile);
cropRect = new RectangleReference(charDisplay.getPortraitThumbnailRect());
characterType = new DefaultReferenceFacade<>(charDisplay.getCharacterType());
previewSheet = new DefaultReferenceFacade<>(charDisplay.getPreviewSheet());
skillFilter = new DefaultReferenceFacade<>(charDisplay.getSkillFilter());
tabName = new DefaultReferenceFacade<>(charDisplay.getTabName());
playersName = new DefaultReferenceFacade<>(charDisplay.getPlayersName());
race = new DefaultReferenceFacade<>(charDisplay.getRace());
raceList = new DefaultListFacade<>();
if (charDisplay.getRace() != null && charDisplay.getRace() != Globals.s_EMPTYRACE)
{
raceList.addElement(charDisplay.getRace());
}
handedness = new DefaultReferenceFacade<>();
gender = new DefaultReferenceFacade<>();
availHands = new DefaultListFacade<>();
availGenders = new DefaultListFacade<>();
for (Handed handed : Handed.values())
{
availHands.addElement(handed);
}
for (Gender gender : Gender.values())
{
availGenders.addElement(gender);
}
if (charDisplay.getRace() != null)
{
for (HandedFacade handsFacade : availHands)
{
if (handsFacade.equals(charDisplay.getHandedObject()))
{
handedness.set(handsFacade);
break;
}
}
for (GenderFacade pcGender : availGenders)
{
if (pcGender.equals(charDisplay.getGenderObject()))
{
gender.set(pcGender);
break;
}
}
}
alignment = new DefaultReferenceFacade<>(charDisplay.getPCAlignment());
age = new DefaultReferenceFacade<>(charDisplay.getAge());
ageCategory = new DefaultReferenceFacade<>();
updateAgeCategoryForAge();
currentXP = new DefaultReferenceFacade<>(charDisplay.getXP());
xpListener = new XPListener();
FacetLibrary.getFacet(XPFacet.class).addDataFacetChangeListener(xpListener);
xpForNextlevel = new DefaultReferenceFacade<>(charDisplay.minXPForNextECL());
xpTableName = new DefaultReferenceFacade<>(charDisplay.getXPTableName());
hpRef = new DefaultReferenceFacade<>(theCharacter.hitPoints());
skinColor = new DefaultReferenceFacade<>(charDisplay.getSafeStringFor(PCStringKey.SKINCOLOR));
hairColor = new DefaultReferenceFacade<>(charDisplay.getSafeStringFor(PCStringKey.HAIRCOLOR));
eyeColor = new DefaultReferenceFacade<>(charDisplay.getSafeStringFor(PCStringKey.EYECOLOR));
weightRef = new DefaultReferenceFacade<>();
heightRef = new DefaultReferenceFacade<>();
refreshHeightWeight();
purchasedEquip = new EquipmentListFacadeImpl(theCharacter.getEquipmentMasterList());
autoEquipListener = new AutoEquipListener();
FacetLibrary.getFacet(AutoEquipmentFacet.class).addDataFacetChangeListener(autoEquipListener);
carriedWeightRef = new DefaultReferenceFacade<>();
loadRef = new DefaultReferenceFacade<>();
weightLimitRef = new DefaultReferenceFacade<>();
equipSet = new DefaultReferenceFacade<>();
equipmentSets = new DefaultListFacade<>();
initEquipSet();
GameMode game = (GameMode) dataSet.getGameMode();
rollMethodRef = new DefaultReferenceFacade<>(game.getRollMethod());
charLevelsFacade =
new CharacterLevelsFacadeImpl(theCharacter, delegate,
todoManager, dataSet, this);
pcClasses = new ArrayList<>();
pcClassLevels = new DefaultListFacade<>();
refreshClassLevelModel();
charLevelsFacade.addHitPointListener(this);
deity =
new DefaultReferenceFacade<>(charDisplay.getDeity());
domains = new DefaultListFacade<>();
maxDomains =
new DefaultReferenceFacade<>(
theCharacter.getMaxCharacterDomains());
remainingDomains =
new DefaultReferenceFacade<>(
theCharacter.getMaxCharacterDomains() - domains.getSize());
availDomains = new DefaultListFacade<>();
buildAvailableDomainsList();
templates =
new DefaultListFacade<>(
charDisplay.getDisplayVisibleTemplateList());
templateListener = new TemplateListener();
FacetLibrary.getFacet(TemplateFacet.class).addDataFacetChangeListener(templateListener);
initTodoList();
statTotalLabelText = new DefaultReferenceFacade<>();
statTotalText = new DefaultReferenceFacade<>();
modTotalLabelText = new DefaultReferenceFacade<>();
modTotalText = new DefaultReferenceFacade<>();
updateScorePurchasePool(false);
languages = new DefaultListFacade<>();
numBonusLang = new DefaultReferenceFacade<>(0);
numSkillLang = new DefaultReferenceFacade<>(0);
refreshLanguageList();
langListener = new LanguageListener();
FacetLibrary.getFacet(LanguageFacet.class).addDataFacetChangeListener(langListener);
purchasedEquip.addListListener(spellSupportFacade);
purchasedEquip.addEquipmentListListener(spellSupportFacade);
fundsRef = new DefaultReferenceFacade<>(theCharacter.getGold());
wealthRef = new DefaultReferenceFacade<>(theCharacter.totalValue());
gearBuySellSchemeRef = new DefaultReferenceFacade<>(findGearBuySellRate());
allowDebt = false;
}
/**
* Build up the list of kits that the character has.
*/
private void refreshKitList()
{
List<Kit> kits = new ArrayList<>();
for (Kit kit : charDisplay.getKitInfo())
{
kits.add(kit);
}
kitList.updateContents(kits);
}
private GearBuySellFacade findGearBuySellRate()
{
int buyRate = SettingsHandler.getGearTab_BuyRate();
int sellRate = SettingsHandler.getGearTab_SellRate();
for (GearBuySellFacade buySell : dataSet.getGearBuySellSchemes())
{
GearBuySellScheme scheme = (GearBuySellScheme) buySell;
if (scheme.getBuyRate().intValue() == buyRate && scheme.getSellRate().intValue() == sellRate)
{
return scheme;
}
}
GearBuySellScheme scheme =
new GearBuySellScheme(LanguageBundle.getString("in_custom"), //$NON-NLS-1$
new BigDecimal(buyRate), new BigDecimal(sellRate),
new BigDecimal(100));
return scheme;
}
/**
* Initialise the equipment set facades, ensuring that the character has a
* default equipment set.
*/
private void initEquipSet()
{
// Setup the default EquipSet if not already present
if (!charDisplay.hasEquipSet())
{
String id = EquipmentSetFacadeImpl.getNewIdPath(charDisplay, null);
EquipSet eSet = new EquipSet(id, LanguageBundle.getString("in_ieDefault"));
theCharacter.addEquipSet(eSet);
theCharacter.setCalcEquipSetId(id);
}
// Detach listeners from old set
if (equipSet.get() != null)
{
EquipmentListFacade equippedItems = equipSet.get().getEquippedItems();
equippedItems.removeListListener(this);
equippedItems.removeEquipmentListListener(this);
}
// Make facades for each root equipset.
List<EquipmentSetFacade> eqSetList = new ArrayList<>();
EquipmentSetFacade currSet = null;
String currIdPath = theCharacter.getCalcEquipSetId();
for (EquipSet es : charDisplay.getEquipSet())
{
if (es.getParentIdPath().equals("0"))
{
final EquipmentSetFacadeImpl facade =
new EquipmentSetFacadeImpl(delegate, theCharacter, es,
dataSet, purchasedEquip, todoManager, this);
eqSetList.add(facade);
if (es.getIdPath().equals(currIdPath))
{
currSet = facade;
}
}
}
equipmentSets.updateContents(eqSetList);
if (currSet != null)
{
equipSet.set(currSet);
}
EquipmentSetFacade set = equipSet.get();
set.getEquippedItems().addListListener(this);
set.getEquippedItems().addEquipmentListListener(this);
refreshTotalWeight();
}
/**
* Create the list of known age categories in the current BioSet.
*/
private void buildAgeCategories()
{
List<String> cats = new ArrayList<>();
for (String aString : SettingsHandler.getGame().getBioSet().getAgeCategories())
{
final int idx = aString.indexOf('\t');
if (idx >= 0)
{
aString = aString.substring(0, idx);
}
if (!cats.contains(aString))
{
cats.add(aString);
}
}
Collections.sort(cats);
ageCategoryList = new DefaultListFacade<>();
for (String ageCat : cats)
{
ageCategoryList.addElement(new SimpleFacadeImpl(ageCat));
}
}
/**
* Create an initial list of todo items
*/
private void initTodoList()
{
if (isNewCharName(charDisplay.getName()))
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY, "Name", "in_sumTodoName", 1));
}
if (charDisplay.getRace() == null || Constants.NONESELECTED.equals(charDisplay.getRace().getKeyName()))
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY, "Race", "in_irTodoRace", 100));
}
// Stats todo already done in updateScorePurchasePool
updateLevelTodo();
}
/**
* Identify if the supplied name is a default one generated by the system
* e.g. Unnamed 1 or Unnamed 2
* @param charName The name to be checked.
* @return True if the name is a default.
*/
private boolean isNewCharName(String charName)
{
if (charName == null)
{
return true;
}
return charName.startsWith("Unnamed"); //$NON-NLS-1$
}
@Override
public ListFacade<HandedFacade> getAvailableHands()
{
return availHands;
}
@Override
public ListFacade<GenderFacade> getAvailableGenders()
{
return availGenders;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#addAbility(pcgen.core.facade.AbilityCategoryFacade, pcgen.core.facade.AbilityFacade)
*/
@Override
public void addAbility(AbilityCategoryFacade category, AbilityFacade ability)
{
characterAbilities.addAbility(category, ability);
refreshKitList();
refreshAvailableTempBonuses();
buildAvailableDomainsList();
companionSupportFacade.refreshCompanionData();
refreshEquipment();
hpRef.set(theCharacter.hitPoints());
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#removeAbility(pcgen.core.facade.AbilityCategoryFacade, pcgen.core.facade.AbilityFacade)
*/
@Override
public void removeAbility(AbilityCategoryFacade category, AbilityFacade ability)
{
characterAbilities.removeAbility(category, ability);
refreshKitList();
companionSupportFacade.refreshCompanionData();
hpRef.set(theCharacter.hitPoints());
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getAbilities(pcgen.core.facade.AbilityCategoryFacade)
*/
@Override
public ListFacade<AbilityFacade> getAbilities(AbilityCategoryFacade category)
{
return characterAbilities.getAbilities(category);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getActiveAbilityCategories()
*/
@Override
public ListFacade<AbilityCategoryFacade> getActiveAbilityCategories()
{
return characterAbilities.getActiveAbilityCategories();
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getTotalSelections(pcgen.core.facade.AbilityCategoryFacade)
*/
@Override
public int getTotalSelections(AbilityCategoryFacade category)
{
return characterAbilities.getTotalSelections(category);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getRemainingSelections(pcgen.core.facade.AbilityCategoryFacade)
*/
@Override
public int getRemainingSelections(AbilityCategoryFacade category)
{
return characterAbilities.getRemainingSelections(category);
}
@Override
public void addAbilityCatSelectionListener(ChangeListener listener)
{
characterAbilities.addAbilityCatSelectionListener(listener);
}
@Override
public void removeAbilityCatSelectionListener(ChangeListener listener)
{
characterAbilities.removeAbilityCatSelectionListener(listener);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setRemainingSelection(pcgen.core.facade.AbilityCategoryFacade, int)
*/
@Override
public void setRemainingSelection(AbilityCategoryFacade category, int remaining)
{
characterAbilities.setRemainingSelection(category, remaining);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#hasAbility(pcgen.core.facade.AbilityCategoryFacade, pcgen.core.facade.AbilityFacade)
*/
@Override
public boolean hasAbility(AbilityCategoryFacade category, AbilityFacade ability)
{
return characterAbilities.hasAbility(category, ability);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getAbilityNature(pcgen.core.facade.AbilityFacade)
*/
@Override
public Nature getAbilityNature(AbilityFacade ability)
{
if (ability == null || !(ability instanceof Ability))
{
return null;
}
/*
* TODO This is making a somewhat DRASTIC assumption that ANY Ability
* Category is appropriate. Unfortunately, the point at which this
* method is called from the UI it is unclear to the untrained eye how
* to get the category.
*/
List<CNAbility> cnas = theCharacter.getMatchingCNAbilities((Ability) ability);
Nature nature = null;
for (CNAbility cna : cnas)
{
nature = Nature.getBestNature(nature, cna.getNature());
}
return nature;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#addCharacterLevels(pcgen.core.facade.ClassFacade[])
*/
@Override
public void addCharacterLevels(ClassFacade[] classes)
{
SettingsHandler.setShowHPDialogAtLevelUp(false);
//SettingsHandler.setShowStatDialogAtLevelUp(false);
int oldLevel = charLevelsFacade.getSize();
boolean needFullRefresh = false;
for (ClassFacade classFacade : classes)
{
if (classFacade instanceof PCClass)
{
int totalLevels = charDisplay.getTotalLevels();
if (!validateAddLevel((PCClass) classFacade))
{
return;
}
Logging.log(Logging.INFO, charDisplay.getName()
+ ": Adding level " + (totalLevels + 1) //$NON-NLS-1$
+ " in class " + classFacade); //$NON-NLS-1$
theCharacter.incrementClassLevel(1, (PCClass) classFacade);
if (totalLevels == charDisplay.getTotalLevels())
{
// The level change was rejected - no further processing needed.
return;
}
if (((PCClass) classFacade).containsKey(ObjectKey.EXCHANGE_LEVEL))
{
needFullRefresh = true;
}
}
if (!pcClasses.contains(classFacade))
{
pcClasses.add(classFacade);
}
CharacterLevelFacadeImpl cl = new CharacterLevelFacadeImpl(classFacade,
charLevelsFacade.getSize() + 1);
pcClassLevels.addElement(cl);
charLevelsFacade.addLevelOfClass(cl);
}
CharacterUtils.selectClothes(getTheCharacter());
// Calculate any active bonuses
theCharacter.calcActiveBonuses();
if (needFullRefresh)
{
refreshClassLevelModel();
}
postLevellingUpdates();
delegate.showLevelUpInfo(this, oldLevel);
}
/**
* Ensure any items that could be affected by the level up or down are refreshed.
*/
void postLevellingUpdates()
{
characterAbilities.rebuildAbilityLists();
companionSupportFacade.refreshCompanionData();
refreshKitList();
refreshAvailableTempBonuses();
refreshEquipment();
currentXP.set(charDisplay.getXP());
xpForNextlevel.set(charDisplay.minXPForNextECL());
xpTableName.set(charDisplay.getXPTableName());
hpRef.set(theCharacter.hitPoints());
age.set(charDisplay.getAge());
refreshHeightWeight();
refreshStatScores();
updateLevelTodo();
buildAvailableDomainsList();
spellSupportFacade.refreshAvailableKnownSpells();
updateScorePurchasePool(false);
refreshLanguageList();
}
/**
* Ensure any items that could be affected by the new equipment are refreshed.
*/
void postEquippingUpdates()
{
characterAbilities.rebuildAbilityLists();
refreshAvailableTempBonuses();
hpRef.set(theCharacter.hitPoints());
}
private void refreshHeightWeight()
{
weightRef.set(Globals.getGameModeUnitSet()
.convertWeightToUnitSet(charDisplay.getWeight()));
heightRef.set((int) Math.round(Globals.getGameModeUnitSet()
.convertHeightToUnitSet(charDisplay.getHeight())));
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#removeCharacterLevels(int)
*/
@Override
public void removeCharacterLevels(int levels)
{
for (int i = levels; i > 0 && !pcClassLevels.isEmpty(); i--)
{
ClassFacade classFacade = charLevelsFacade
.getClassTaken(pcClassLevels.getElementAt(pcClassLevels.getSize() - 1));
pcClassLevels.removeElement(pcClassLevels.getSize() - 1);
if (classFacade instanceof PCClass)
{
Logging.log(Logging.INFO, charDisplay.getName()
+ ": Removing level " + (pcClassLevels.getSize()+1) //$NON-NLS-1$
+ " in class " + classFacade); //$NON-NLS-1$
theCharacter.incrementClassLevel(-1, (PCClass) classFacade);
}
charLevelsFacade.removeLastLevel();
}
// Clean up the class list
for (Iterator<ClassFacade> iterator = pcClasses.iterator(); iterator.hasNext();)
{
ClassFacade classFacade = iterator.next();
boolean stillPresent = false;
for (CharacterLevelFacade charLevel : pcClassLevels)
{
if (charLevelsFacade.getClassTaken(charLevel) == classFacade)
{
stillPresent = true;
break;
}
}
if (!stillPresent)
{
iterator.remove();
}
}
postLevellingUpdates();
}
/**
* Update the todo list to reflect the change in level or experience.
*/
private void updateLevelTodo()
{
if (charDisplay.getXP() >= charDisplay.minXPForNextECL())
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY, "Class", "in_clTodoLevelUp", 120));
}
else
{
todoManager.removeTodo("in_clTodoLevelUp");
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getClassLevel(pcgen.core.facade.ClassFacade)
*/
@Override
public int getClassLevel(ClassFacade c)
{
int clsLevel = 0;
// We have to compare by class key as classes get cloned and we may have multiple instances of the same class in our level list
String classKey = c.getKeyName();
for (CharacterLevelFacade charLevel : pcClassLevels)
{
if (charLevelsFacade.getClassTaken(charLevel).getKeyName().equals(classKey))
{
clsLevel++;
}
}
return clsLevel;
}
private boolean validateAddLevel(PCClass theClass)
{
int levels = 1;
if (theClass == null)
{
return false;
}
if (!theCharacter.isQualified(theClass))
{
delegate.showErrorMessage(Constants.APPLICATION_NAME,
LanguageBundle.getString("in_clYouAreNotQualifiedToTakeTheClass"));
return false;
}
if (!theCharacter.canLevelUp())
{
delegate.showErrorMessage(Constants.APPLICATION_NAME, LanguageBundle.getString("in_Enforce_rejectLevelUp"));
return false;
}
final PCClass aClass = theCharacter.getClassKeyed(theClass.getKeyName());
// Check if the subclass (if any) is qualified for
if (aClass != null)
{
String subClassKey = charDisplay.getSubClassName(aClass);
if (subClassKey != null)
{
final PCClass subClass = aClass.getSubClassKeyed(subClassKey);
if (subClass != null && !theCharacter.isQualified(subClass))
{
delegate.showErrorMessage(Constants.APPLICATION_NAME,
LanguageBundle.getFormattedString(
"in_sumYouAreNotQualifiedToTakeTheClass", //$NON-NLS-1$
aClass.getDisplayName()
+ "/" + subClass.getDisplayName())); //$NON-NLS-1$
return false;
}
}
}
if (!Globals.checkRule(RuleConstants.LEVELCAP)
&& theClass.hasMaxLevel()
&& ((levels > theClass.getSafe(IntegerKey.LEVEL_LIMIT)) || ((aClass != null) && ((charDisplay
.getLevel(aClass) + levels) > aClass.getSafe(IntegerKey.LEVEL_LIMIT)))))
{
delegate.showInfoMessage(Constants.APPLICATION_NAME,
LanguageBundle.getFormattedString("in_sumMaximumLevelIs", //$NON-NLS-1$
String.valueOf(theClass.getSafe(IntegerKey.LEVEL_LIMIT))));
return false;
}
// Check with the user on their first level up
if (charDisplay.getTotalLevels() == 0)
{
if (SettingsHandler.getGame().isPurchaseStatMode()
&& (theCharacter.getPointBuyPoints() > getUsedStatPool()))
{
if (!delegate.showWarningConfirm(LanguageBundle.getString("in_sumLevelWarnTitle"), //$NON-NLS-1$
LanguageBundle.getString("in_sumPoolWarning")))//$NON-NLS-1$
{
return false;
}
}
else if (allAbilitiesAreZero())
{
if (!delegate.showWarningConfirm(LanguageBundle.getString("in_sumLevelWarnTitle"),
LanguageBundle.getString("in_sumAbilitiesZeroWarning")))
{
return false;
}
}
else
{
Boolean proceed = delegate.maybeShowWarningConfirm(LanguageBundle.getString("in_sumLevelWarnTitle"),
LanguageBundle.getString("in_sumAbilitiesWarning"),
LanguageBundle.getString("in_sumAbilitiesWarningCheckBox"), PCGenSettings.OPTIONS_CONTEXT,
PCGenSettings.OPTION_SHOW_WARNING_AT_FIRST_LEVEL_UP);
if (Boolean.FALSE.equals(proceed))
{
return false;
}
}
}
return true;
}
/**
* Determine if all of the character's stats are still set to 0.
*
* @return True if they are all zero, false if any are non-zero.
*/
private boolean allAbilitiesAreZero()
{
for (StatFacade stat : dataSet.getStats())
{
ReferenceFacade<Number> facade = getScoreBaseRef(stat);
if (facade.get().intValue() != 0)
{
return false;
}
}
return true;
}
/**
* This method gets the number of stat points used in the pool
* @param pc The PlayerCharacter to get used stat pool for
* @return used stat pool
*/
private int getUsedStatPool()
{
int i = 0;
for (PCStat aStat : charDisplay.getStatSet())
{
if (!aStat.getSafe(ObjectKey.ROLLED))
{
continue;
}
final int statValue = theCharacter.getBaseStatFor(aStat);
i += getPurchaseCostForStat(theCharacter, statValue);
}
i += (int) theCharacter.getTotalBonusTo("POINTBUY", "SPENT"); //$NON-NLS-1$ //$NON-NLS-2$
return i;
}
private static int getPurchaseCostForStat(final PlayerCharacter aPC, int statValue)
{
final int iMax = SettingsHandler.getGame().getPurchaseScoreMax(aPC);
final int iMin = SettingsHandler.getGame().getPurchaseScoreMin(aPC);
if (statValue > iMax)
{
statValue = iMax;
}
if (statValue >= iMin)
{
return SettingsHandler.getGame().getAbilityScoreCost(statValue - iMin);
}
return 0;
}
void refreshAvailableTempBonuses()
{
List<TempBonusFacadeImpl> tempBonuses = new ArrayList<>();
// first objects on the PC
for (CDOMObject cdo : theCharacter.getCDOMObjectList())
{
scanForTempBonuses(tempBonuses, cdo);
}
//
// next do all abilities to get TEMPBONUS:ANYPC only
GameMode game = (GameMode) dataSet.getGameMode();
for (AbilityCategory cat : game.getAllAbilityCategories())
{
if (cat.getParentCategory() == cat)
{
for (Ability aFeat : Globals.getContext().getReferenceContext().getManufacturer(
Ability.class, cat).getAllObjects())
{
scanForAnyPcTempBonuses(tempBonuses, aFeat);
}
}
}
//
// Do all the PC's spells
for (Spell aSpell : theCharacter.aggregateSpellList("", "", "", 0, 9))
{
scanForTempBonuses(tempBonuses, aSpell);
}
// Do all the pc's innate spells.
Collection<CharacterSpell> innateSpells = theCharacter.getCharacterSpells(charDisplay.getRace(),
Constants.INNATE_SPELL_BOOK_NAME);
for (CharacterSpell aCharacterSpell : innateSpells)
{
if (aCharacterSpell == null)
{
continue;
}
scanForTempBonuses(tempBonuses, aCharacterSpell.getSpell());
}
//
// Next do all spells to get TEMPBONUS:ANYPC or TEMPBONUS:EQUIP
for (Spell spell : Globals.getContext().getReferenceContext()
.getConstructedCDOMObjects(Spell.class))
{
scanForNonPcTempBonuses(tempBonuses, spell);
}
// do all Templates to get TEMPBONUS:ANYPC or TEMPBONUS:EQUIP
for (PCTemplate aTemp : Globals.getContext().getReferenceContext()
.getConstructedCDOMObjects(PCTemplate.class))
{
scanForNonPcTempBonuses(tempBonuses, aTemp);
}
Collections.sort(tempBonuses);
availTempBonuses.updateContents(tempBonuses);
}
private void scanForNonPcTempBonuses(List<TempBonusFacadeImpl> tempBonuses, PObject obj)
{
if (obj == null)
{
return;
}
if (TempBonusHelper.hasNonPCTempBonus(obj))
{
tempBonuses.add(new TempBonusFacadeImpl(obj));
}
}
private void scanForAnyPcTempBonuses(List<TempBonusFacadeImpl> tempBonuses, PObject obj)
{
if (obj == null)
{
return;
}
if (TempBonusHelper.hasAnyPCTempBonus(obj))
{
tempBonuses.add(new TempBonusFacadeImpl(obj));
}
}
private void scanForTempBonuses(List<TempBonusFacadeImpl> tempBonuses, CDOMObject obj)
{
if (obj == null)
{
return;
}
if (TempBonusHelper.hasTempBonus(obj))
{
tempBonuses.add(new TempBonusFacadeImpl(obj));
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getAvailableTempBonuses()
*/
@Override
public ListFacade<TempBonusFacade> getAvailableTempBonuses()
{
return availTempBonuses;
}
/**
* Build up the list of temporary bonuses which have been applied to this character.
*/
private void buildAppliedTempBonusList()
{
Set<String> found = new HashSet<>();
for (TempBonusInfo tbi : theCharacter.getTempBonusMap().values())
{
Object aC = tbi.source;
Object aT = tbi.target;
String name = BonusDisplay.getBonusDisplayName(tbi);
if (!found.contains(name))
{
found.add(name);
TempBonusFacadeImpl facade = new TempBonusFacadeImpl((CDOMObject) aC, aT, name);
facade.setActive(!theCharacter.getTempBonusFilters().contains(
name));
appliedTempBonuses.addElement(facade);
}
}
}
@Override
public void addTempBonus(TempBonusFacade bonusFacade)
{
if (bonusFacade == null || !(bonusFacade instanceof TempBonusFacadeImpl))
{
return;
}
TempBonusFacadeImpl tempBonus = (TempBonusFacadeImpl) bonusFacade;
// Allow selection of target for bonus affecting equipment
CDOMObject originObj = tempBonus.getOriginObj();
Equipment aEq = null;
Object target =
TempBonusHelper.getTempBonusTarget(originObj,
theCharacter, delegate, infoFactory);
if (target == null)
{
return;
}
TempBonusFacadeImpl appliedTempBonus;
if (target instanceof Equipment)
{
aEq = (Equipment) target;
appliedTempBonus =
TempBonusHelper.applyBonusToCharacterEquipment(aEq,
originObj, theCharacter);
}
else
{
appliedTempBonus =
TempBonusHelper.applyBonusToCharacter(originObj,
theCharacter);
}
// Resolve choices and apply the bonus to the character.
if (appliedTempBonus == null)
{
return;
}
appliedTempBonuses.addElement(appliedTempBonus);
refreshStatScores();
postLevellingUpdates();
}
@Override
public void removeTempBonus(TempBonusFacade bonusFacade)
{
if (bonusFacade == null || !(bonusFacade instanceof TempBonusFacadeImpl))
{
return;
}
TempBonusFacadeImpl tempBonus = (TempBonusFacadeImpl) bonusFacade;
Equipment aEq = null;
if (tempBonus.getTarget() instanceof Equipment)
{
aEq = (Equipment) tempBonus.getTarget();
}
CDOMObject originObj = tempBonus.getOriginObj();
TempBonusHelper.removeBonusFromCharacter(theCharacter, aEq, originObj);
appliedTempBonuses.removeElement(tempBonus);
refreshStatScores();
postLevellingUpdates();
}
@Override
public void setTempBonusActive(TempBonusFacade bonusFacade, boolean active)
{
if (bonusFacade == null || !(bonusFacade instanceof TempBonusFacadeImpl))
{
return;
}
TempBonusFacadeImpl tempBonus = (TempBonusFacadeImpl) bonusFacade;
if (active)
{
theCharacter.unsetTempBonusFilter(tempBonus.toString());
}
else
{
theCharacter.setTempBonusFilter(tempBonus.toString());
}
tempBonus.setActive(active);
appliedTempBonuses.modifyElement(tempBonus);
refreshStatScores();
}
@Override
public ListFacade<TempBonusFacade> getTempBonuses()
{
return appliedTempBonuses;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getAlignmentRef()
*/
@Override
public ReferenceFacade<AlignmentFacade> getAlignmentRef()
{
return alignment;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setAlignment(pcgen.core.facade.AlignmentFacade)
*/
@Override
public void setAlignment(AlignmentFacade alignment)
{
if (!validateAlignmentChange(alignment))
{
return;
}
this.alignment.set(alignment);
if (alignment instanceof PCAlignment)
{
theCharacter.setAlignment((PCAlignment) alignment);
}
refreshLanguageList();
}
/**
* Validate the new alignment matches those allowed for the character's
* classes. If not offer the user a choice of backing out or making the
* classes into ex-classes.
*
* @param newAlign The alignment to be set
*/
private boolean validateAlignmentChange(AlignmentFacade newAlign)
{
AlignmentFacade oldAlign = this.alignment.get();
if (oldAlign == null || newAlign.equals(oldAlign))
{
return true;
}
// We can't do any validation if the new alignment isn't a known class
if (!(newAlign instanceof PCAlignment))
{
return true;
}
//
// Get a list of classes that will become unqualified (and have an ex-class)
//
StringBuilder unqualified = new StringBuilder(100);
List<PCClass> classList = charDisplay.getClassList();
List<PCClass> exclassList = new ArrayList<>();
PCAlignment savedAlignmnet = charDisplay.getPCAlignment();
for (PCClass aClass : classList)
{
theCharacter.setAlignment((PCAlignment) newAlign);
{
if (!theCharacter.isQualified(aClass))
{
if (aClass.containsKey(ObjectKey.EX_CLASS))
{
if (unqualified.length() > 0)
{
unqualified.append(", "); //$NON-NLS-1$
}
unqualified.append(aClass.getKeyName());
exclassList.add(aClass);
}
}
}
}
//
// Give the user a chance to bail
//
if (unqualified.length() > 0)
{
if (!delegate.showWarningConfirm(Constants.APPLICATION_NAME,
LanguageBundle.getString("in_sumExClassesWarning") + Constants.LINE_SEPARATOR + unqualified))
{
theCharacter.setAlignment(savedAlignmnet);
return false;
}
}
//
// Convert the class(es)
//
for (PCClass aClass : exclassList)
{
theCharacter.makeIntoExClass(aClass);
}
// Update the facade and UI
refreshClassLevelModel();
return true;
}
void refreshClassLevelModel()
{
List<CharacterLevelFacade> newlevels = new ArrayList<>();
List<PCClass> newClasses = charDisplay.getClassList();
Collection<PCLevelInfo> levelInfo = charDisplay.getLevelInfo();
Map<String, PCClass> classMap = new HashMap<>();
for (PCClass pcClass : newClasses)
{
classMap.put(pcClass.getKeyName(), pcClass);
}
for (PCLevelInfo lvlInfo : levelInfo)
{
final String classKeyName = lvlInfo.getClassKeyName();
PCClass currClass = classMap.get(classKeyName);
if (currClass == null)
{
Logging.errorPrint("No PCClass found for '" + classKeyName + "' in character's class list: "
+ newClasses);
return;
}
CharacterLevelFacadeImpl cl = new CharacterLevelFacadeImpl(currClass, newlevels.size() + 1);
newlevels.add(cl);
}
pcClasses.clear();
pcClasses.addAll(newClasses);
pcClassLevels.updateContents(newlevels);
// Now get the CharacterLevelsFacadeImpl to do a refresh too.
charLevelsFacade.classListRefreshRequired();
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getDataSet()
*/
@Override
public DataSetFacade getDataSet()
{
return dataSet;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getEquipmentSets()
*/
@Override
public ListFacade<EquipmentSetFacade> getEquipmentSets()
{
return equipmentSets;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getGenderRef()
*/
@Override
public ReferenceFacade<GenderFacade> getGenderRef()
{
return gender;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setGender(pcgen.cdom.enumeration.Gender)
*/
@Override
public void setGender(GenderFacade gender)
{
theCharacter.setGender((Gender) gender);
Gender newGender = charDisplay.getGenderObject();
this.selectedGender = newGender.toString();
this.gender.set(newGender);
refreshLanguageList();
}
@Override
public void setGender(String gender)
{
this.selectedGender = gender;
if (charDisplay.getRace() != null)
{
for (GenderFacade raceGender : availGenders)
{
if (raceGender.toString().equals(gender))
{
setGender(raceGender);
}
}
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getModTotal(pcgen.core.facade.StatFacade)
*/
@Override
public int getModTotal(StatFacade stat)
{
if (stat instanceof PCStat && !charDisplay.isNonAbility((PCStat) stat))
{
return theCharacter.getStatModFor((PCStat) stat);
}
return 0;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getScoreTotalRef(pcgen.core.facade.StatFacade)
*/
@Override
public ReferenceFacade<Number> getScoreBaseRef(StatFacade stat)
{
WriteableReferenceFacade<Number> score = statScoreMap.get(stat);
if (score == null)
{
score = new DefaultReferenceFacade<>(theCharacter.getTotalStatFor((PCStat) stat));
statScoreMap.put(stat, score);
}
return score;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getScoreBase(pcgen.core.facade.StatFacade)
*/
@Override
public int getScoreBase(StatFacade stat)
{
if (!(stat instanceof PCStat))
{
return 0;
}
return theCharacter.getBaseStatFor((PCStat) stat);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getScoreTotalString(pcgen.core.facade.StatFacade)
*/
@Override
public String getScoreTotalString(StatFacade stat)
{
if (!(stat instanceof PCStat))
{
return "";
}
if (charDisplay.isNonAbility((PCStat) stat))
{
return "*"; //$NON-NLS-1$
}
return SettingsHandler.getGame().getStatDisplayText(theCharacter.getTotalStatFor((PCStat) stat));
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getScoreRaceBonus(pcgen.core.facade.StatFacade)
*/
@Override
public int getScoreRaceBonus(StatFacade stat)
{
if (!(stat instanceof PCStat))
{
return 0;
}
PCStat activeStat = (PCStat) stat;
if (charDisplay.isNonAbility(activeStat))
{
return 0;
}
//return Integer.valueOf(currentStatAnalysis.getTotalStatFor(aStat) - currentStatAnalysis.getBaseStatFor(aStat));
int rBonus = (int) theCharacter.getRaceBonusTo("STAT", activeStat.getKeyName()); //$NON-NLS-1$
rBonus += (int) theCharacter.getBonusDueToType("STAT", activeStat.getKeyName(), "RACIAL");
return rBonus;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getScoreOtherBonus(pcgen.core.facade.StatFacade)
*/
@Override
public int getScoreOtherBonus(StatFacade stat)
{
if (!(stat instanceof PCStat))
{
return 0;
}
PCStat activeStat = (PCStat) stat;
if (charDisplay.isNonAbility(activeStat))
{
return 0;
}
//return Integer.valueOf(currentStatAnalysis.getTotalStatFor(aStat) - currentStatAnalysis.getBaseStatFor(aStat));
int iRace = (int) theCharacter.getRaceBonusTo("STAT", activeStat.getKeyName()); //$NON-NLS-1$
iRace += (int) theCharacter.getBonusDueToType("STAT", activeStat.getKeyName(), "RACIAL");
return theCharacter.getTotalStatFor(activeStat)
- theCharacter.getBaseStatFor(activeStat) - iRace;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setScoreBase(pcgen.core.facade.StatFacade, int)
*/
@Override
public void setScoreBase(StatFacade stat, int score)
{
WriteableReferenceFacade<Number> facade = statScoreMap.get(stat);
if (facade == null)
{
facade = new DefaultReferenceFacade<>(score);
statScoreMap.put(stat, facade);
}
PCStat pcStat = null;
final int pcPlayerLevels = charDisplay.totalNonMonsterLevels();
Collection<PCStat> pcStatList = charDisplay.getStatSet();
for (PCStat aStat : pcStatList)
{
if (stat.getKeyName().equals(aStat.getKeyName()))
{
pcStat = aStat;
break;
}
}
if (pcStat == null)
{
Logging.errorPrint("Unexpected stat '" + stat + "' found - ignoring.");
return;
}
// Checking for bounds, locked stats and pool points
String errorMsg = validateNewStatBaseScore(score, pcStat, pcPlayerLevels);
if (StringUtils.isNotBlank(errorMsg))
{
delegate.showErrorMessage(Constants.APPLICATION_NAME, errorMsg);
return;
}
final int baseScore = charDisplay.getStat(pcStat);
// Deal with a point pool based game mode where you buy skills and feats as well as stats
if (Globals.getGameModeHasPointPool())
{
if (pcPlayerLevels > 0)
{
int poolMod = getPurchaseCostForStat(theCharacter, score)
- getPurchaseCostForStat(theCharacter, baseScore);
//
// Adding to stat
//
if (poolMod > 0)
{
if (poolMod > theCharacter.getSkillPoints())
{
delegate.showErrorMessage(Constants.APPLICATION_NAME,
LanguageBundle.getFormattedString("in_sumStatPoolEmpty", Globals //$NON-NLS-1$
.getGameModePointPoolName()));
return;
}
}
else if (poolMod < 0)
{
if (theCharacter.getStatIncrease(pcStat, true) < Math.abs(score - baseScore))
{
delegate.showErrorMessage(Constants.APPLICATION_NAME,
LanguageBundle.getString("in_sumStatStartedHigher")); //$NON-NLS-1$
return;
}
}
theCharacter.adjustAbilities(AbilityCategory.FEAT,
new BigDecimal(-poolMod));
showPointPool();
}
}
theCharacter.setStat(pcStat, score);
facade.set(score);
theCharacter.saveStatIncrease(pcStat, score - baseScore, false);
theCharacter.calcActiveBonuses();
hpRef.set(theCharacter.hitPoints());
refreshLanguageList();
updateScorePurchasePool(true);
if (charLevelsFacade != null)
{
charLevelsFacade.fireSkillBonusEvent(this, 0, true);
}
}
/**
* Assess if the new score is valid for the stat.
*
* @param score The new score being checked.
* @param pcStat The stats being checked
* @param pcPlayerLevels The number of non moster levels the character currently has.
* @return An error message if the score is not valid.
*/
private String validateNewStatBaseScore(int score, PCStat pcStat, final int pcPlayerLevels)
{
if (charDisplay.isNonAbility(pcStat))
{
return LanguageBundle.getString("in_sumCannotModifyANonAbility"); //$NON-NLS-1$
}
else if (score < pcStat.getSafe(IntegerKey.MIN_VALUE))
{
return LanguageBundle.getFormattedString("in_sumCannotLowerStatBelow", SettingsHandler.getGame() //$NON-NLS-1$
.getStatDisplayText(pcStat.getSafe(IntegerKey.MIN_VALUE)));
}
else if (score > pcStat.getSafe(IntegerKey.MAX_VALUE))
{
return LanguageBundle.getFormattedString("in_sumCannotRaiseStatAbove", SettingsHandler.getGame() //$NON-NLS-1$
.getStatDisplayText(pcStat.getSafe(IntegerKey.MAX_VALUE)));
}
else if ((pcPlayerLevels < 2)
&& SettingsHandler.getGame().isPurchaseStatMode())
{
final int maxPurchaseScore = SettingsHandler.getGame().getPurchaseScoreMax(theCharacter);
if (score > maxPurchaseScore)
{
return LanguageBundle.getFormattedString("in_sumCannotRaiseStatAbovePurchase", SettingsHandler //$NON-NLS-1$
.getGame().getStatDisplayText(maxPurchaseScore));
}
final int minPurchaseScore = SettingsHandler.getGame().getPurchaseScoreMin(theCharacter);
if (score < minPurchaseScore)
{
return LanguageBundle.getFormattedString("in_sumCannotLowerStatBelowPurchase", SettingsHandler //$NON-NLS-1$
.getGame().getStatDisplayText(minPurchaseScore));
}
}
return null;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#rollStats()
*/
@Override
public void rollStats()
{
GameMode game = (GameMode) dataSet.getGameMode();
int rollMethod = game.getRollMethod();
if (rollMethod == Constants.CHARACTER_STAT_METHOD_ROLLED && game.getCurrentRollingMethod() == null)
{
return;
}
if (rollMethod == Constants.CHARACTER_STAT_METHOD_USER)
{
// If a user asks to roll in user mode, set it to the current all same value.
rollMethod = Constants.CHARACTER_STAT_METHOD_ALL_THE_SAME;
}
theCharacter.rollStats(rollMethod);
//XXX This is here to stop the stat mod from being stale. Can be removed once we merge with CDOM
theCharacter.calcActiveBonuses();
refreshStatScores();
updateScorePurchasePool(true);
}
private void refreshStatScores()
{
// for (StatFacade stat : statScoreMap.keySet())
// {
// WriteableReferenceFacade<Integer> score = statScoreMap.get(stat);
// if (stat instanceof PCStat)
// {
// score.set(theCharacter.getTotalStatFor((PCStat) stat));
// }
// }
if (charLevelsFacade != null)
{
charLevelsFacade.fireSkillBonusEvent(this, 0, true);
charLevelsFacade.updateSkillsTodo();
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#isStatRollEnabled()
*/
@Override
public boolean isStatRollEnabled()
{
return (charLevelsFacade.getSize() == 0);
}
/**
* Update the
*/
private void showPointPool()
{
if (poolPointText == null)
{
return;
}
int poolPointsTotal = 0;
for (PCLevelInfo pcl : charDisplay.getLevelInfo())
{
poolPointsTotal += pcl.getSkillPointsGained(theCharacter);
}
int poolPointsUsed = poolPointsTotal - theCharacter.getSkillPoints();
poolPointText.set(Integer.toString(poolPointsUsed) + " / " + Integer.toString(poolPointsTotal)); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getUndoManager()
*/
@Override
public UndoManager getUndoManager()
{
return undoManager;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getRaceRef()
*/
@Override
public ReferenceFacade<RaceFacade> getRaceRef()
{
return race;
}
/**
* @return A reference to a list containing the character's race.
*/
@Override
public ListFacade<RaceFacade> getRaceAsList()
{
return raceList;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setRace(pcgen.core.facade.RaceFacade)
*/
@Override
public void setRace(RaceFacade race)
{
// TODO: We don't have a HP dialog implemented yet, so don't try to show it
SettingsHandler.setShowHPDialogAtLevelUp(false);
//SettingsHandler.setShowStatDialogAtLevelUp(false);
int oldLevel = charLevelsFacade.getSize();
if (race == null)
{
race = Globals.s_EMPTYRACE;
}
this.race.set(race);
if (race instanceof Race && race != charDisplay.getRace())
{
Logging.log(Logging.INFO, charDisplay.getName()
+ ": Setting race to " + race); //$NON-NLS-1$
theCharacter.setRace((Race) race);
raceList.clearContents();
if (race != Globals.s_EMPTYRACE)
{
raceList.addElement(race);
}
}
refreshLanguageList();
if (selectedGender != null)
{
setGender(selectedGender);
}
refreshRaceRelatedFields();
if (oldLevel != charLevelsFacade.getSize())
{
delegate.showLevelUpInfo(this, oldLevel);
}
}
private void refreshRaceRelatedFields()
{
race.set(charDisplay.getRace());
if (charDisplay.getRace() != null)
{
for (HandedFacade handsFacade : availHands)
{
if (handsFacade.toString().equals(charDisplay.getHanded()))
{
handedness.set(handsFacade);
break;
}
}
for (GenderFacade pcGender : availGenders)
{
if (pcGender.equals(charDisplay.getGenderObject()))
{
gender.set(pcGender);
break;
}
}
}
refreshClassLevelModel();
refreshStatScores();
age.set(charDisplay.getAge());
updateAgeCategoryForAge();
refreshHeightWeight();
characterAbilities.rebuildAbilityLists();
currentXP.set(charDisplay.getXP());
xpForNextlevel.set(charDisplay.minXPForNextECL());
xpTableName.set(charDisplay.getXPTableName());
hpRef.set(theCharacter.hitPoints());
alignment.set(charDisplay.getPCAlignment());
refreshAvailableTempBonuses();
companionSupportFacade.refreshCompanionData();
updateLevelTodo();
buildAvailableDomainsList();
spellSupportFacade.refreshAvailableKnownSpells();
updateScorePurchasePool(false);
refreshEquipment();
if (charDisplay.getRace() == null || Constants.NONESELECTED.equals(charDisplay.getRace().getKeyName()))
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY, "Race", "in_irTodoRace", 100));
}
else
{
todoManager.removeTodo("in_irTodoRace");
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getTabNameRef()
*/
@Override
public ReferenceFacade<String> getTabNameRef()
{
return tabName;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setTabName(java.lang.String)
*/
@Override
public void setTabName(String name)
{
tabName.set(name);
theCharacter.setPCAttribute(PCAttribute.TABNAME, name);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getNameRef()
*/
@Override
public ReferenceFacade<String> getNameRef()
{
return name;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setName(java.lang.String)
*/
@Override
public void setName(String name)
{
this.name.set(name);
theCharacter.setName(name);
if (isNewCharName(charDisplay.getName()))
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY, "Name", "in_sumTodoName", 1));
}
else
{
todoManager.removeTodo("in_sumTodoName");
}
}
/**
* Check whether the field should be output.
* @param field The BiographyField to check export rules for.
* @return true if the field should be output, false if it may not be.
*/
@Override
public boolean getExportBioField(BiographyField field)
{
return !charDisplay.getSuppressBioField(field);
}
/**
* Set whether the field should be output.
* @param field The BiographyField to set export rules for.
* @param export Should the field be shown in output.
*/
@Override
public void setExportBioField(BiographyField field, boolean export)
{
theCharacter.setSuppressBioField(field, !export);
}
@Override
public ReferenceFacade<String> getSkinColorRef()
{
return skinColor;
}
@Override
public void setSkinColor(String color)
{
skinColor.set(color);
theCharacter.setPCAttribute(PCAttribute.SKINCOLOR, color);
}
@Override
public ReferenceFacade<String> getHairColorRef()
{
return hairColor;
}
@Override
public void setHairColor(String color)
{
hairColor.set(color);
theCharacter.setPCAttribute(PCAttribute.HAIRCOLOR, color);
}
@Override
public ReferenceFacade<String> getEyeColorRef()
{
return eyeColor;
}
@Override
public void setEyeColor(String color)
{
eyeColor.set(color);
theCharacter.setEyeColor(color);
}
@Override
public ReferenceFacade<Integer> getHeightRef()
{
return heightRef;
}
@Override
public void setHeight(int height)
{
int heightInInches =
Globals.getGameModeUnitSet().convertHeightFromUnitSet(height);
heightRef.set(height);
theCharacter.setHeight(heightInInches);
}
@Override
public ReferenceFacade<Integer> getWeightRef()
{
return weightRef;
}
@Override
public void setWeight(int weight)
{
int weightInPounds =
(int) Globals.getGameModeUnitSet().convertWeightFromUnitSet(
weight);
weightRef.set(weight);
theCharacter.setPCAttribute(NumericPCAttribute.WEIGHT,weightInPounds);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getDeityRef()
*/
@Override
public ReferenceFacade<DeityFacade> getDeityRef()
{
return deity;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setDeity(pcgen.core.facade.DeityFacade)
*/
@Override
public void setDeity(DeityFacade deity)
{
this.deity.set(deity);
if (deity instanceof Deity)
{
theCharacter.setDeity((Deity) deity);
}
refreshLanguageList();
buildAvailableDomainsList();
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#addDomain(pcgen.core.facade.DomainFacade)
*/
@Override
public void addDomain(DomainFacade domainFacade)
{
if (!(domainFacade instanceof DomainFacadeImpl))
{
return;
}
DomainFacadeImpl domainFI = (DomainFacadeImpl) domainFacade;
Domain domain = domainFI.getRawObject();
if (charDisplay.hasDomain(domain))
{
return;
}
if (!isQualifiedFor(domainFacade))
{
delegate.showErrorMessage(Constants.APPLICATION_NAME,
LanguageBundle.getFormattedString("in_qualifyMess", domain.getDisplayName()));
return;
}
// Check selected domains vs Max number allowed
if (charDisplay.getDomainCount() >= theCharacter.getMaxCharacterDomains())
{
delegate.showErrorMessage(Constants.APPLICATION_NAME,
LanguageBundle.getFormattedString("in_errorNoMoreDomains"));
return;
}
if (!theCharacter.hasDefaultDomainSource())
{
// No source for the domain yet? Default to the last added class level
int level = charDisplay.getLevelInfoSize();
PCLevelInfo highestLevelInfo = charDisplay.getLevelInfo(level - 1);
PCClass cls = theCharacter.getClassKeyed(highestLevelInfo.getClassKeyName());
theCharacter.setDefaultDomainSource(new ClassSource(cls, highestLevelInfo.getClassLevel()));
}
if (theCharacter.addDomain(domain))
{
domains.addElement(domainFI);
DomainApplication.applyDomain(theCharacter, domain);
theCharacter.calcActiveBonuses();
remainingDomains.set(theCharacter.getMaxCharacterDomains() - charDisplay.getDomainCount());
updateDomainTodo();
spellSupportFacade.refreshAvailableKnownSpells();
companionSupportFacade.refreshCompanionData();
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getDomains()
*/
@Override
public ListFacade<DomainFacade> getDomains()
{
return domains;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#removeDomain(pcgen.core.facade.DomainFacade)
*/
@Override
public void removeDomain(DomainFacade domain)
{
if (domains.removeElement(domain))
{
Domain dom = ((DomainFacadeImpl) domain).getRawObject();
DomainApplication.removeDomain(theCharacter, dom);
theCharacter.removeDomain(((DomainFacadeImpl) domain).getRawObject());
remainingDomains.set(theCharacter.getMaxCharacterDomains() - charDisplay.getDomainCount());
updateDomainTodo();
spellSupportFacade.refreshAvailableKnownSpells();
}
}
/**
* Update the todo list to reflect the change in number of domains.
*/
private void updateDomainTodo()
{
if (remainingDomains.get() > 0)
{
todoManager.addTodo(new TodoFacadeImpl(Tab.DOMAINS, "Domains", "in_domTodoDomainsLeft", 120));
todoManager.removeTodo("in_domTodoTooManyDomains");
}
else if (remainingDomains.get() < 0)
{
todoManager
.addTodo(new TodoFacadeImpl(Tab.DOMAINS, "Domains", "in_domTodoTooManyDomains", 120));
todoManager.removeTodo("in_domTodoDomainsLeft");
}
else
{
todoManager.removeTodo("in_domTodoDomainsLeft");
todoManager.removeTodo("in_domTodoTooManyDomains");
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getMaxDomains()
*/
@Override
public ReferenceFacade<Integer> getMaxDomains()
{
return maxDomains;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getRemainingDomainSelectionsRef()
*/
@Override
public ReferenceFacade<Integer> getRemainingDomainSelectionsRef()
{
return remainingDomains;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getAvailableDomains()
*/
@Override
public ListFacade<DomainFacade> getAvailableDomains()
{
return availDomains;
}
/**
* This method returns all available domains, without filtering.
*/
private void buildAvailableDomainsList()
{
List<DomainFacadeImpl> availDomainList = new ArrayList<>();
List<DomainFacadeImpl> selDomainList = new ArrayList<>();
Deity pcDeity = charDisplay.getDeity();
if (pcDeity != null)
{
for (CDOMReference<Domain> domainRef : pcDeity.getSafeListMods(Deity.DOMAINLIST))
{
Collection<AssociatedPrereqObject> assoc = pcDeity.getListAssociations(Deity.DOMAINLIST, domainRef);
for (AssociatedPrereqObject apo : assoc)
{
for (Domain d : domainRef.getContainedObjects())
{
if (!isDomainInList(availDomainList, d))
{
availDomainList.add(new DomainFacadeImpl(d, apo.getPrerequisiteList()));
}
}
}
}
}
// Loop through the available prestige domains
for (PCClass aClass : charDisplay.getClassList())
{
/*
* Need to do for the class, for compatibility, since level 0 is
* loaded into the class itself
*/
processDomainList(aClass, availDomainList);
processAddDomains(aClass, availDomainList);
for (int lvl = 0; lvl <= charDisplay.getLevel(aClass); lvl++)
{
PCClassLevel cl = charDisplay.getActiveClassLevel(aClass, lvl);
processAddDomains(cl, availDomainList);
processDomainList(cl, availDomainList);
}
}
// Loop through the character's selected domains
for (Domain d : charDisplay.getDomainSet())
{
DomainFacadeImpl domainFI = new DomainFacadeImpl(d);
boolean found = false;
for (DomainFacadeImpl row : availDomainList)
{
if (d.equals(row.getRawObject()))
{
domainFI = row;
found = true;
break;
}
}
if (!found)
{
availDomainList.add(domainFI);
}
if (!isDomainInList(selDomainList, d))
{
selDomainList.add(domainFI);
}
}
availDomains.updateContents(availDomainList);
domains.updateContents(selDomainList);
maxDomains.set(theCharacter.getMaxCharacterDomains());
remainingDomains.set(theCharacter.getMaxCharacterDomains() - charDisplay.getDomainCount());
updateDomainTodo();
}
/**
* Check if a domain is a list of domains, irrespective of prerequisites.
*
* @param qualDomainList The list of domains with their prerequisites.
* @param qualDomain The domain to search for.
* @return tue if the domain is in the list
*/
private boolean isDomainInList(List<DomainFacadeImpl> qualDomainList, Domain domain)
{
for (DomainFacadeImpl row : qualDomainList)
{
if (domain.equals(row.getRawObject()))
{
return true;
}
}
return false;
}
private void processAddDomains(CDOMObject cdo, final List<DomainFacadeImpl> availDomainList)
{
Collection<CDOMReference<Domain>> domainRefs = cdo.getListMods(PCClass.ALLOWED_DOMAINS);
if (domainRefs != null)
{
for (CDOMReference<Domain> ref : domainRefs)
{
Collection<AssociatedPrereqObject> assoc = cdo.getListAssociations(PCClass.ALLOWED_DOMAINS, ref);
for (AssociatedPrereqObject apo : assoc)
{
for (Domain d : ref.getContainedObjects())
{
/*
* TODO This gate produces a rather interesting, and
* potentially wrong situation. What if two ADDDOMAINS
* exist with different PRE? Doesn't this fail?
*/
if (!isDomainInList(availDomainList, d))
{
availDomainList.add(new DomainFacadeImpl(d, apo.getPrerequisiteList()));
}
}
}
}
}
}
private void processDomainList(CDOMObject obj, final List<DomainFacadeImpl> availDomainList)
{
for (QualifiedObject<CDOMSingleRef<Domain>> qo : obj.getSafeListFor(ListKey.DOMAIN))
{
CDOMSingleRef<Domain> ref = qo.getRawObject();
Domain domain = ref.get();
if (!isDomainInList(availDomainList, domain))
{
availDomainList.add(new DomainFacadeImpl(domain, qo.getPrerequisiteList()));
}
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getEquipmentSetRef()
*/
@Override
public ReferenceFacade<EquipmentSetFacade> getEquipmentSetRef()
{
return equipSet;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setEquipmentSet(pcgen.core.facade.EquipmentSetFacade)
*/
@Override
public void setEquipmentSet(EquipmentSetFacade set)
{
EquipmentSetFacade oldSet = equipSet.get();
if (oldSet != null)
{
oldSet.getEquippedItems().removeListListener(this);
oldSet.getEquippedItems().removeEquipmentListListener(this);
}
if (set instanceof EquipmentSetFacadeImpl)
{
((EquipmentSetFacadeImpl) set).activateEquipSet();
}
equipSet.set(set);
set.getEquippedItems().addListListener(this);
set.getEquippedItems().addEquipmentListListener(this);
refreshTotalWeight();
}
/**
* Regenerate the character's list of languages.
*/
void refreshLanguageList()
{
long startTime = new Date().getTime();
List<Language> sortedLanguages = new ArrayList<>(charDisplay.getLanguageSet());
Collections.sort(sortedLanguages);
languages.updateContents(sortedLanguages);
autoLanguagesCache = null;
boolean allowBonusLangAfterFirst = Globals.checkRule(RuleConstants.INTBONUSLANG);
boolean atFirstLvl = theCharacter.getTotalLevels() <= 1;
int bonusLangMax = theCharacter.getBonusLanguageCount();
currBonusLangs = new ArrayList<>();
CNAbility a = theCharacter.getBonusLanguageAbility();
List<String> currBonusLangNameList = theCharacter.getAssociationList(a);
for (LanguageFacade langFacade : languages)
{
Language lang = (Language) langFacade;
if (currBonusLangNameList.contains(lang.getKeyName()))
{
currBonusLangs.add(lang);
}
}
int bonusLangRemain = bonusLangMax - currBonusLangs.size();
if (!allowBonusLangAfterFirst && !atFirstLvl)
{
bonusLangRemain = 0;
}
numBonusLang.set(bonusLangRemain);
if (bonusLangRemain > 0)
{
if (allowBonusLangAfterFirst)
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY,
"Languages", "in_sumTodoBonusLanguage", 110));
todoManager.removeTodo("in_sumTodoBonusLanguageFirstOnly");
}
else
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY,
"Languages", "in_sumTodoBonusLanguageFirstOnly", 110));
todoManager.removeTodo("in_sumTodoBonusLanguage");
}
}
else
{
todoManager.removeTodo("in_sumTodoBonusLanguage");
todoManager.removeTodo("in_sumTodoBonusLanguageFirstOnly");
}
int numSkillLangSelected = 0;
int skillLangMax = 0;
//TODO: Need to cope with multiple skill languages
SkillFacade speakLangSkill = dataSet.getSpeakLanguageSkill();
if (speakLangSkill != null)
{
Skill skill = (Skill) speakLangSkill;
List<String> langList = theCharacter.getAssociationList(skill);
numSkillLangSelected = langList.size();
skillLangMax = SkillRankControl.getTotalRank(theCharacter, skill).intValue();
}
int skillLangRemain = skillLangMax - numSkillLangSelected;
numSkillLang.set(skillLangRemain);
if (skillLangRemain > 0)
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY,
"Languages", "in_sumTodoSkillLanguage", 112));
}
else
{
todoManager.removeTodo("in_sumTodoSkillLanguage");
}
if (skillLangRemain < 0)
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY,
"Languages", "in_sumTodoSkillLanguageTooMany", 112));
}
else
{
todoManager.removeTodo("in_sumTodoSkillLanguageTooMany");
}
long endTime = new Date().getTime();
Logging.log(Logging.DEBUG, "refreshLanguageList took " + (endTime - startTime) + " ms.");
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getLanguages()
*/
@Override
public ListFacade<LanguageFacade> getLanguages()
{
return languages;
}
@Override
public ListFacade<LanguageChooserFacade> getLanguageChoosers()
{
CNAbility cna = theCharacter.getBonusLanguageAbility();
DefaultListFacade<LanguageChooserFacade> chooserList = new DefaultListFacade<>();
chooserList.addElement(new LanguageChooserFacadeImpl(this,
LanguageBundle.getString("in_sumLangBonus"), cna)); //$NON-NLS-1$
SkillFacade speakLangSkill = dataSet.getSpeakLanguageSkill();
if (speakLangSkill != null)
{
chooserList.addElement(new LanguageChooserFacadeImpl(this,
LanguageBundle.getString("in_sumLangSkill"), //$NON-NLS-1$
(Skill) speakLangSkill));
}
return chooserList;
}
@Override
public void removeLanguage(LanguageFacade lang)
{
ChooseDriver owner = getLaguageOwner(lang);
if (owner == null)
{
return;
}
List<Language> availLangs = new ArrayList<>();
List<Language> selLangs = new ArrayList<>();
ChoiceManagerList<Language> choiceManager = ChooserUtilities.getChoiceManager(owner, theCharacter);
choiceManager.getChoices(theCharacter, availLangs, selLangs);
selLangs.remove(lang);
choiceManager.applyChoices(theCharacter, selLangs);
}
/**
* Identify the object that the language is associated with. i.e. The rules
* object that granted the ability to use the language.
* @param lang The language to be found.
* @return The granting rules object, or null if none or automatic.
*/
private ChooseDriver getLaguageOwner(LanguageFacade lang)
{
if (currBonusLangs.contains(lang))
{
return theCharacter.getBonusLanguageAbility();
}
else if (languages.containsElement(lang) && !isAutomatic(lang))
{
return (Skill) dataSet.getSpeakLanguageSkill();
}
return null;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getFileRef()
*/
@Override
public ReferenceFacade<File> getFileRef()
{
return file;
}
@Override
public void setFile(File file)
{
this.file.set(file);
try
{
theCharacter.setFileName(file.getCanonicalPath());
}
catch (IOException e)
{
Logging.errorPrint("CharacterFacadeImpl.setFile failed for " + file, e);
theCharacter.setFileName(file.getPath());
}
}
/**
* Retrieve a copy of the current character suitable for export. This
* attempts to minimise the expensive cloning function, by returning the
* previously cloned character if the base character has not changed in
* the meantime.
* @return A copy of the current character.
*/
private synchronized PlayerCharacter getExportCharacter()
{
PlayerCharacter exportPc = lastExportChar;
if (exportPc == null || theCharacter.getSerial() != lastExportCharSerial)
{
// Calling preparePCForOutput will mark export character as modified, so compare original character serial when checking for real changes
// Get serial at beginning so we can detect if a change occurs during clone and preparePCForOutput
lastExportCharSerial = theCharacter.getSerial();
exportPc = theCharacter.clone();
// Get the PC all up to date, (equipment and active bonuses etc)
exportPc.preparePCForOutput();
lastExportChar = exportPc;
// It is possible another thread changed PC during export; log for now, the next export will rebuild
int countSerialChanges = theCharacter.getSerial() - lastExportCharSerial;
if (countSerialChanges > 0)
{
Logging.log(Logging.DEBUG,
"Player character " + exportPc.getName() + " changed "
+ countSerialChanges + " times during export.");
}
}
return exportPc;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#export(pcgen.io.ExportHandler, java.io.BufferedWriter)
*/
@Override
public void export(ExportHandler theHandler, BufferedWriter buf) throws ExportException
{
final int maxRetries = 3;
for (int i = 0; i < maxRetries; i++)
{
try
{
Logging.log(Logging.DEBUG, "Starting export at serial " + theCharacter.getSerial() + " to " + theHandler.getTemplateFile());
PlayerCharacter exportPc = getExportCharacter();
//PlayerCharacter exportPc = theCharacter;
theHandler.write(exportPc, buf);
Logging.log(Logging.DEBUG, "Finished export at serial " + theCharacter.getSerial() + " to " + theHandler.getTemplateFile());
return;
}
catch (ConcurrentModificationException e)
{
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
for (Entry<Thread, StackTraceElement[]> threadEntry : allStackTraces.entrySet())
{
if (threadEntry.getValue().length > 1)
{
StringBuilder sb = new StringBuilder("Thread: " + threadEntry.getKey() + "\n");
for (StackTraceElement elem : threadEntry.getValue())
{
sb.append(" ");
sb.append(elem.toString());
sb.append("\n");
}
Logging.log(Logging.INFO, sb.toString());
}
}
Logging.log(Logging.WARNING, "Retrying export after ConcurrentModificationException", e);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e1)
{
Logging.errorPrint("Interrupted sleep - probably closing.");
return;
}
}
}
Logging.errorPrint("Unable to export using "
+ theHandler.getTemplateFile()
+ " due to concurrent modifications.");
}
@Override
public void setDefaultOutputSheet(boolean pdf, File outputSheet)
{
UIPropertyContext context = UIPropertyContext.getInstance();
String outputSheetPath = outputSheet.getAbsolutePath();
if (pdf)
{
context.setProperty(UIPropertyContext.DEFAULT_PDF_OUTPUT_SHEET, outputSheetPath);
}
else
{
context.setProperty(UIPropertyContext.DEFAULT_HTML_OUTPUT_SHEET, outputSheetPath);
}
if (context.getBoolean(UIPropertyContext.SAVE_OUTPUT_SHEET_WITH_PC))
{
if (pdf)
{
theCharacter.setSelectedCharacterPDFOutputSheet(outputSheetPath);
}
else
{
theCharacter.setSelectedCharacterHTMLOutputSheet(outputSheetPath);
}
}
}
@Override
public String getDefaultOutputSheet(boolean pdf)
{
UIPropertyContext context = UIPropertyContext.getInstance();
if (context.getBoolean(UIPropertyContext.SAVE_OUTPUT_SHEET_WITH_PC))
{
String sheet;
if (pdf)
{
sheet = theCharacter.getSelectedCharacterPDFOutputSheet();
}
else
{
sheet = theCharacter.getSelectedCharacterHTMLOutputSheet();
}
if (StringUtils.isNotEmpty(sheet))
{
return sheet;
}
}
if (pdf)
{
return context.getProperty(UIPropertyContext.DEFAULT_PDF_OUTPUT_SHEET);
}
return context.getProperty(UIPropertyContext.DEFAULT_HTML_OUTPUT_SHEET);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getHandedRef()
*/
@Override
public ReferenceFacade<HandedFacade> getHandedRef()
{
return handedness;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setHanded(java.lang.String)
*/
@Override
public void setHanded(HandedFacade handedness)
{
this.handedness.set(handedness);
theCharacter.setHanded((Handed) handedness);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getPlayersNameRef()
*/
@Override
public ReferenceFacade<String> getPlayersNameRef()
{
return playersName;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setPlayersName(java.lang.String)
*/
@Override
public void setPlayersName(String name)
{
playersName.set(name);
theCharacter.setPCAttribute(PCAttribute.PLAYERSNAME, name);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#isQualifiedFor(pcgen.core.facade.ClassFacade)
*/
@Override
public boolean isQualifiedFor(ClassFacade c)
{
if (c instanceof PCClass)
{
return theCharacter.isQualified((PCClass) c);
}
return false;
}
@Override
public UIDelegate getUIDelegate()
{
return delegate;
}
/**
* Save the character to disc using its filename. Note this method is not
* part of the CharacterFacade and should only be used by the
* ChracterManager class.
*
* @throws NullPointerException
* @throws IOException If the write fails
*/
public void save() throws NullPointerException, IOException
{
GameMode mode = (GameMode) dataSet.getGameMode();
List<CampaignFacade> campaigns = ListFacades.wrap(dataSet.getCampaigns());
(new PCGIOHandler()).write(theCharacter, mode, campaigns, file.get());
theCharacter.setDirty(false);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#isAutomatic(pcgen.core.facade.LanguageFacade)
*/
@Override
public boolean isAutomatic(LanguageFacade language)
{
if (autoLanguagesCache == null)
{
autoLanguagesCache = charDisplay.getAutoLanguages();
}
return autoLanguagesCache.contains(language);
}
public boolean isRemovable(LanguageFacade language)
{
if (isAutomatic(language))
{
return false;
}
if (currBonusLangs.contains(language))
{
boolean allowBonusLangAfterFirst = Globals.checkRule(RuleConstants.INTBONUSLANG);
boolean atFirstLvl = theCharacter.getTotalLevels() <= 1;
return allowBonusLangAfterFirst || atFirstLvl;
}
return true;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getCharacterLevelsFacade()
*/
@Override
public CharacterLevelsFacade getCharacterLevelsFacade()
{
return charLevelsFacade;
}
@Override
public DescriptionFacade getDescriptionFacade()
{
return descriptionFacade;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setXP(int)
*/
@Override
public void setXP(final int xp)
{
if (xp == currentXP.get())
{
// We've already processed this change, most likely via the adjustXP method
return;
}
theCharacter.setXP(xp);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getXPRef()
*/
@Override
public ReferenceFacade<Integer> getXPRef()
{
return currentXP;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#adjustXP(int)
*/
@Override
public void adjustXP(final int xp)
{
int currVal = currentXP.get();
int newVal = currVal + xp;
theCharacter.setXP(newVal);
checkForNewLevel();
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getXPForNextLevelRef()
*/
@Override
public ReferenceFacade<Integer> getXPForNextLevelRef()
{
return xpForNextlevel;
}
@Override
public ReferenceFacade<String> getXPTableNameRef()
{
return xpTableName;
}
@Override
public void setXPTable(String newTable)
{
xpTableName.set(newTable);
theCharacter.setXPTable(newTable);
checkForNewLevel();
}
private void checkForNewLevel()
{
currentXP.set(charDisplay.getXP());
xpForNextlevel.set(charDisplay.minXPForNextECL());
if (charDisplay.getXP() >= charDisplay.minXPForNextECL())
{
delegate.showInfoMessage(Constants.APPLICATION_NAME, SettingsHandler.getGame().getLevelUpMessage());
}
updateLevelTodo();
}
@Override
public ReferenceFacade<String> getCharacterTypeRef()
{
return characterType;
}
@Override
public void setCharacterType(String newType)
{
characterType.set(newType);
theCharacter.setCharacterType(newType);
theCharacter.calcActiveBonuses();
// This can affect traits mainly.
characterAbilities.rebuildAbilityLists();
}
@Override
public ReferenceFacade<String> getPreviewSheetRef()
{
return previewSheet;
}
@Override
public void setPreviewSheet(String newSheet)
{
previewSheet.set(newSheet);
theCharacter.setPreviewSheet(newSheet);
}
@Override
public ReferenceFacade<SkillFilter> getSkillFilterRef()
{
return skillFilter;
}
@Override
public void setSkillFilter(SkillFilter newFilter)
{
skillFilter.set(newFilter);
theCharacter.setSkillFilter(newFilter);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#setAge(int)
*/
@Override
public void setAge(final int age)
{
if (age == this.age.get())
{
// We've already processed this change, most likely via the setAgeCategory method
return;
}
theCharacter.setPCAttribute(NumericPCAttribute.AGE, age);
this.age.set(age);
updateAgeCategoryForAge();
refreshStatScores();
refreshLanguageList();
}
/**
* Update the character's age category based on their age.
*/
private void updateAgeCategoryForAge()
{
AgeSet ageSet = charDisplay.getAgeSet();
if (ageSet != null)
{
String ageCatName = ageSet.getName();
for (SimpleFacade ageCatFacade : ageCategoryList)
{
if (ageCatFacade.toString().equals(ageCatName))
{
ageCategory.set(ageCatFacade);
}
}
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getAgeRef()
*/
@Override
public ReferenceFacade<Integer> getAgeRef()
{
return age;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getAgeCategories()
*/
@Override
public ListFacade<SimpleFacade> getAgeCategories()
{
return ageCategoryList;
}
@Override
public void setAgeCategory(final SimpleFacade ageCat)
{
if (ageCat == this.ageCategory.get())
{
// We've already processed this change, most likely via the setAge method
return;
}
final Race pcRace = charDisplay.getRace();
final String selAgeCat = ageCat.toString();
if ((pcRace != null) && !pcRace.equals(Globals.s_EMPTYRACE))
{
if (selAgeCat != null)
{
final int idx = SettingsHandler.getGame().getBioSet().getAgeSetNamed(selAgeCat);
if (idx >= 0)
{
ageCategory.set(ageCat);
SettingsHandler.getGame().getBioSet().randomize("AGECAT" + Integer.toString(idx), theCharacter);
age.set(charDisplay.getAge());
ageCategory.set(ageCat);
refreshStatScores();
refreshLanguageList();
}
}
}
}
@Override
public ReferenceFacade<SimpleFacade> getAgeCategoryRef()
{
return ageCategory;
}
/**
* This method updates the purchase point pool and the stat total text. The
* stat total text will be updated whether we are in purchase mode or not.
* displayed
* @param checkPurchasePoints boolean true if the pool should be checked
* for available points before doing the update.
*/
private void updateScorePurchasePool(boolean checkPurchasePoints)
{
int usedStatPool = getUsedStatPool();
// Handle purchase mode for stats
if (SettingsHandler.getGame().isPurchaseStatMode())
{
// Let them dink on stats at 0th or 1st PC levels
if (canChangePurchasePool())
{
theCharacter.setCostPool(usedStatPool);
theCharacter.setPoolAmount(usedStatPool);
}
final String bString = Integer.toString(theCharacter.getCostPool());
// int availablePool = SettingsHandler.getPurchaseModeMethodPool();
int availablePool = theCharacter.getPointBuyPoints();
if (availablePool < 0)
{
availablePool = RollingMethods.roll(SettingsHandler.getGame().getPurchaseModeMethodPoolFormula());
theCharacter.setPointBuyPoints(availablePool);
}
if (availablePool != 0)
{
statTotalLabelText.set(LanguageBundle.getFormattedString("in_sumStatCost", SettingsHandler //$NON-NLS-1$
.getGame().getPurchaseModeMethodName()));
statTotalText.set(LanguageBundle.getFormattedString(
"in_sumStatPurchaseDisplay", bString, availablePool)); //$NON-NLS-1$
modTotalLabelText.set("");
modTotalText.set("");
}
if (checkPurchasePoints && (availablePool != 0))
{
//
// Let the user know that they've exceeded their goal, but allow them to keep going if they want...
// Only do this at 1st level or lower
//
if (canChangePurchasePool() && (availablePool > 0) && (usedStatPool > availablePool))
{
delegate.showInfoMessage(Constants.APPLICATION_NAME, LanguageBundle.getFormattedString(
"in_sumYouHaveExcededTheMaximumPointsOf", //$NON-NLS-1$
String.valueOf(availablePool), SettingsHandler.getGame().getPurchaseModeMethodName()));
}
}
}
// Non-purchase mode for stats
if (!SettingsHandler.getGame().isPurchaseStatMode() || (theCharacter.getPointBuyPoints() == 0))
{
int statTotal = 0;
int modTotal = 0;
for (PCStat aStat : charDisplay.getStatSet())
{
if (charDisplay.isNonAbility(aStat) || !aStat.getSafe(ObjectKey.ROLLED))
{
continue;
}
final int currentStat = theCharacter.getBaseStatFor(aStat);
final int currentMod = theCharacter.getStatModFor(aStat);
statTotal += currentStat;
modTotal += currentMod;
}
statTotalLabelText.set(LanguageBundle.getString("in_sumStatTotalLabel")); //$NON-NLS-1$
statTotalText
.set(LanguageBundle.getFormattedString("in_sumStatTotal", Integer.toString(statTotal)));
modTotalLabelText.set(LanguageBundle.getString("in_sumModTotalLabel"));
modTotalText.set(LanguageBundle.getFormattedString("in_sumModTotal", Integer.toString(modTotal)));
}
if (charLevelsFacade.getSize() == 0
&& (allAbilitiesAreZero() || (SettingsHandler.getGame().isPurchaseStatMode() && (theCharacter
.getPointBuyPoints() != getUsedStatPool()))))
{
todoManager.addTodo(new TodoFacadeImpl(Tab.SUMMARY, "Ability Scores", "in_sumTodoStats", 50));
}
else
{
todoManager.removeTodo("in_sumTodoStats");
}
}
/**
* Idenitfy if the character can stil change purchase pool values - spent
* or available. This action is restricted by level.
* @return true if the character is allowed to change the purchase pool
*/
public boolean canChangePurchasePool()
{
// This is a problem for races with non-0 level
// adjustment so only count PC & NPC levels, not
// monster levels XXX
int pcPlayerLevels = charDisplay.totalNonMonsterLevels();
int maxDiddleLevel;
if (poolPointText != null)
{
maxDiddleLevel = 0;
}
else
{
maxDiddleLevel = 1;
}
return pcPlayerLevels <= maxDiddleLevel;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getStatTotalLabelTextRef()
*/
@Override
public ReferenceFacade<String> getStatTotalLabelTextRef()
{
return statTotalLabelText;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getStatTotalTextRef()
*/
@Override
public ReferenceFacade<String> getStatTotalTextRef()
{
return statTotalText;
}
/**
* @return A reference to the label text for the character's modifier total
*/
@Override
public ReferenceFacade<String> getModTotalLabelTextRef()
{
return modTotalLabelText;
}
/**
* @return A reference to the text for the character's modifier total
*/
@Override
public ReferenceFacade<String> getModTotalTextRef()
{
return modTotalText;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getTodoList()
*/
@Override
public ListFacade<TodoFacade> getTodoList()
{
return todoManager.getTodoList();
}
/**
* @return the PlayerCharacter the facade is fronting for.
*/
PlayerCharacter getTheCharacter()
{
return theCharacter;
}
@Override
public ReferenceFacade<Integer> getTotalHPRef()
{
return hpRef;
}
@Override
public ReferenceFacade<Integer> getRollMethodRef()
{
return rollMethodRef;
}
@Override
public void refreshRollMethod()
{
if (!canChangePurchasePool())
{
return;
}
GameMode game = (GameMode) dataSet.getGameMode();
rollMethodRef.set(game.getRollMethod());
if (SettingsHandler.getGame().isPurchaseStatMode())
{
int availablePool = RollingMethods.roll(SettingsHandler.getGame().getPurchaseModeMethodPoolFormula());
theCharacter.setPointBuyPoints(availablePool);
// Make sure all scores are within the valid range
for (StatFacade stat : statScoreMap.keySet())
{
WriteableReferenceFacade<Number> score = statScoreMap.get(stat);
if (score.get().intValue() < SettingsHandler.getGame().getPurchaseScoreMin(theCharacter)
&& stat instanceof PCStat)
{
setStatToPurchaseNeutral((PCStat) stat, score);
}
}
}
hpRef.set(theCharacter.hitPoints());
updateScorePurchasePool(false);
}
/**
* Reset the stat score to the neutral value (usually 10) for
* the point buy method.
*
* @param pcStat The stata ebing adjusted.
* @param scoreRef The reference tothe current score.
*/
private void setStatToPurchaseNeutral(PCStat pcStat, WriteableReferenceFacade<Number> scoreRef)
{
int newScore = SettingsHandler.getGame().getPurchaseModeBaseStatScore(theCharacter);
if (StringUtils.isNotEmpty(validateNewStatBaseScore(newScore, pcStat, charDisplay.totalNonMonsterLevels())))
{
newScore = SettingsHandler.getGame().getPurchaseScoreMin(theCharacter);
if (StringUtils
.isNotEmpty(validateNewStatBaseScore(newScore, pcStat, charDisplay.totalNonMonsterLevels())))
{
return;
}
}
theCharacter.setStat(pcStat, newScore);
scoreRef.set(newScore);
}
@Override
public void adjustFunds(BigDecimal modVal)
{
BigDecimal currFunds = theCharacter.getGold();
theCharacter.setGold(currFunds.add(modVal));
updateWealthFields();
}
@Override
public void setFunds(BigDecimal newVal)
{
theCharacter.setGold(newVal);
updateWealthFields();
}
@Override
public ReferenceFacade<BigDecimal> getFundsRef()
{
return fundsRef;
}
@Override
public ReferenceFacade<BigDecimal> getWealthRef()
{
return wealthRef;
}
@Override
public ReferenceFacade<GearBuySellFacade> getGearBuySellRef()
{
return gearBuySellSchemeRef;
}
@Override
public void setGearBuySellRef(GearBuySellFacade gearBuySell)
{
gearBuySellSchemeRef.set(gearBuySell);
GearBuySellScheme scheme = (GearBuySellScheme) gearBuySell;
int rate = scheme.getBuyRate().intValue();
SettingsHandler.setGearTab_BuyRate(rate);
rate = scheme.getSellRate().intValue();
SettingsHandler.setGearTab_SellRate(rate);
}
/**
* Update the wealth related fields.
*/
private void updateWealthFields()
{
fundsRef.set(theCharacter.getGold());
wealthRef.set(theCharacter.totalValue());
}
@Override
public void setAllowDebt(boolean allowDebt)
{
this.allowDebt = allowDebt;
}
@Override
public boolean isAllowDebt()
{
return allowDebt;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getPurchasedEquipment()
*/
@Override
public EquipmentListFacade getPurchasedEquipment()
{
return purchasedEquip;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#addPurchasedEquipment(pcgen.core.facade.EquipmentFacade, int)
*/
@Override
public void addPurchasedEquipment(EquipmentFacade equipment, int quantity, boolean customize, boolean free)
{
if (equipment == null || quantity <= 0)
{
return;
}
// int nextOutputIndex = 1;
Equipment equipItemToAdjust = (Equipment) equipment;
if (customize)
{
equipItemToAdjust = openCustomizer(equipItemToAdjust);
if (equipItemToAdjust == null)
{
return;
}
}
else
{
if (equipItemToAdjust.getSafe(ObjectKey.MOD_CONTROL).getModifiersRequired())
{
if (!hasBeenAdjusted(equipItemToAdjust))
{
delegate.showErrorMessage(Constants.APPLICATION_NAME,
LanguageBundle
.getString("in_igBuyMustCustomizeItemFirst")); //$NON-NLS-1$
return;
}
}
}
Equipment updatedItem = theCharacter.getEquipmentNamed(equipItemToAdjust.getName());
if (!free
&& !canAfford(equipItemToAdjust, quantity,
(GearBuySellScheme) gearBuySellSchemeRef.get()))
{
delegate.showInfoMessage(Constants.APPLICATION_NAME,
LanguageBundle.getFormattedString("in_igBuyInsufficientFunds", quantity, //$NON-NLS-1$
equipItemToAdjust.getName()));
return;
}
if (updatedItem != null)
{
// item is already in inventory; update it
final double prevQty = (updatedItem.qty() < 0) ? 0 : updatedItem.qty();
final double newQty = prevQty + quantity;
theCharacter.updateEquipmentQty(updatedItem, prevQty, newQty);
Float qty = new Float(newQty);
updatedItem.setQty(qty);
purchasedEquip.setQuantity(equipment, qty.intValue());
}
else
{
// item is not in inventory; add it
updatedItem = equipItemToAdjust.clone();
if (updatedItem != null)
{
// Set the number carried and add it to the character
Float qty = new Float(quantity);
updatedItem.setQty(qty);
theCharacter.addEquipment(updatedItem);
}
purchasedEquip.addElement(updatedItem, quantity);
}
// Update the PC and equipment
if (!free)
{
double itemCost = calcItemCost(updatedItem, quantity, (GearBuySellScheme) gearBuySellSchemeRef.get());
theCharacter.adjustGold(itemCost * -1);
}
theCharacter.setCalcEquipmentList();
theCharacter.setDirty(true);
updateWealthFields();
}
private boolean hasBeenAdjusted(Equipment equipItemToAdjust)
{
Set<EquipmentModifier> allEqMods = new HashSet<>(equipItemToAdjust.getEqModifierList(true));
allEqMods.addAll(equipItemToAdjust.getEqModifierList(false));
for (EquipmentModifier eqMod : allEqMods)
{
if (!eqMod.isType(Constants.EQMOD_TYPE_BASEMATERIAL))
{
return true;
}
}
return false;
}
/**
* This method is called to determine whether the character can afford to buy
* the requested quantity of an item at the rate selected.
* @param selected Equipment item being bought, used to determine the base price
* @param purchaseQty double number of the item bought
* @param gearBuySellScheme The scheme for buying and selling rates
*
* This method was overhauled March, 2003 by sage_sam as part of FREQ 606205
* @return true if it can be afforded
*/
private boolean canAfford(Equipment selected, double purchaseQty, GearBuySellScheme gearBuySellScheme)
{
final float currentFunds = theCharacter.getGold().floatValue();
final double itemCost = calcItemCost(selected, purchaseQty, gearBuySellScheme);
return allowDebt || (itemCost <= currentFunds);
}
private double calcItemCost(Equipment selected, double purchaseQty, GearBuySellScheme gearBuySellScheme)
{
if (selected == null)
{
return 0;
}
BigDecimal rate = purchaseQty >= 0 ? gearBuySellScheme.getBuyRate() : gearBuySellScheme.getSellRate();
if (purchaseQty < 0 && selected.isSellAsCash())
{
rate = gearBuySellScheme.getCashSellRate();
}
return (purchaseQty * rate.intValue()) * (float) 0.01 * selected.getCost(theCharacter).floatValue();
}
private Equipment openCustomizer(Equipment aEq)
{
if (aEq == null)
{
return null;
}
Equipment newEquip = aEq.clone();
if (!newEquip.containsKey(ObjectKey.BASE_ITEM))
{
newEquip.put(ObjectKey.BASE_ITEM, CDOMDirectSingleRef.getRef(aEq));
}
List<VarModifier<?>> modifiers = newEquip.getListFor(ListKey.MODIFY);
if (modifiers != null)
{
for (VarModifier<?> vm : modifiers)
{
theCharacter.addModifier(vm, newEquip, newEquip);
}
}
for (EquipmentHead head : newEquip.getEquipmentHeads())
{
modifiers = head.getListFor(ListKey.MODIFY);
if (modifiers != null)
{
for (VarModifier<?> vm : modifiers)
{
theCharacter.addModifier(vm, head, head);
}
}
}
EquipmentBuilderFacadeImpl builder =
new EquipmentBuilderFacadeImpl(newEquip, theCharacter, delegate);
CustomEquipResult result =
delegate.showCustomEquipDialog(this, builder);
if (newEquip != null && result != CustomEquipResult.CANCELLED)
{
dataSet.addEquipment(newEquip);
}
//TODO if this is returning null, then the AggressiveSolverManager needs to destroy the unused channels :/
return result == CustomEquipResult.PURCHASE ? newEquip : null;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#removePurchasedEquipment(pcgen.core.facade.EquipmentFacade, int)
*/
@Override
public void removePurchasedEquipment(EquipmentFacade equipment, int quantity, boolean free)
{
if (equipment == null || quantity <= 0)
{
return;
}
Equipment equipItemToAdjust = (Equipment) equipment;
Equipment updatedItem = theCharacter.getEquipmentNamed(equipItemToAdjust.getName());
double numRemoved = 0;
// see if item is already in inventory; update it
if (updatedItem != null)
{
final double prevQty = (updatedItem.qty() < 0) ? 0 : updatedItem.qty();
numRemoved = Math.min(quantity, prevQty);
final double newQty = Math.max(prevQty - numRemoved, 0);
if (newQty <= 0)
{
// completely remove item
updatedItem.setNumberCarried(new Float(0));
updatedItem.setLocation(EquipmentLocation.NOT_CARRIED);
final Equipment eqParent = updatedItem.getParent();
if (eqParent != null)
{
eqParent.removeChild(theCharacter, updatedItem);
}
theCharacter.removeEquipment(updatedItem);
purchasedEquip.removeElement(updatedItem);
}
else
{
// update item count
theCharacter.updateEquipmentQty(updatedItem, prevQty, newQty);
Float qty = new Float(newQty);
updatedItem.setQty(qty);
updatedItem.setNumberCarried(qty);
purchasedEquip.setQuantity(equipment, qty.intValue());
}
theCharacter.updateEquipmentQty(updatedItem, prevQty, newQty);
Float qty = new Float(newQty);
updatedItem.setQty(qty);
updatedItem.setNumberCarried(qty);
}
// Update the PC and equipment
if (!free)
{
double itemCost =
calcItemCost(updatedItem, numRemoved * -1,
(GearBuySellScheme) gearBuySellSchemeRef.get());
theCharacter.adjustGold(itemCost * -1);
}
theCharacter.setCalcEquipmentList();
theCharacter.setDirty(true);
updateWealthFields();
}
@Override
public void deleteCustomEquipment(EquipmentFacade eqFacade)
{
if (eqFacade == null || !(eqFacade instanceof Equipment))
{
return;
}
Equipment itemToBeDeleted = (Equipment) eqFacade;
if (!itemToBeDeleted.isType(Constants.TYPE_CUSTOM))
{
return;
}
if (!delegate.showWarningConfirm(LanguageBundle
.getString("in_igDeleteCustomWarnTitle"), //$NON-NLS-1$
LanguageBundle.getFormattedString("in_igDeleteCustomWarning", //$NON-NLS-1$
itemToBeDeleted)))
{
return;
}
removePurchasedEquipment(itemToBeDeleted, Integer.MAX_VALUE, false);
Globals.getContext().getReferenceContext().forget(itemToBeDeleted);
if (dataSet.getEquipment() instanceof DefaultListFacade<?>)
{
((DefaultListFacade<EquipmentFacade>) dataSet.getEquipment())
.removeElement(itemToBeDeleted);
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#isQualifiedFor(pcgen.core.facade.EquipmentFacade)
*/
@Override
public boolean isQualifiedFor(EquipmentFacade equipment)
{
final Equipment equip = (Equipment) equipment;
final boolean accept = PrereqHandler.passesAll(equip.getPrerequisiteList(), theCharacter, equip);
if (accept && (equip.isShield() || equip.isWeapon() || equip.isArmor()))
{
return theCharacter.isProficientWith(equip);
}
return accept;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getEquipmentSizedForCharacter(pcgen.core.facade.EquipmentFacade)
*/
@Override
public EquipmentFacade getEquipmentSizedForCharacter(EquipmentFacade equipment)
{
final Equipment equip = (Equipment) equipment;
final SizeAdjustment newSize = charDisplay.getSizeAdjustment();
if (equip.getSizeAdjustment() == newSize || !Globals.canResizeHaveEffect(equip, null))
{
return equipment;
}
final String existingKey = equip.getKeyName();
final String newKey = equip.createKeyForAutoResize(newSize);
Equipment potential = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(Equipment.class, newKey);
if (newKey.equals(existingKey))
{
return equipment;
}
// If we've already resized this piece of equipment to this size
// on a previous occasion, just substitute that piece of equipment
// in place of the selected equipment.
if (potential != null)
{
return potential;
}
final String newName = equip.createNameForAutoResize(newSize);
potential = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(Equipment.class, newName);
if (potential != null)
{
return potential;
}
final Equipment newEq = equip.clone();
if (!newEq.containsKey(ObjectKey.BASE_ITEM))
{
newEq.put(ObjectKey.BASE_ITEM, CDOMDirectSingleRef.getRef(equip));
}
newEq.setName(newName);
newEq.put(StringKey.OUTPUT_NAME, newName);
newEq.put(StringKey.KEY_NAME, newKey);
newEq.resizeItem(theCharacter, newSize);
newEq.removeType(Type.AUTO_GEN);
newEq.removeType(Type.STANDARD);
if (!newEq.isType(Constants.TYPE_CUSTOM))
{
newEq.addType(Type.CUSTOM);
}
Globals.getContext().getReferenceContext().importObject(newEq);
return newEq;
}
/**
* Whether we should automatically resize all purchased gear to match the
* character's size.
* @return true if equipment should be auto resize.
*/
@Override
public boolean isAutoResize()
{
return theCharacter.isAutoResize();
}
/**
* Update whether we should automatically resize all purchased gear to match
* the character's size.
*
* @param autoResize The new value for auto resize equipment option.
*/
@Override
public void setAutoResize(boolean autoResize)
{
theCharacter.setAutoResize(autoResize);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#createEquipmentSet(java.lang.String)
*/
@Override
public EquipmentSetFacade createEquipmentSet(String setName)
{
String id = EquipmentSetFacadeImpl.getNewIdPath(charDisplay, null);
EquipSet eSet = new EquipSet(id, setName);
theCharacter.addEquipSet(eSet);
final EquipmentSetFacadeImpl facade =
new EquipmentSetFacadeImpl(delegate, theCharacter, eSet,
dataSet, purchasedEquip, todoManager, this);
equipmentSets.addElement(facade);
return facade;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#deleteEquipmentSet(pcgen.core.facade.EquipmentSetFacade)
*/
@Override
public void deleteEquipmentSet(EquipmentSetFacade set)
{
if (set == null || !(set instanceof EquipmentSetFacadeImpl))
{
return;
}
EquipmentSetFacadeImpl setImpl = (EquipmentSetFacadeImpl) set;
EquipSet eSet = setImpl.getEquipSet();
theCharacter.delEquipSet(eSet);
equipmentSets.removeElement(set);
}
@Override
public ReferenceFacade<String> getCarriedWeightRef()
{
return carriedWeightRef;
}
@Override
public ReferenceFacade<String> getLoadRef()
{
return loadRef;
}
@Override
public ReferenceFacade<String> getWeightLimitRef()
{
return weightLimitRef;
}
/* (non-Javadoc)
* @see pcgen.core.facade.EquipmentListFacade.EquipmentListListener#quantityChanged(pcgen.core.facade.EquipmentListFacade.EquipmentListEvent)
*/
@Override
public void quantityChanged(EquipmentListEvent e)
{
refreshTotalWeight();
}
/* (non-Javadoc)
* @see pcgen.core.facade.event.ListListener#elementAdded(pcgen.core.facade.event.ListEvent)
*/
@Override
public void elementAdded(ListEvent<EquipmentFacade> e)
{
refreshTotalWeight();
}
/* (non-Javadoc)
* @see pcgen.core.facade.event.ListListener#elementRemoved(pcgen.core.facade.event.ListEvent)
*/
@Override
public void elementRemoved(ListEvent<EquipmentFacade> e)
{
refreshTotalWeight();
}
/* (non-Javadoc)
* @see pcgen.core.facade.event.ListListener#elementsChanged(pcgen.core.facade.event.ListEvent)
*/
@Override
public void elementsChanged(ListEvent<EquipmentFacade> e)
{
refreshTotalWeight();
}
/* (non-Javadoc)
* @see pcgen.core.facade.event.ListListener#elementModified(pcgen.core.facade.event.ListEvent)
*/
@Override
public void elementModified(ListEvent<EquipmentFacade> e)
{
refreshTotalWeight();
}
/**
* Refreshes the total weight by reading it from the current equipment set.
*/
private void refreshTotalWeight()
{
String weight = Globals.getGameModeUnitSet().displayWeightInUnitSet(charDisplay.totalWeight().doubleValue());
carriedWeightRef.set(weight);
Load load = charDisplay.getLoadType();
loadRef.set(CoreUtility.capitalizeFirstLetter(load.toString()));
Float mult = SettingsHandler.getGame().getLoadInfo().getLoadMultiplier(load.toString());
double limit = 0.0f;
if (mult != null)
{
limit = charDisplay.getLoadToken(load.toString());
}
double lowerLimit = 0.0f;
for (Load testLoad : Load.values())
{
double testLimit = charDisplay.getLoadToken(testLoad.toString());
if (testLoad.compareTo(load) < 0 && testLimit > lowerLimit)
{
lowerLimit = testLimit;
}
}
StringBuilder loadLimit = new StringBuilder(Globals.getGameModeUnitSet().displayWeightInUnitSet(lowerLimit));
if (limit > 0)
{
loadLimit.append(" - ");
loadLimit.append(Globals.getGameModeUnitSet().displayWeightInUnitSet(limit));
}
else
{
loadLimit.append("+ ");
}
loadLimit.append(Globals.getGameModeUnitSet().getWeightUnit());
weightLimitRef.set(loadLimit.toString());
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterLevelsFacade.HitPointListener#hitPointsChanged(pcgen.core.facade.CharacterLevelsFacade.CharacterLevelEvent)
*/
@Override
public void hitPointsChanged(CharacterLevelEvent e)
{
hpRef.set(theCharacter.hitPoints());
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getInfoFactory()
*/
@Override
public InfoFactory getInfoFactory()
{
return infoFactory;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#isQualifiedFor(pcgen.core.facade.InfoFacade)
*/
@Override
public boolean isQualifiedFor(InfoFacade infoFacade)
{
if (!(infoFacade instanceof PObject))
{
return false;
}
PObject pObj = (PObject) infoFacade;
if (!theCharacter.isQualified(pObj))
{
return false;
}
if (infoFacade instanceof Kit)
{
Kit kit = (Kit) infoFacade;
BigDecimal totalCost = kit.getTotalCostToBeCharged(theCharacter);
if (totalCost != null)
{
if (theCharacter.getGold().compareTo(totalCost) < 0)
{
// Character cannto afford the kit
return false;
}
}
}
return true;
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#isQualifiedFor(pcgen.core.facade.DeityFacade)
*/
@Override
public boolean isQualifiedFor(DeityFacade deityFacade)
{
if (!(deityFacade instanceof Deity))
{
return false;
}
Deity aDeity = (Deity) deityFacade;
return PrereqHandler.passesAll(aDeity.getPrerequisiteList(), theCharacter, aDeity)
&& theCharacter.isQualified(aDeity);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#isQualifiedFor(pcgen.core.facade.DomainFacade)
*/
@Override
public boolean isQualifiedFor(DomainFacade domainFacade)
{
if (!(domainFacade instanceof DomainFacadeImpl))
{
return false;
}
DomainFacadeImpl domainFI = (DomainFacadeImpl) domainFacade;
Domain domain = domainFI.getRawObject();
if (!PrereqHandler.passesAll(domainFI.getPrerequisiteList(), theCharacter, domain)
|| !theCharacter.isQualified(domain))
{
return false;
}
return true;
}
@Override
public boolean isQualifiedFor(TempBonusFacade tempBonusFacade)
{
if (!(tempBonusFacade instanceof TempBonusFacadeImpl))
{
return false;
}
TempBonusFacadeImpl tempBonus = (TempBonusFacadeImpl) tempBonusFacade;
CDOMObject originObj = tempBonus.getOriginObj();
if (!theCharacter.isQualified(originObj))
{
return false;
}
return true;
}
@Override
public boolean isQualifiedFor(SpellFacade spellFacade,
ClassFacade classFacade)
{
if (!(spellFacade instanceof SpellFacadeImplem)
|| !(classFacade == null || classFacade instanceof PCClass))
{
return false;
}
SpellFacadeImplem spellFI = (SpellFacadeImplem) spellFacade;
PCClass pcClass = (PCClass) classFacade;
if (!theCharacter.isQualified(spellFI.getSpell()))
{
return false;
}
if (!spellFI.getCharSpell().isSpecialtySpell(theCharacter)
&& SpellCountCalc.isProhibited(spellFI.getSpell(), pcClass,
theCharacter))
{
return false;
}
return true;
}
@Override
public boolean isQualifiedFor(EquipmentFacade equipFacade, EquipModFacade eqModFacade)
{
if (!(equipFacade instanceof Equipment) || !(eqModFacade instanceof EquipmentModifier))
{
return false;
}
Equipment equip = (Equipment) equipFacade;
EquipmentModifier eqMod = (EquipmentModifier) eqModFacade;
//TODO: Handle second head
return equip.canAddModifier(theCharacter, eqMod, true);
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#addTemplate(pcgen.core.facade.TemplateFacade)
*/
@Override
public void addTemplate(TemplateFacade templateFacade)
{
if (templateFacade == null || !(templateFacade instanceof PCTemplate))
{
return;
}
PCTemplate template = (PCTemplate) templateFacade;
if (!PrereqHandler.passesAll(template.getPrerequisiteList(), theCharacter, template))
{
return;
}
if (!charDisplay.hasTemplate(template))
{
Logging.log(Logging.INFO, charDisplay.getName()
+ ": Adding template " + template); //$NON-NLS-1$
int oldLevel = charLevelsFacade.getSize();
if (theCharacter.addTemplate(template))
{
Logging.log(Logging.INFO, charDisplay.getName()
+ ": Successful add of template " + template); //$NON-NLS-1$
templates.addElement(template);
refreshRaceRelatedFields();
if (oldLevel != charLevelsFacade.getSize())
{
delegate.showLevelUpInfo(this, oldLevel);
}
}
else
{
Logging.log(
Logging.INFO,
charDisplay.getName()
+ ": Nope: Add template " + template + " failed because no selection was made"); //$NON-NLS-1$
}
}
else
{
delegate.showErrorMessage(Constants.APPLICATION_NAME, LanguageBundle.getString("in_irHaveTemplate"));
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#removeTemplate(pcgen.core.facade.TemplateFacade)
*/
@Override
public void removeTemplate(TemplateFacade templateFacade)
{
if (templateFacade == null || !(templateFacade instanceof PCTemplate))
{
return;
}
PCTemplate template = (PCTemplate) templateFacade;
if (charDisplay.hasTemplate(template) && template.isRemovable())
{
theCharacter.removeTemplate(template);
theCharacter.calcActiveBonuses();
templates.removeElement(template);
}
else
{
delegate.showErrorMessage(Constants.APPLICATION_NAME, LanguageBundle.getString("in_irNotRemovable"));
}
}
private void refreshTemplates()
{
Collection<PCTemplate> pcTemplates = charDisplay.getDisplayVisibleTemplateList();
for (PCTemplate template : pcTemplates)
{
if (!templates.containsElement(template))
{
templates.addElement(template);
}
}
for (Iterator<TemplateFacade> iterator = templates.iterator(); iterator.hasNext();)
{
PCTemplate pcTemplate = (PCTemplate) iterator.next();
if (!pcTemplates.contains(pcTemplate))
{
iterator.remove();
}
}
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getTemplates()
*/
@Override
public ListFacade<TemplateFacade> getTemplates()
{
return templates;
}
@Override
public void addCharacterChangeListener(CharacterChangeListener listener)
{
//TODO: implement this
}
@Override
public void removeCharacterChangeListener(CharacterChangeListener listener)
{
//TODO: implement this
}
/* (non-Javadoc)
* @see pcgen.core.facade.CharacterFacade#getSpellSupport()
*/
@Override
public SpellSupportFacade getSpellSupport()
{
return spellSupportFacade;
}
@Override
public ReferenceFacade<File> getPortraitRef()
{
return portrait;
}
@Override
public void setPortrait(File file)
{
portrait.set(file);
theCharacter.setPortraitPath(file == null ? null : file.getAbsolutePath());
}
@Override
public ReferenceFacade<Rectangle> getThumbnailCropRef()
{
return cropRect;
}
@Override
public void setThumbnailCrop(Rectangle rect)
{
cropRect.set(rect);
theCharacter.setPortraitThumbnailRect(rect);
}
@Override
public boolean isDirty()
{
return theCharacter.isDirty();
}
@Override
public CompanionSupportFacade getCompanionSupport()
{
return companionSupportFacade;
}
@Override
public String getCompanionType()
{
Follower master = charDisplay.getMaster();
if (master != null)
{
return master.getType().getKeyName();
}
return null;
}
@Override
public CharacterStubFacade getMaster()
{
Follower master = charDisplay.getMaster();
if (master == null)
{
return null;
}
CompanionNotLoaded stub = new CompanionNotLoaded(master.getName(), new File(master.getFileName()),
master.getRace(), master.getType().getKeyName());
CharacterFacade masterFacade = CharacterManager.getCharacterMatching(stub);
if (masterFacade != null)
{
return masterFacade;
}
return stub;
}
/**
* Since Rectangles are modifiable we make sure that no references of the reference
* object are leaked to the outside world. This guarantees that the underlying reference
* object will not changed after it is set.
*/
private static class RectangleReference extends DefaultReferenceFacade<Rectangle>
{
/**
* Create a new reference based on the supplied rectangle.
* @param rect
*/
public RectangleReference(Rectangle rect)
{
super(rect == null ? null : (Rectangle) rect.clone());
}
@Override
public Rectangle get()
{
Rectangle rect = super.get();
if (rect != null)
{
rect = (Rectangle) rect.clone();
}
return rect;
}
@Override
public void set(Rectangle rect)
{
Rectangle old = get();
if (ObjectUtils.equals(old, rect))
{
return;
}
if (rect != null)
{
rect = (Rectangle) rect.clone();
}
this.object = rect;
if (rect != null)
{
rect = (Rectangle) rect.clone();
}
fireReferenceChangedEvent(this, old, rect);
}
}
@Override
public DefaultListFacade<KitFacade> getKits()
{
return kitList;
}
@Override
public void addKit(KitFacade obj)
{
if (obj == null || !(obj instanceof Kit))
{
return;
}
Kit kit = (Kit) obj;
if (!theCharacter.isQualified(kit))
{
return;
}
Logging.log(Logging.INFO, charDisplay.getName()
+ ": Testing kit " + kit); //$NON-NLS-1$
List<BaseKit> thingsToAdd = new ArrayList<>();
List<String> warnings = new ArrayList<>();
kit.testApplyKit(theCharacter, thingsToAdd, warnings);
//
// See if user wants to apply the kit even though there were errors
//
if (!showKitWarnings(kit, warnings))
{
return;
}
// The user is applying the kit so use the real PC now.
Logging.log(Logging.INFO, charDisplay.getName()
+ ": Adding kit " + kit); //$NON-NLS-1$
kit.processKit(theCharacter, thingsToAdd);
kitList.addElement(obj);
// Kits can upate most things so do a thorough refresh
race.set(charDisplay.getRace());
refreshRaceRelatedFields();
name.set(charDisplay.getName());
characterType.set(charDisplay.getCharacterType());
// Deity and domains
deity.set(charDisplay.getDeity());
buildAvailableDomainsList();
refreshStatScores();
}
/**
*
*/
private void refreshEquipment()
{
fundsRef.set(theCharacter.getGold());
wealthRef.set(theCharacter.totalValue());
purchasedEquip.refresh(theCharacter.getEquipmentMasterList());
initEquipSet();
}
/**
* Show the user any warnings from thekit application and get
* their approval to continue.
*
* @param kit The kit being applied.
* @param warnings The warnigns generated in the test application.
* @return true if the kit should be applied, false if not.
*/
private boolean showKitWarnings(Kit kit, List<String> warnings)
{
if (warnings.isEmpty())
{
return true;
}
HtmlInfoBuilder warningMsg = new HtmlInfoBuilder();
warningMsg.append(LanguageBundle.getString("in_kitWarnStart")); //$NON-NLS-1$
warningMsg.appendLineBreak();
warningMsg.append("<UL>"); //$NON-NLS-1$
for (String string : warnings)
{
warningMsg.appendLineBreak();
warningMsg.append("<li>"); //$NON-NLS-1$
warningMsg.append(string);
warningMsg.append("</li>"); //$NON-NLS-1$
}
warningMsg.append("</UL>"); //$NON-NLS-1$
warningMsg.appendLineBreak();
warningMsg.append(LanguageBundle.getString("in_kitWarnEnd")); //$NON-NLS-1$
return delegate.showWarningConfirm(kit.getDisplayName(), warningMsg.toString());
}
@Override
public List<KitFacade> getAvailableKits()
{
List<KitFacade> kits = new ArrayList<>();
for (KitFacade obj : dataSet.getKits())
{
if (obj == null || !(obj instanceof Kit))
{
continue;
}
if (((Kit) obj).isVisible(theCharacter, View.VISIBLE_DISPLAY))
{
kits.add(obj);
}
}
return kits;
}
@Override
public VariableProcessor getVariableProcessor()
{
return theCharacter.getVariableProcessor();
}
@Override
public Float getVariable(String variableString, boolean isMax)
{
return theCharacter.getVariable(variableString, isMax);
}
@Override
public boolean matchesCharacter(PlayerCharacter pc)
{
return theCharacter != null && theCharacter.equals(pc);
}
@Override
public void modifyCharges(List<EquipmentFacade> targets)
{
List<Equipment> chargedEquip = new ArrayList<>();
for (EquipmentFacade equipmentFacade : targets)
{
if (equipmentFacade instanceof Equipment && ((Equipment) equipmentFacade).getMaxCharges() > 0)
{
chargedEquip.add((Equipment) equipmentFacade);
}
}
if (chargedEquip.isEmpty())
{
return;
}
for (Equipment equip : chargedEquip)
{
int selectedCharges = getSelectedCharges(equip);
if (selectedCharges < 0)
{
return;
}
equip.setRemainingCharges(selectedCharges);
purchasedEquip.modifyElement(equip);
}
}
private int getSelectedCharges(Equipment equip)
{
int minCharges = equip.getMinCharges();
int maxCharges = equip.getMaxCharges();
String selectedValue =
delegate.showInputDialog(equip.toString(), LanguageBundle
.getFormattedString("in_igNumCharges", //$NON-NLS-1$
Integer.toString(minCharges),
Integer.toString(maxCharges)), Integer.toString(equip
.getRemainingCharges()));
if (selectedValue == null)
{
return -1;
}
int charges;
try
{
charges = Integer.parseInt(selectedValue.trim());
}
catch (NumberFormatException e)
{
charges = minCharges-1;
}
if ((charges < minCharges) || (charges > maxCharges))
{
ShowMessageDelegate.showMessageDialog(LanguageBundle.getString("in_igValueOutOfRange"),
Constants.APPLICATION_NAME, MessageType.ERROR);
return getSelectedCharges(equip);
}
return charges;
}
@Override
public void addNote(List<EquipmentFacade> targets)
{
List<Equipment> notedEquip = new ArrayList<>();
for (EquipmentFacade equipmentFacade : targets)
{
if (equipmentFacade instanceof Equipment)
{
notedEquip.add((Equipment) equipmentFacade);
}
}
if (notedEquip.isEmpty())
{
return;
}
for (Equipment equip : notedEquip)
{
String note = getNote(equip);
if (note == null)
{
return;
}
equip.setNote(note);
purchasedEquip.modifyElement(equip);
}
}
private String getNote(Equipment equip)
{
return delegate.showInputDialog(equip.toString(), LanguageBundle
.getFormattedString("in_igEnterNote"), //$NON-NLS-1$
equip.getNote());
}
/**
* The Class {@code LanguageListener} tracks adding and removal of
* languages to the character.
*/
public class LanguageListener implements DataFacetChangeListener<CharID, Language>
{
@Override
public void dataAdded(DataFacetChangeEvent<CharID, Language> dfce)
{
if (dfce.getCharID() != theCharacter.getCharID())
{
return;
}
refreshLanguageList();
}
@Override
public void dataRemoved(DataFacetChangeEvent<CharID, Language> dfce)
{
if (dfce.getCharID() != theCharacter.getCharID())
{
return;
}
refreshLanguageList();
}
}
/**
* The Class {@code TemplateListener} tracks adding and removal of
* templates to the character.
*/
public class TemplateListener implements DataFacetChangeListener<CharID, PCTemplate>
{
@Override
public void dataAdded(DataFacetChangeEvent<CharID, PCTemplate> dfce)
{
if (dfce.getCharID() != theCharacter.getCharID())
{
return;
}
refreshTemplates();
}
@Override
public void dataRemoved(DataFacetChangeEvent<CharID, PCTemplate> dfce)
{
if (dfce.getCharID() != theCharacter.getCharID())
{
return;
}
refreshTemplates();
}
}
/**
* The Class {@code XPListener} tracks changes to the character's experience value.
*/
public class XPListener implements DataFacetChangeListener<CharID, Integer>
{
@Override
public void dataAdded(DataFacetChangeEvent<CharID, Integer> dfce)
{
if (dfce.getCharID() != theCharacter.getCharID())
{
return;
}
checkForNewLevel();
}
@Override
public void dataRemoved(DataFacetChangeEvent<CharID, Integer> dfce)
{
// Ignored - we will always get the added message.
}
}
/**
* The Class {@code AutoEquipListener} tracks changes to the character's automatically granted equipment.
*/
public class AutoEquipListener implements DataFacetChangeListener<CharID, QualifiedObject<CDOMReference<Equipment>>>
{
@Override
public void dataAdded(DataFacetChangeEvent<CharID, QualifiedObject<CDOMReference<Equipment>>> dfce)
{
if (dfce.getCharID() != theCharacter.getCharID())
{
return;
}
refreshEquipment();
}
@Override
public void dataRemoved(DataFacetChangeEvent<CharID, QualifiedObject<CDOMReference<Equipment>>> dfce)
{
if (dfce.getCharID() != theCharacter.getCharID())
{
return;
}
refreshEquipment();
}
}
@Override
public List<CoreViewNodeFacade> getCoreViewTree(CorePerspective pers)
{
List<CoreViewNodeFacade> coreDebugList = CoreUtils.buildCoreDebugList(theCharacter, pers);
return coreDebugList;
}
@Override
public CharID getCharID()
{
return theCharacter.getCharID();
}
}