/*
* PCGVer2Creator.java
* Copyright 2002 (C) Thomas Behr <ravenlock@gmx.de>
*
* 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 March 19, 2002, 4:15 PM
*
* Current Ver: $Revision$
*
*/
package pcgen.io;
import java.awt.Rectangle;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.apache.commons.lang3.StringUtils;
import pcgen.base.lang.StringUtil;
import pcgen.cdom.base.CDOMList;
import pcgen.cdom.base.CDOMListObject;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.base.CDOMReference;
import pcgen.cdom.base.Category;
import pcgen.cdom.base.Constants;
import pcgen.cdom.base.PersistentTransitionChoice;
import pcgen.cdom.base.SelectableSet;
import pcgen.cdom.base.TransitionChoice;
import pcgen.cdom.content.CNAbility;
import pcgen.cdom.enumeration.AssociationKey;
import pcgen.cdom.enumeration.AssociationListKey;
import pcgen.cdom.enumeration.BiographyField;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.Nature;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.enumeration.PCStringKey;
import pcgen.cdom.helper.CNAbilitySelection;
import pcgen.cdom.helper.ClassSource;
import pcgen.cdom.inst.PCClassLevel;
import pcgen.cdom.list.ClassSpellList;
import pcgen.cdom.reference.CDOMSingleRef;
import pcgen.core.Ability;
import pcgen.core.AbilityCategory;
import pcgen.core.BonusManager;
import pcgen.core.BonusManager.TempBonusInfo;
import pcgen.core.ChronicleEntry;
import pcgen.core.Deity;
import pcgen.core.Description;
import pcgen.core.Domain;
import pcgen.core.Equipment;
import pcgen.core.GameMode;
import pcgen.core.Globals;
import pcgen.core.Kit;
import pcgen.core.Language;
import pcgen.core.NoteItem;
import pcgen.core.PCAlignment;
import pcgen.core.PCClass;
import pcgen.core.PCStat;
import pcgen.core.PCTemplate;
import pcgen.core.PlayerCharacter;
import pcgen.core.SettingsHandler;
import pcgen.core.Skill;
import pcgen.core.SpecialAbility;
import pcgen.core.SpellProhibitor;
import pcgen.core.WeaponProf;
import pcgen.core.analysis.SpellLevel;
import pcgen.core.bonus.BonusObj;
import pcgen.core.character.CharacterSpell;
import pcgen.core.character.EquipSet;
import pcgen.core.character.Follower;
import pcgen.core.character.SpellBook;
import pcgen.core.character.SpellInfo;
import pcgen.core.display.BonusDisplay;
import pcgen.core.display.CharacterDisplay;
import pcgen.core.pclevelinfo.PCLevelInfo;
import pcgen.core.pclevelinfo.PCLevelInfoStat;
import pcgen.core.spell.Spell;
import pcgen.facade.core.CampaignFacade;
import pcgen.system.PCGenPropBundle;
import pcgen.util.FileHelper;
import pcgen.util.Logging;
import pcgen.util.StringPClassUtil;
/**
* {@code PCGVer2Creator}<br>
* Creates a line oriented format.
* Each line should adhere to the following grammar:<br>
*
* <i>line</i> := EMPTY | <i>comment</i> | <i>taglist</i>
* <i>comment</i> := '#' STRING
* <i>taglist</i> := tag ('|' tag)*
* <i>tag</i> := simpletag | nestedtag
* <i>nestedtag</i> := TAGNAME ':' '[' taglist ']'
* <i>simpletag</i> := TAGNAME ':' TAGVALUE
*
* @author Thomas Behr 19-03-02
*/
public final class PCGVer2Creator
{
/*
* DO NOT CHANGE line separator.
* Need to keep the Unix line separator to ensure cross-platform portability.
*
* author: Thomas Behr 2002-11-13
*/
private final PlayerCharacter thePC;
private final CharacterDisplay charDisplay;
private GameMode mode;
private List<? extends CampaignFacade> campaigns;
/**
* Constructor
* @param aPC
*/
public PCGVer2Creator(final PlayerCharacter aPC, GameMode mode, List<? extends CampaignFacade> campaigns)
{
thePC = aPC;
charDisplay = aPC.getDisplay();
this.mode = mode;
this.campaigns = campaigns;
}
/**
* create PCG string for a given PlayerCharacter
*
* <br>author: Thomas Behr 19-03-02
*
* @return a String in PCG format, containing all information
* PCGen associates with a given PlayerCharacter
*/
public String createPCGString()
{
// Guess that this should be about 1000
StringBuilder buffer = new StringBuilder(1000);
appendPCGVersionLine(buffer);
/*
* #System Information
* CAMPAIGNS:>:-delimited list<
* VERSION:x.x.x
* ROLLMETHOD:xxx
* PURCHASEPOINTS:Y or N|TYPE:>living City, Living greyhawk, etc<
* UNLIMITEDPOOLCHECKED:Y or N
* POOLPOINTS:>numeric value 0-?<
* GAMEMODE:DnD
* TABLABEL:0
* AUTOSPELLS:Y or N
* AUTOCOMPANIONS:Y or N
*
* hmmm, better have
* CAMPAIGNS:>campaign_name<|CAMPAIGNS:>campaign_name<|...
*/
appendNewline(buffer);
appendComment("System Information", buffer); //$NON-NLS-1$
//appendCampaignLineOldFormat(buffer);
appendCampaignLine(buffer);
appendVersionLine(buffer);
appendRollMethodLine(buffer);
appendPurchasePointsLine(buffer);
appendCharacterTypeLine(buffer);
appendPreviewSheetLine(buffer);
//appendUnlimitedPoolCheckedLine(buffer);
appendPoolPointsLine(buffer);
appendGameModeLine(buffer);
appendTabLabelLine(buffer);
appendAutoSpellsLine(buffer);
appendUseHigherSpellSlotsLines(buffer);
appendLoadCompanionLine(buffer);
appendUseTempModsLine(buffer);
appendOutputSheetsLines(buffer);
appendAutoSortLines(buffer);
appendSkillFilterLine(buffer);
appendGearCostSizeLines(buffer);
/*
* #Character Bio
* CHARACTERNAME:Code Monkey
* TABNAME:Code Monkey the Best Ever No Really!
* PLAYERNAME:Jason Monkey
* HEIGHT:75
* WEIGHT:198
* AGE:17
* GENDER:text
* HANDED:text
* SKIN:text
* EYECOLOR:text
* HAIRCOLOR:text
* HAIRSTYLE:text
* LOCATION:text
* CITY:text
* PERSONALITYTRAIT1:text
* PERSONALITYTRAIT2:text
* SPEECHPATTERN:text
* PHOBIAS:text
* INTERESTS:text
* CATCHPHRASE:text
*/
appendNewline(buffer);
appendComment("Character Bio", buffer); //$NON-NLS-1$
appendCharacterNameLine(buffer);
appendTabNameLine(buffer);
appendPlayerNameLine(buffer);
appendHeightLine(buffer);
appendWeightLine(buffer);
appendAgeLine(buffer);
appendGenderLine(buffer);
appendHandedLine(buffer);
appendSkinColorLine(buffer);
appendEyeColorLine(buffer);
appendHairColorLine(buffer);
appendHairStyleLine(buffer);
appendLocationLine(buffer);
appendResidenceLine(buffer);
appendBirthdayLine(buffer);
appendBirthplaceLine(buffer);
appendPersonalityTrait1Line(buffer);
appendPersonalityTrait2Line(buffer);
appendSpeechPatternLine(buffer);
appendPhobiasLine(buffer);
appendInterestsLine(buffer);
appendCatchPhraseLine(buffer);
appendPortraitLine(buffer);
/*
* #Character Attributes
* STAT:STR=18
* STAT:DEX=18
* STAT:CON=18
* STAT:INT=18
* STAT:WIS=18
* STAT:CHA=18
* ALIGN:LG
* RACE:Human
*
* hmmm better have
* STAT:STR|SCORE:18
*/
appendNewline(buffer);
appendComment("Character Attributes", buffer); //$NON-NLS-1$
appendStatLines(buffer);
appendAlignmentLine(buffer);
appendRaceLine(buffer);
appendFavoredClassLine(buffer);
/*
* #Character Class(es)
* CLASS:Fighter|LEVEL=3
* CLASSABILITIESLEVEL:Fighter=1(>This would only display up to the level the character has already,)
* CLASSABILITIESLEVEL:Fighter=2(>with any special abilities not covered by other areas,)
* CLASSABILITIESLEVEL:Fighter=3(>such as skills, feats, etc., but would list SA's, and the like<)
* CLASS:Wizard|LEVEL=1
* CLASSABILITIESLEVEL:Wizard=1(SA's, MEMORIZE:Y, etc)
*
* hmmm, better have
* CLASS:Fighter|LEVEL:3|SKILLPOOL:0
* CLASS:Wizard|LEVEL:1|SKILLPOOL:0|CANCASTPERDAY:1,1
*/
appendNewline(buffer);
appendComment("Character Class(es)", buffer); //$NON-NLS-1$
appendClassLines(buffer);
/*
* #Character Experience
* EXPERIENCE:6000
*/
appendNewline(buffer);
appendComment("Character Experience", buffer); //$NON-NLS-1$
appendExperienceLine(buffer);
appendExperienceTableLine(buffer);
/*
* #Character Templates
* TEMPLATESAPPLIED:If any, else this would just have the comment line, and skip to the next
*/
appendNewline(buffer);
appendComment("Character Templates", buffer); //$NON-NLS-1$
appendTemplateLines(buffer);
appendNewline(buffer);
appendComment("Character Region", buffer); //$NON-NLS-1$
appendRegionLine(buffer);
/*
* #Character Skills
* CLASSBOUGHT:Fighter
* SKILL:Alchemy|CROSSCLASS:Y|COST:2|RANK:7 (Should be Obvious what each of these does, I hope ;p)
* SKILL:Survival|CLASS:Y|COST:1|SYNERGY:Wilderness Lore=5=2|RANK:10
* CLASSBOUGHT:Wizard
* SKILL:Spellcraft|CLASS:Y|COST:1|RANK7
*
*
* hmmm, better have
* SKILL:Alchemy|SYNERGY:....|OUTPUTORDER:1|CLASSBOUGHT:[CLASS:FIGHTER|RANKS:7|COST:2|CLASSSKILL:N]
* SKILL:Spellcraft|SYNERGY:....|OUTPUTORDER:1|CLASSBOUGHT:[CLASS:WIZARD|RANKS:7|COST:1|CLASSSKILL:Y]
*/
appendNewline(buffer);
appendComment("Character Skills", buffer); //$NON-NLS-1$
appendSkillLines(buffer);
/*
* #Character Languages
*/
appendNewline(buffer);
appendComment("Character Languages", buffer); //$NON-NLS-1$
appendLanguageLine(buffer);
/*
* Anything that is already Pipe Delimited should be in
* parenthesis to avoid confusion on PCGen's part
*
* #Character Feats
* FEAT:Alertness|TYPE:General|(BONUS:SKILL|Listen,Spot|2)|DESC:+2 on Listen and Spot checks
*
* hmmm, better have colons and pipes encoded as entities
* FEAT:Alertness|TYPE:General|SAVE:BONUS:SKILL&pipe;Listen,Spot&pipe;2|DESC:+2 on Listen and Spot checks
*/
appendNewline(buffer);
appendComment("Character Feats", buffer); //$NON-NLS-1$
appendFeatLines(buffer);
appendNewline(buffer);
appendComment("Character Abilities", buffer); //$NON-NLS-1$
appendAbilityLines(buffer);
/*
* #Character Weapon proficiencies
*/
appendNewline(buffer);
appendComment("Character Weapon proficiencies", buffer); //$NON-NLS-1$
appendWeaponProficiencyLines(buffer);
/*
* This is the REALLY ugly part for all characters as it should contain ALL the information for the equipment
* Money goes here as well
*
* #Character Equipment
* EQUIPNAME:Longsword|OUTPUTORDER:2|COST:5|WT:5|QTY:1|>other info<
* EQUIPNAME:Backpack|OUTPUTORDER:9|COST:5|WT:5
* EQUIPNAME:Rope (Silk)|OUTPUTORDER:-1|COST:5|WT:5
*/
appendNewline(buffer);
appendComment("Character Equipment", buffer); //$NON-NLS-1$
appendMoneyLine(buffer);
appendEquipmentLines(buffer);
appendEquipmentSetLines(buffer);
/*
* Append Temporary Bonuses
*/
appendNewline(buffer);
appendComment("Temporary Bonuses", buffer); //$NON-NLS-1$
appendTempBonuses(buffer);
/*
* Append EquipSet Temp Bonuses
*/
appendNewline(buffer);
appendComment("EquipSet Temp Bonuses", buffer); //$NON-NLS-1$
appendEqSetBonuses(buffer);
/*
* #Character Deity/Domain
* DEITY:Yondalla|DEITYDOMAINS:Good,Law,Protection|ALIGNALLOW:013|DESC:Halflings, Protection, Fertility|SYMBOL:None|DEITYFAVWEAP:Sword (Short)|DEITYALIGN:ALIGN:LG
* DOMAIN:GOOD|DOMAINGRANTS:>list of abilities<
* DOMAINSPELLS:GOOD(>list of level by level spells)
*
* hmmm, better have
* DEITY:Yondalla|DEITYDOMAINS:[DOMAIN:Good|DOMAIN:Law|DOMAIN:Protection]|...
* DOMAINSPELLS:GOOD|SPELLLIST:(>list of level by level spells)
*/
appendNewline(buffer);
appendComment("Character Deity/Domain", buffer); //$NON-NLS-1$
appendDeityLine(buffer);
appendDomainLines(buffer);
/*
* This one is what will make spellcasters U G L Y!!!
*
* #Character Spells Information
* CLASS:Wizard|CANCASTPERDAY:2,4(Totals the levels all up + includes attribute bonuses)
* SPELLNAME:Blah|SCHOOL:blah|SUBSCHOOL:blah|Etc
*
* hmmm, moved CANCASTPERDAY to standard class line
*/
appendNewline(buffer);
appendComment("Character Spells Information", buffer); //$NON-NLS-1$
appendSpellBookLines(buffer);
appendSpellLines(buffer);
appendSpellListLines(buffer);
/*
* #Character Description/Bio/History
* CHARACTERBIO:any text that's in the BIO field
* CHARACTERDESC:any text that's in the BIO field
*/
appendNewline(buffer);
appendComment("Character Description/Bio/History", buffer); //$NON-NLS-1$
appendCharacterBioLine(buffer);
appendCharacterDescLine(buffer);
appendCharacterCompLine(buffer);
appendCharacterAssetLine(buffer);
appendCharacterMagicLine(buffer);
appendCharacterDmNotesLine(buffer);
/*
* #Kits
*/
appendNewline(buffer);
appendComment("Kits", buffer); //$NON-NLS-1$
appendKitLines(buffer);
/*
* #Character Master/Followers
* MASTER:Mynex|TYPE:Follower|HITDICE:20|FILE:E$\DnD\dnd-chars\ravenlock.pcg
* FOLLOWER:Raven|TYPE:Animal Companion|HITDICE:5|FILE:E$\DnD\dnd-chars\raven.pcg
*/
appendNewline(buffer);
appendComment("Character Master/Follower", buffer); //$NON-NLS-1$
appendFollowerLines(buffer);
/*
* #Character Notes Tab
*/
appendNewline(buffer);
appendComment("Character Notes Tab", buffer); //$NON-NLS-1$
appendNotesLines(buffer);
/*
* #AgeSet Kit selections
*/
appendNewline(buffer);
appendComment("Age Set Selections", buffer); //$NON-NLS-1$
appendAgeSetLine(buffer);
/*
* #Campaign History
*/
appendNewline(buffer);
appendComment("Campaign History", buffer); //$NON-NLS-1$
appendCampaignHistoryLines(buffer);
/*
* #Suppressed fields
*/
appendNewline(buffer);
appendComment("Suppressed Biography Fields", buffer); //$NON-NLS-1$
appendSuppressBioFieldLines(buffer);
/*
* Add one more newline at end of file
*/
appendNewline(buffer);
// All done!
return buffer.toString();
}
private void appendCampaignLine(StringBuilder buffer)
{
String del = Constants.EMPTY_STRING;
if (campaigns != null)
{
for (CampaignFacade campaign : campaigns)
{
buffer.append(del);
buffer.append(IOConstants.TAG_CAMPAIGN).append(':');
buffer.append(campaign.getKeyName());
del = "|"; //$NON-NLS-1$
}
buffer.append(IOConstants.LINE_SEP);
}
}
/**
* @param buffer
*/
private void appendSuppressBioFieldLines(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_SUPPRESS_BIO_FIELDS).append(':');
String delim = Constants.EMPTY_STRING;
for (BiographyField field : BiographyField.values())
{
if (charDisplay.getSuppressBioField(field))
{
buffer.append(delim);
buffer.append(field);
delim = "|"; //$NON-NLS-1$
}
}
buffer.append(IOConstants.LINE_SEP);
}
private GameMode getGameMode()
{
if (mode != null)
{
return mode;
}
else
{
return SettingsHandler.getGame();
}
}
private void appendGameModeLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_GAMEMODE).append(':');
buffer.append(getGameMode().getName());
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* private helper methods
* ###############################################################
*/
private static void appendPCGVersionLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PCGVERSION).append(':');
buffer.append("2.0"); //$NON-NLS-1$
buffer.append(IOConstants.LINE_SEP);
}
private void appendPurchasePointsLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PURCHASEPOINTS).append(':');
if (getGameMode().isPurchaseStatMode())
{
buffer.append('Y');
buffer.append('|');
buffer.append(IOConstants.TAG_TYPE).append(':');
buffer
.append(getGameMode().getPurchaseModeMethodName());
}
else
{
buffer.append('N');
}
buffer.append(IOConstants.LINE_SEP);
}
private void appendRollMethodLine(StringBuilder buffer)
{
final GameMode game = getGameMode();
buffer.append(IOConstants.TAG_ROLLMETHOD).append(':');
buffer.append(game.getRollMethod());
buffer.append('|');
buffer.append(IOConstants.TAG_EXPRESSION).append(':');
switch (game.getRollMethod())
{
case Constants.CHARACTER_STAT_METHOD_ALL_THE_SAME:
buffer.append(game.getAllStatsValue());
break;
case Constants.CHARACTER_STAT_METHOD_PURCHASE:
buffer.append(game.getPurchaseModeMethodName());
break;
case Constants.CHARACTER_STAT_METHOD_ROLLED:
buffer.append(game.getRollMethodExpression());
break;
default:
buffer.append(0);
break;
}
buffer.append(IOConstants.LINE_SEP);
}
/*
* modified this function to output the version number
* as displayed in pcgenprop.properties instead of a simple int.
* This will record the version more accurately.
*/
private static void appendVersionLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_VERSION).append(':');
buffer.append(PCGenPropBundle.getVersionNumber());
buffer.append(IOConstants.LINE_SEP);
}
private void appendAgeLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_AGE).append(':');
buffer.append(charDisplay.getAge());
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* AgeSet
* ###############################################################
*/
private void appendAgeSetLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_AGESET);
for (int i = 0; i < 10; i++)
{
buffer.append(':');
if (thePC.hasMadeKitSelectionForAgeSet(i))
{
buffer.append('1');
}
else
{
buffer.append('0');
}
}
buffer.append(IOConstants.LINE_SEP);
}
private void appendAlignmentLine(StringBuilder buffer)
{
//
// Only save alignment if game mode supports it
//
if (!Globals.getGameModeAlignmentText().isEmpty() && charDisplay.getPCAlignment() != null)
{
buffer.append(IOConstants.TAG_ALIGNMENT).append(':');
buffer.append(charDisplay.getPCAlignment().getKeyName());
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendBirthdayLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_BIRTHDAY).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.BIRTHDAY)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendBirthplaceLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_BIRTHPLACE).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.BIRTHPLACE)));
buffer.append(IOConstants.LINE_SEP);
}
/**
* @param buffer
*/
private void appendCampaignHistoryLines(StringBuilder buffer)
{
for (ChronicleEntry ce : charDisplay.getChronicleEntries())
{
buffer.append(IOConstants.TAG_CHRONICLE_ENTRY).append(':');
buffer.append(ce.isOutputEntry()?"Y":"N:");
buffer.append('|');
buffer.append(IOConstants.TAG_CAMPAIGN).append(':');
buffer.append(EntityEncoder.encode(ce.getCampaign()));
buffer.append('|');
buffer.append(IOConstants.TAG_ADVENTURE).append(':');
buffer.append(EntityEncoder.encode(ce.getAdventure()));
buffer.append('|');
buffer.append(IOConstants.TAG_PARTY).append(':');
buffer.append(EntityEncoder.encode(ce.getParty()));
buffer.append('|');
buffer.append(IOConstants.TAG_DATE).append(':');
buffer.append(EntityEncoder.encode(ce.getDate()));
buffer.append('|');
buffer.append(IOConstants.TAG_EXPERIENCE).append(':');
buffer.append(ce.getXpField());
buffer.append('|');
buffer.append(IOConstants.TAG_GM).append(':');
buffer.append(EntityEncoder.encode(ce.getGmField()));
buffer.append('|');
buffer.append(IOConstants.TAG_CHRONICLE).append(':');
buffer.append(EntityEncoder.encode(ce.getChronicle()));
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendCatchPhraseLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CATCHPHRASE).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.CATCHPHRASE)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendCharacterAssetLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERASSET).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.ASSETS)));
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Description/Bio/History methods
* ###############################################################
*/
private void appendCharacterBioLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERBIO).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.BIO)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendCharacterCompLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERCOMP).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.COMPANIONS)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendCharacterDescLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERDESC).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.DESCRIPTION)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendCharacterMagicLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERMAGIC).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.MAGIC)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendCharacterDmNotesLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERDMNOTES).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.GMNOTES)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendCharacterTypeLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERTYPE).append(':');
buffer.append(charDisplay.getCharacterType());
buffer.append(IOConstants.LINE_SEP);
}
private void appendPreviewSheetLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PREVIEWSHEET).append(':');
buffer.append(charDisplay.getPreviewSheet());
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Class(es) methods
* ###############################################################
*/
private void appendClassLines(StringBuilder buffer)
{
Cache specials = new Cache();
for (PCClass pcClass : charDisplay.getClassSet())
{
int classLevel = charDisplay.getLevel(pcClass);
buffer.append(IOConstants.TAG_CLASS).append(':');
buffer.append(EntityEncoder.encode(pcClass.getKeyName()));
final String subClassKey = charDisplay.getSubClassName(pcClass);
if (subClassKey != null && !Constants.EMPTY_STRING.equals(subClassKey))
{
buffer.append('|');
buffer.append(IOConstants.TAG_SUBCLASS).append(':');
buffer.append(EntityEncoder.encode(subClassKey));
}
buffer.append('|');
buffer.append(IOConstants.TAG_LEVEL).append(':');
buffer.append(classLevel);
buffer.append('|');
buffer.append(IOConstants.TAG_SKILLPOOL).append(':');
Integer currentPool = thePC.getSkillPool(pcClass);
buffer.append(currentPool == null ? 0 : currentPool);
// determine if this class can cast spells
boolean isCaster = false;
if (!thePC.getSpellSupport(pcClass).canCastSpells(thePC))
{
isCaster = true;
}
boolean isPsionic = thePC.getSpellSupport(pcClass).hasKnownList() && !isCaster;
if (isCaster || isPsionic)
{
buffer.append('|');
buffer.append(IOConstants.TAG_SPELLBASE).append(':');
buffer.append(EntityEncoder.encode(pcClass.getSpellBaseStat()));
buffer.append('|');
buffer.append(IOConstants.TAG_CANCASTPERDAY).append(':');
buffer.append(StringUtil.join(thePC.getSpellSupport(pcClass)
.getCastListForLevel(classLevel), ","));
}
Collection<? extends SpellProhibitor> prohib = charDisplay
.getProhibitedSchools(pcClass);
if (prohib != null)
{
Set<String> set = new TreeSet<>();
for (SpellProhibitor sp : prohib)
{
set.addAll(sp.getValueList());
}
if (!set.isEmpty())
{
buffer.append('|');
buffer.append(IOConstants.TAG_PROHIBITED).append(':');
buffer.append(EntityEncoder.encode(StringUtil
.join(set, ",")));
}
}
appendAddTokenInfo(buffer, pcClass);
buffer.append(IOConstants.LINE_SEP);
String spec = thePC.getAssoc(pcClass, AssociationKey.SPECIALTY);
if (spec != null)
{
specials.put(pcClass.getKeyName() + IOConstants.TAG_SPECIALTY + '0', spec);
}
String key;
key = pcClass.getKeyName() + IOConstants.TAG_SAVE + '0';
List<? extends SpecialAbility> salist = charDisplay.getUserSpecialAbilityList(pcClass);
if (salist != null)
{
for (SpecialAbility sa : salist)
{
specials.put(pcClass.getKeyName() + IOConstants.TAG_SA + 0, sa
.getKeyName());
break;
}
}
for (BonusObj save : thePC.getSaveableBonusList(pcClass))
{
specials.put(key, "BONUS|" + save);
}
for (int i = 1; i <= charDisplay.getLevel(pcClass); i++)
{
key = pcClass.getKeyName() + IOConstants.TAG_SAVE + (i - 1);
PCClassLevel pcl = charDisplay.getActiveClassLevel(pcClass, i);
for (BonusObj save : thePC.getSaveableBonusList(pcl))
{
specials.put(key, "BONUS|" + save);
}
}
}
//
// Save level up information in the order of levelling
//
for (PCLevelInfo pcl : charDisplay.getLevelInfo())
{
final String classKeyName = pcl.getClassKeyName();
int lvl = pcl.getClassLevel() - 1;
PCClass pcClass = thePC.getClassKeyed(classKeyName);
buffer.append(IOConstants.TAG_CLASSABILITIESLEVEL).append(':');
if (pcClass == null)
{
pcClass =
Globals.getContext().getReferenceContext()
.silentlyGetConstructedCDOMObject(PCClass.class,
classKeyName);
if (pcClass != null)
{
pcClass =
thePC.getClassKeyed(pcClass.get(ObjectKey.EX_CLASS)
.get().getKeyName());
}
}
if (pcClass != null)
{
buffer.append(EntityEncoder.encode(pcClass.getKeyName()));
}
else
{
buffer.append(EntityEncoder.encode("???")); //$NON-NLS-1$
}
buffer.append('=').append(lvl + 1);
if (pcClass != null)
{
String aKey =
charDisplay.getSubstitutionClassName(charDisplay
.getActiveClassLevel(pcClass, lvl + 1));
if (aKey != null)
{
buffer.append('|');
buffer.append(IOConstants.TAG_SUBSTITUTIONLEVEL).append(':');
buffer.append(aKey);
}
buffer.append('|');
buffer.append(IOConstants.TAG_HITPOINTS).append(':');
PCClassLevel classLevel = charDisplay.getActiveClassLevel(pcClass, lvl);
Integer hp = charDisplay.getHP(classLevel);
buffer.append(hp == null ? 0 : hp);
appendSpecials(buffer, specials.get(pcClass.getKeyName()
+ IOConstants.TAG_SAVE + lvl), IOConstants.TAG_SAVES, IOConstants.TAG_SAVE, lvl);
appendSpecials(buffer, specials.get(pcClass.getKeyName()
+ IOConstants.TAG_SPECIALTY + lvl), IOConstants.TAG_SPECIALTIES, IOConstants.TAG_SPECIALTY, lvl);
appendSpecials(buffer, specials.get(pcClass.getKeyName()
+ IOConstants.TAG_SA + lvl), IOConstants.TAG_SPECIALABILITIES, IOConstants.TAG_SA, lvl);
if (lvl == 0)
{
appendSpecials(buffer, specials.get(pcClass.getKeyName()
+ IOConstants.TAG_SA + (lvl - 1)), IOConstants.TAG_SPECIALABILITIES, IOConstants.TAG_SA, -1);
}
//
// Remember what choices were made for each of the ADD: tags
//
appendAddTokenInfo(buffer, charDisplay.getActiveClassLevel(pcClass, lvl + 1));
}
List<PCLevelInfoStat> statList = pcl.getModifiedStats(true);
if (statList != null)
{
for (PCLevelInfoStat stat : statList)
{
buffer.append('|').append(IOConstants.TAG_PRESTAT).append(':').append(
stat.toString());
}
}
statList = pcl.getModifiedStats(false);
if (statList != null)
{
for (PCLevelInfoStat stat : statList)
{
buffer.append('|').append(IOConstants.TAG_PRESTAT).append(':').append(
stat.toString());
}
}
int sp = pcl.getSkillPointsGained(thePC);
//if (sp != 0)
{
buffer.append('|').append(IOConstants.TAG_SKILLPOINTSGAINED).append(':')
.append(sp);
}
sp = pcl.getSkillPointsRemaining();
//if (sp != 0)
{
buffer.append('|').append(IOConstants.TAG_SKILLPOINTSREMAINING).append(':')
.append(sp);
}
buffer.append(IOConstants.LINE_SEP);
}
}
/**
* Convenience Method
*
* <br>author: Thomas Behr 19-03-02
*
* @param comment
* @param buffer
*/
private static void appendComment(String comment, StringBuilder buffer)
{
buffer.append(createComment(comment));
}
/*
* ###############################################################
* Character Deity/Domain methods
* ###############################################################
*/
private void appendDeityLine(StringBuilder buffer)
{
if (charDisplay.getDeity() != null)
{
final Deity aDeity = charDisplay.getDeity();
buffer.append(IOConstants.TAG_DEITY).append(':');
buffer.append(EntityEncoder.encode(aDeity.getKeyName()));
/*
* currently unused information
*
* author: Thomas Behr 09-09-02
*/
buffer.append('|');
buffer.append(IOConstants.TAG_DEITYDOMAINS).append(':');
buffer.append('[');
String del = Constants.EMPTY_STRING;
for (CDOMReference<Domain> ref : aDeity
.getSafeListMods(Deity.DOMAINLIST))
{
for (Domain d : ref.getContainedObjects())
{
buffer.append(del);
buffer.append(IOConstants.TAG_DOMAIN).append(':');
buffer.append(EntityEncoder.encode(d.getKeyName()));
del = "|"; //$NON-NLS-1$
}
}
buffer.append(']');
buffer.append('|');
buffer.append(IOConstants.TAG_ALIGNALLOW).append(':');
//TODO Need to clean this up?
for (final Description desc : aDeity
.getSafeListFor(ListKey.DESCRIPTION))
{
buffer.append('|');
buffer.append(IOConstants.TAG_DESC).append(':');
buffer.append(desc.getPCCText());
}
buffer.append('|');
buffer.append(IOConstants.TAG_DEITYFAVWEAP).append(':');
buffer.append('[');
List<CDOMReference<WeaponProf>> dwp =
aDeity.getListFor(ListKey.DEITYWEAPON);
if (dwp != null)
{
del = Constants.EMPTY_STRING;
for (CDOMReference<WeaponProf> ref : dwp)
{
buffer.append(del);
buffer.append(IOConstants.TAG_WEAPON).append(':');
buffer.append(EntityEncoder.encode(ref.getLSTformat(false)));
del = "|"; //$NON-NLS-1$
}
}
buffer.append(']');
buffer.append('|');
buffer.append(IOConstants.TAG_DEITYALIGN).append(':');
CDOMSingleRef<PCAlignment> al = aDeity.get(ObjectKey.ALIGNMENT);
if (al != null)
{
buffer.append(al.getLSTformat(false));
}
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendDomainLines(StringBuilder buffer)
{
for (final Domain domain : charDisplay.getDomainSet())
{
// TODO is any of this commented out code any use anymore?:
//
// // improve here - performance and concept!!!!
// domainSpells.clear();
// for (Iterator it2 = Globals.getSpellMap().values().iterator(); it2.hasNext();)
// {
// aSpell = (Spell)it2.next();
// // levelString = aSpell.levelForClass(aDomain.getName());
// if ((levelString.length() > 0) &&
// (levelString.indexOf("-1") < 0))
// {
// tokens = new StringTokenizer(levelString, ",");
// while (tokens.hasMoreTokens())
// {
// if (tokens.nextToken().equals(aDomain.getName()))
// {
// break;
// }
// }
// domainSpells.add(((tokens.hasMoreTokens()) ? tokens.nextToken() + " " : "") +
// aSpell.getName());
// }
// }
buffer.append(IOConstants.TAG_DOMAIN).append(':');
buffer.append(EntityEncoder.encode(domain.getKeyName()));
for (String assoc : thePC.getAssociationList(domain))
{
buffer.append('|');
buffer.append(IOConstants.TAG_ASSOCIATEDDATA).append(':');
buffer.append(EntityEncoder.encode(assoc));
}
for (final Description desc : domain
.getSafeListFor(ListKey.DESCRIPTION))
{
buffer.append('|');
buffer.append(IOConstants.TAG_DOMAINGRANTS).append(':');
buffer.append(desc.getPCCText());
}
buffer.append('|');
appendSourceInTaggedFormat(buffer, getDomainSourcePcgString(domain));
// buffer.append('|');
// buffer.append(TAG_DOMAINFEATS).append(':');
// buffer.append(aDomain.getFeatList());
// buffer.append('|');
// buffer.append(TAG_DOMAINSKILLS).append(':');
// buffer.append(aDomain.getSkillList());
// buffer.append('|');
// buffer.append(TAG_DOMAINSPECIALS).append(':');
// buffer.append(aDomain.getSpecialAbility());
// buffer.append('|');
// buffer.append(TAG_DOMAINSPELLS).append(':');
// buffer.append(aDomain.getSpellList());
appendAddTokenInfo(buffer, domain);
buffer.append(IOConstants.LINE_SEP);
/*
* not working yet anyways
*
* author: Thomas Behr 09-09-02
*/
// buffer.append(TAG_DOMAINSPELLS).append(':');
// buffer.append(EntityEncoder.encode(aDomain.getKeyName()));
// buffer.append('|');
// buffer.append(TAG_SPELLLIST).append(':');
// buffer.append('[');
// del = "";
// Collections.sort(domainSpells);
// for (Iterator it2 = domainSpells.iterator(); it2.hasNext();)
// {
// buffer.append(del);
// buffer.append(TAG_SPELL).append(':');
// buffer.append(EntityEncoder.encode((String)it2.next()));
// del = "|";
// }
// buffer.append(']');
// buffer.append(LINE_SEP);
}
}
/**
* Returns the source of the domain in the format "PObject|name[|level]"
* For example, "PCClass|Cleric|1"
* (since the level is relevant)
* For example, "Feat|Awesome Divinity" to attach a domain to a feat
*
* This method should NOT be called outside of file i/o routines
* DO NOT perform comparisons on this String
*
* @return String the source of the domain
*/
private String getDomainSourcePcgString(Domain domain)
{
final StringBuilder buff = new StringBuilder(30);
ClassSource source = thePC.getDomainSource(domain);
buff.append("PCClass");
buff.append('|');
buff.append(source.getPcclass().getKeyName());
if (source.getLevel() > 0)
{
buff.append('|');
buff.append(source.getLevel());
}
return buff.toString();
}
/**
* For each EquipSet, check for a tempBonusList and if found, save each
* bonus
*
* @param buffer
*/
private void appendEqSetBonuses(StringBuilder buffer)
{
for (EquipSet eSet : charDisplay.getEquipSet())
{
if (eSet.useTempBonusList())
{
buffer.append(IOConstants.TAG_EQSETBONUS).append(':');
buffer.append(eSet.getIdPath());
List<String> trackList = new ArrayList<>();
for (Map.Entry<BonusObj, BonusManager.TempBonusInfo> me : eSet
.getTempBonusMap().entrySet())
{
//BonusObj bObj = me.getKey();
TempBonusInfo tbi = me.getValue();
Object cObj = tbi.source;
Object tObj = tbi.target;
final String aName = tempBonusName(cObj, tObj);
if (trackList.contains(aName))
{
continue;
}
trackList.add(aName);
buffer.append('|');
buffer.append(IOConstants.TAG_TEMPBONUSBONUS).append(':');
buffer.append(EntityEncoder.encode(aName));
}
buffer.append(IOConstants.LINE_SEP);
}
}
}
private void appendEquipmentLines(StringBuilder buffer)
{
List<Equipment> eqsorted = thePC.getEquipmentMasterList();
Collections.sort(eqsorted);
for (final Equipment eq : eqsorted)
{
buffer.append(IOConstants.TAG_EQUIPNAME).append(':');
buffer.append(EntityEncoder.encode(eq.getName()));
buffer.append('|');
buffer.append(IOConstants.TAG_OUTPUTORDER).append(':');
buffer.append(eq.getOutputIndex());
buffer.append('|');
buffer.append(IOConstants.TAG_COST).append(':');
buffer.append(eq.getCost(thePC).toString());
buffer.append('|');
buffer.append(IOConstants.TAG_WT).append(':');
buffer.append(eq.getWeight(thePC).toString());
buffer.append('|');
buffer.append(IOConstants.TAG_QUANTITY).append(':');
buffer.append(eq.qty());
final String note = eq.getNote();
if (note != null)
{
buffer.append('|');
buffer.append(IOConstants.TAG_NOTE).append(':');
buffer.append(eq.getNote());
}
final String customization = eq.formatSaveLine('$', '=').trim();
final int delimiterIndex = customization.indexOf('$');
if ((!customization.isEmpty()) && (delimiterIndex >= 0))
{
buffer.append('|');
buffer.append(IOConstants.TAG_CUSTOMIZATION).append(':');
buffer.append('[');
buffer.append(IOConstants.TAG_BASEITEM).append(':');
buffer.append(EntityEncoder.encode(customization.substring(0,
delimiterIndex)));
buffer.append('|');
buffer.append(IOConstants.TAG_DATA).append(':');
buffer.append(EntityEncoder.encode(customization
.substring(delimiterIndex + 1)));
buffer.append(']');
}
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendEquipmentSetLines(StringBuilder buffer)
{
// Output all the EquipSets
final List<EquipSet> eqSetList = new ArrayList<>(charDisplay.getEquipSet());
Collections.sort(eqSetList);
for (EquipSet eqSet : eqSetList)
{
buffer.append(IOConstants.TAG_EQUIPSET).append(':');
buffer.append(EntityEncoder.encode(eqSet.getName()));
buffer.append('|');
buffer.append(IOConstants.TAG_ID).append(':');
buffer.append(eqSet.getIdPath());
if (!eqSet.getValue().isEmpty())
{
buffer.append('|');
buffer.append(IOConstants.TAG_VALUE).append(':');
buffer.append(EntityEncoder.encode(eqSet.getValue()));
buffer.append('|');
buffer.append(IOConstants.TAG_QUANTITY).append(':');
buffer.append(eqSet.getQty());
}
if (!eqSet.getNote().isEmpty())
{
buffer.append('|');
buffer.append(IOConstants.TAG_NOTE).append(':');
buffer.append(eqSet.getNote());
}
if (eqSet.getUseTempMods())
{
buffer.append('|');
buffer.append(IOConstants.TAG_USETEMPMODS).append(':');
buffer.append(eqSet.getUseTempMods() ? 'Y' : 'N');
}
buffer.append(IOConstants.LINE_SEP);
}
// Then output EquipSet used for "working" equipmentList
final String calcEquipSet = thePC.getCalcEquipSetId();
buffer.append(IOConstants.TAG_CALCEQUIPSET).append(':');
buffer.append(calcEquipSet);
buffer.append(IOConstants.LINE_SEP);
}
private void appendEyeColorLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_EYECOLOR).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.EYECOLOR)));
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Feats methods
* Only need to output pool, other FEAT info is stored in the ABILITY lines.
* ###############################################################
*/
private void appendFeatLines(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_FEATPOOL).append(':');
buffer.append(thePC.getRemainingFeatPoints(false));
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Ability methods
* ###############################################################
*/
private void appendAbilityLines(StringBuilder buffer)
{
ArrayList<AbilityCategory> categories = new ArrayList<>(
getGameMode().getAllAbilityCategories());
categories.add(AbilityCategory.LANGBONUS);
Collection<CNAbilitySelection> virtSave = thePC.getSaveAbilities();
categories.sort(new Comparator<AbilityCategory>()
{
@Override
public int compare(AbilityCategory a, AbilityCategory b)
{
return a.getKeyName().compareTo(b.getKeyName());
}
});
for (final AbilityCategory cat : categories)
{
final List<CNAbility> normalAbilitiesToSave =
new ArrayList<>(thePC.getPoolAbilities(cat, Nature.NORMAL));
// ABILITY:FEAT|NORMAL|Feat Key|APPLIEDTO:xxx|TYPE:xxx|SAVE:xxx|DESC:xxx
Collections.sort(normalAbilitiesToSave);
for (final CNAbility ability : normalAbilitiesToSave)
{
writeAbilityToBuffer(buffer, ability);
}
boolean hasVirt = false;
for (final CNAbilitySelection ability : virtSave)
{
CNAbility cnAbility = ability.getCNAbility();
if (cnAbility.getAbilityCategory().equals(cat))
{
//TODO Need to write each CNAbility only once :/
writeAbilityToBuffer(buffer, cnAbility);
hasVirt = true;
}
}
if (!normalAbilitiesToSave.isEmpty() || hasVirt
|| thePC.getUserPoolBonus(cat) != 0.0)
{
buffer.append(IOConstants.TAG_USERPOOL).append(IOConstants.TAG_END);
buffer.append(EntityEncoder.encode(cat.getKeyName())).append(
IOConstants.TAG_SEPARATOR);
buffer.append(IOConstants.TAG_POOLPOINTS).append(IOConstants.TAG_END);
buffer.append(thePC.getUserPoolBonus(cat));
buffer.append(IOConstants.LINE_SEP);
}
}
}
private void writeAbilityToBuffer(StringBuilder buffer, CNAbility cna)
{
Category<Ability> cat = cna.getAbilityCategory();
Nature nature = cna.getNature();
Ability ability = cna.getAbility();
buffer.append(IOConstants.TAG_ABILITY).append(IOConstants.TAG_END);
buffer.append(EntityEncoder.encode(cat.getKeyName())).append(
IOConstants.TAG_SEPARATOR);
buffer.append(IOConstants.TAG_TYPE).append(IOConstants.TAG_END);
buffer.append(EntityEncoder.encode(nature.toString())).append(
IOConstants.TAG_SEPARATOR);
buffer.append(IOConstants.TAG_CATEGORY).append(IOConstants.TAG_END);
buffer.append(EntityEncoder.encode(ability.getCategory()))
.append(IOConstants.TAG_SEPARATOR);
buffer.append(IOConstants.TAG_MAPKEY).append(IOConstants.TAG_END);
buffer.append(EntityEncoder.encode(ability.getKeyName()))
.append(IOConstants.TAG_SEPARATOR);
if (ability.getSafe(ObjectKey.MULTIPLE_ALLOWED))
{
buffer.append(IOConstants.TAG_APPLIEDTO).append(IOConstants.TAG_END);
List<String> assocList = thePC.getAssociationList(cna);
boolean first = true;
for (String assoc : assocList)
{
if (!first)
{
buffer.append(Constants.COMMA);
}
first = false;
buffer.append(EntityEncoder.encode(assoc));
}
buffer.append(IOConstants.TAG_SEPARATOR);
}
buffer.append(IOConstants.TAG_TYPE).append(IOConstants.TAG_END);
buffer.append(EntityEncoder.encode(ability.getType()));
for (final BonusObj save : thePC.getSaveableBonusList(ability))
{
buffer.append('|');
buffer.append(IOConstants.TAG_SAVE).append(':');
buffer.append(EntityEncoder.encode("BONUS|" + save));
}
for (final Description desc : ability
.getSafeListFor(ListKey.DESCRIPTION))
{
buffer.append(Constants.PIPE);
buffer.append(IOConstants.TAG_DESC).append(':');
buffer.append(EntityEncoder.encode(desc.getPCCText()));
}
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Follower methods
* ###############################################################
*/
private void appendFollowerLines(StringBuilder buffer)
{
final Follower aMaster = charDisplay.getMaster();
if (aMaster != null)
{
buffer.append(IOConstants.TAG_MASTER).append(':');
buffer.append(EntityEncoder.encode(aMaster.getName()));
buffer.append('|');
buffer.append(IOConstants.TAG_TYPE).append(':');
buffer.append(EntityEncoder.encode(aMaster.getType().getKeyName()));
buffer.append('|');
buffer.append(IOConstants.TAG_HITDICE).append(':');
buffer.append(aMaster.getUsedHD());
buffer.append('|');
buffer.append(IOConstants.TAG_FILE).append(':');
buffer
.append(EntityEncoder.encode(FileHelper.findRelativePath(
new File(charDisplay.getFileName()),
new File(aMaster.getFileName()))));
buffer.append('|');
buffer.append(IOConstants.TAG_ADJUSTMENT).append(':');
buffer.append(aMaster.getAdjustment());
buffer.append(IOConstants.LINE_SEP);
}
if (charDisplay.hasFollowers())
{
for (Follower follower : charDisplay.getFollowerList())
{
buffer.append(IOConstants.TAG_FOLLOWER).append(':');
buffer.append(EntityEncoder.encode(follower.getName()));
buffer.append('|');
buffer.append(IOConstants.TAG_TYPE).append(':');
buffer.append(EntityEncoder.encode(follower.getType()
.getKeyName()));
buffer.append('|');
if (follower.getRace() != null)
{
buffer.append(IOConstants.TAG_RACE).append(':');
buffer.append(EntityEncoder.encode(follower.getRace()
.getKeyName().toUpperCase()));
buffer.append('|');
}
buffer.append(IOConstants.TAG_HITDICE).append(':');
buffer.append(follower.getUsedHD());
buffer.append('|');
buffer.append(IOConstants.TAG_FILE).append(':');
if (StringUtils.isNotEmpty(follower.getFileName()))
{
buffer.append(EntityEncoder.encode(FileHelper
.findRelativePath(new File(charDisplay.getFileName()),
new File(follower.getFileName()))));
}
buffer.append(IOConstants.LINE_SEP);
}
}
}
private void appendGenderLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_GENDER).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getGenderObject().name()));
buffer.append(IOConstants.LINE_SEP);
}
private void appendHairColorLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_HAIRCOLOR).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.HAIRCOLOR)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendHairStyleLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_HAIRSTYLE).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.HAIRSTYLE)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendHandedLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_HANDED).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getHandedObject().name()));
buffer.append(IOConstants.LINE_SEP);
}
private void appendInterestsLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_INTERESTS).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.INTERESTS)));
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Kit Information methods
* ###############################################################
*/
/*
* #Kits
* KIT:KitType|Region|KitName
*
* TODO: Do we need to support the below?
* KIT:KitName|TYPE:KitType|REGION:Region
*/
private void appendKitLines(StringBuilder buffer)
{
for (final Kit kit : charDisplay.getKitInfo())
{
buffer.append(IOConstants.TAG_KIT).append(':').append(kit.getKeyName())
.append(IOConstants.LINE_SEP);
}
}
/*
* ###############################################################
* Character Language methods
* ###############################################################
*/
private void appendLanguageLine(StringBuilder buffer)
{
String del = Constants.EMPTY_STRING;
TreeSet<Language> sortedlangs = new TreeSet(charDisplay.getLanguageSet());
for (final Language lang : sortedlangs)
{
buffer.append(del);
buffer.append(IOConstants.TAG_LANGUAGE).append(':');
buffer.append(EntityEncoder.encode(lang.getKeyName()));
del = "|"; //$NON-NLS-1$
}
buffer.append(IOConstants.LINE_SEP);
}
private void appendLocationLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_LOCATION).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.LOCATION)));
buffer.append(IOConstants.LINE_SEP);
}
/**
* Convenience Method
*
* <br>author: Thomas Behr 19-03-02
*
* @param buffer
*/
private static void appendNewline(StringBuilder buffer)
{
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Notes Tab methods
* ###############################################################
*/
private void appendNotesLines(StringBuilder buffer)
{
for (NoteItem ni : charDisplay.getNotesList())
{
buffer.append(IOConstants.TAG_NOTE).append(':');
buffer.append(EntityEncoder.encode(ni.getName()));
buffer.append('|');
buffer.append(IOConstants.TAG_ID).append(':');
buffer.append(ni.getId());
buffer.append('|');
buffer.append(IOConstants.TAG_PARENTID).append(':');
buffer.append(ni.getParentId());
buffer.append('|');
buffer.append(IOConstants.TAG_VALUE).append(':');
buffer.append(EntityEncoder.encode(ni.getValue()));
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendPersonalityTrait1Line(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PERSONALITYTRAIT1).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.PERSONALITY1)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendPersonalityTrait2Line(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PERSONALITYTRAIT2).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.PERSONALITY2)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendPhobiasLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PHOBIAS).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.PHOBIAS)));
buffer.append(IOConstants.LINE_SEP);
}
//private void appendUnlimitedPoolCheckedLine(StringBuilder buffer)
//{
//buffer.append(TAG_UNLIMITEDPOOLCHECKED).append(':');
//buffer.append((SettingsHandler.isStatPoolUnlimited()) ? "Y" : "N");
//buffer.append(LINE_SEP);
//}
private void appendPoolPointsLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_POOLPOINTS).append(':');
buffer.append(thePC.getPoolAmount());
buffer.append(IOConstants.LINE_SEP);
buffer.append(IOConstants.TAG_POOLPOINTSAVAIL).append(':');
buffer.append(thePC.getPointBuyPoints());
buffer.append(IOConstants.LINE_SEP);
}
private static void appendTabLabelLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_TABLABEL).append(':');
buffer.append(SettingsHandler.getNameDisplayStyle());
buffer.append(IOConstants.LINE_SEP);
}
private void appendAutoSortLines(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_AUTOSORTGEAR).append(':');
buffer.append(thePC.isAutoSortGear() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
buffer.append(IOConstants.TAG_SKILLSOUTPUTORDER).append(':');
buffer.append(thePC.getSkillsOutputOrder().ordinal());
buffer.append(IOConstants.LINE_SEP);
}
private void appendSkillFilterLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_SKILLFILTER).append(':');
buffer.append(thePC.getSkillFilter().getValue());
buffer.append(IOConstants.LINE_SEP);
}
private void appendGearCostSizeLines(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_IGNORECOST).append(':');
buffer.append(thePC.isIgnoreCost() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
buffer.append(IOConstants.TAG_ALLOWDEBT).append(':');
buffer.append(thePC.isAllowDebt() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
buffer.append(IOConstants.TAG_AUTORESIZEGEAR).append(':');
buffer.append(thePC.isAutoResize() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
}
private void appendAutoSpellsLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_AUTOSPELLS).append(':');
buffer.append(thePC.getAutoSpells() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
}
/**
* Append the settings related to higher level slot use for spells.
* @param buffer The buffer to append to.
*/
private void appendUseHigherSpellSlotsLines(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_USEHIGHERKNOWN).append(':');
buffer.append(thePC.getUseHigherKnownSlots() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
buffer.append(IOConstants.TAG_USEHIGHERPREPPED).append(':');
buffer.append(thePC.getUseHigherPreppedSlots() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Bio methods
* ###############################################################
*/
private void appendCharacterNameLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CHARACTERNAME).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getName()));
buffer.append(IOConstants.LINE_SEP);
}
private void appendHeightLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_HEIGHT).append(':');
buffer.append(charDisplay.getHeight());
buffer.append(IOConstants.LINE_SEP);
}
private void appendLoadCompanionLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_LOADCOMPANIONS).append(':');
buffer.append(thePC.getLoadCompanion() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
}
private void appendOutputSheetsLines(StringBuilder buffer)
{
if (SettingsHandler.getSaveOutputSheetWithPC())
{
buffer.append(IOConstants.TAG_HTMLOUTPUTSHEET).append(':');
buffer.append(EntityEncoder.encode(SettingsHandler
.getSelectedCharacterHTMLOutputSheet(null)));
buffer.append(IOConstants.LINE_SEP);
buffer.append(IOConstants.TAG_PDFOUTPUTSHEET).append(':');
buffer.append(EntityEncoder.encode(SettingsHandler
.getSelectedCharacterPDFOutputSheet(null)));
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendPlayerNameLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PLAYERNAME).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getPlayersName()));
buffer.append(IOConstants.LINE_SEP);
}
private void appendPortraitLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_PORTRAIT).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getPortraitPath()));
buffer.append(IOConstants.LINE_SEP);
Rectangle rect = charDisplay.getPortraitThumbnailRect();
if (rect != null)
{
buffer.append(IOConstants.TAG_PORTRAIT_THUMBNAIL_RECT).append(':');
buffer.append(rect.x).append(',');
buffer.append(rect.y).append(',');
buffer.append(rect.width).append(',');
buffer.append(rect.height);
buffer.append(IOConstants.LINE_SEP);
}
}
/**
* @param buffer
*/
private void appendRaceLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_RACE).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getRace().getKeyName()));
List<String> assocList =
thePC.getAssociationList(charDisplay.getRace());
if (assocList != null && !assocList.isEmpty())
{
buffer.append(IOConstants.TAG_SEPARATOR);
buffer.append(IOConstants.TAG_APPLIEDTO).append(IOConstants.TAG_END);
boolean first = true;
for (String assoc : assocList)
{
if (!first)
{
buffer.append(Constants.COMMA);
}
first = false;
buffer.append(EntityEncoder.encode(assoc));
}
}
appendAddTokenInfo(buffer, charDisplay.getRace());
buffer.append(IOConstants.LINE_SEP);
}
private void appendFavoredClassLine(StringBuilder buffer)
{
PCClass sfc = thePC.getLegacyFavoredClass();
if (sfc != null)
{
buffer.append(IOConstants.TAG_FAVOREDCLASS).append(':');
buffer.append(EntityEncoder.encode(sfc.getKeyName()));
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendResidenceLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_CITY).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.RESIDENCE)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendSkinColorLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_SKINCOLOR).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.SKINCOLOR)));
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Miscellaneous methods
* ###############################################################
*/
/*
* currently source is either empty or
* PCCLASS|classname|classlevel (means it's a chosen special ability)
* PCCLASS=classname|classlevel (means it's a defined special ability)
* DEITY=deityname|totallevels
*/
private static void appendSourceInTaggedFormat(StringBuilder buffer,
String source)
{
final StringTokenizer tokens = new StringTokenizer(source, "|="); //$NON-NLS-1$
buffer.append(IOConstants.TAG_SOURCE).append(':');
buffer.append('[');
buffer.append(IOConstants.TAG_TYPE).append(':');
buffer.append(tokens.nextToken());
buffer.append('|');
buffer.append(IOConstants.TAG_NAME).append(':');
buffer.append(tokens.nextToken());
if (tokens.hasMoreTokens())
{
buffer.append('|');
buffer.append(IOConstants.TAG_LEVEL).append(':');
buffer.append(tokens.nextToken());
}
if (source.indexOf('=') >= 0)
{
buffer.append('|');
buffer.append(IOConstants.TAG_DEFINED).append(':');
buffer.append('Y');
}
buffer.append(']');
}
/*
* currently source is either empty or
* PCCLASS|classname|classlevel (means it's a chosen special ability)
* PCCLASS=classname|classlevel (means it's a defined special ability)
* DEITY=deityname|totallevels
*/
private static void appendSourceInTaggedFormat(StringBuilder buffer,
CDOMObject source)
{
buffer.append(IOConstants.TAG_SOURCE).append(':');
buffer.append('[');
buffer.append(IOConstants.TAG_TYPE).append(':');
// I love reflection :-)
final Class<? extends CDOMObject> srcClass = source.getClass();
final String pckName = srcClass.getPackage().getName();
final String srcName =
srcClass.getName().substring(pckName.length() + 1);
buffer.append(srcName.toUpperCase());
buffer.append('|');
buffer.append(IOConstants.TAG_NAME).append(':');
buffer.append(source.getKeyName());
buffer.append(']');
}
private static void appendSpecials(StringBuilder buffer,
List<String> specials, String tag_group, String tag_item, int lvl)
{
if ((specials != null) && (!specials.isEmpty()))
{
buffer.append('|');
buffer.append(tag_group).append(':');
buffer.append('[');
String del = Constants.EMPTY_STRING;
for (String special : specials)
{
buffer.append(del);
buffer.append(tag_item).append(':');
buffer.append(EntityEncoder.encode(special));
if (lvl == -1)
{
buffer.append(":-1"); //$NON-NLS-1$
}
del = "|"; //$NON-NLS-1$
}
buffer.append(']');
}
}
/*
* ###############################################################
* Character Experience methods
* ###############################################################
*/
private void appendExperienceLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_EXPERIENCE).append(':');
buffer.append(charDisplay.getXP());
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character XP table methods
* ###############################################################
*/
private void appendExperienceTableLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_EXPERIENCETABLE).append(':');
buffer.append(charDisplay.getXPTableName());
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Region method
* ###############################################################
*/
private void appendRegionLine(StringBuilder buffer)
{
final String r = charDisplay.getRegionString();
if (r != null)
{
buffer.append(IOConstants.TAG_REGION).append(':').append(r).append(IOConstants.LINE_SEP);
}
}
/*
* ###############################################################
* Character Skills methods
* ###############################################################
*/
private void appendSkillLines(StringBuilder buffer)
{
List<Skill> skillList = new ArrayList<>(charDisplay.getSkillSet());
Collections.sort(skillList);
for (Skill skill : skillList)
{
Integer outputIndex = thePC.getSkillOrder(skill);
if ((thePC.getRank(skill).doubleValue() > 0)
|| (outputIndex != null && outputIndex != 0))
{
buffer.append(IOConstants.TAG_SKILL).append(':');
buffer.append(EntityEncoder.encode(skill.getKeyName()));
buffer.append('|');
if (outputIndex != null && outputIndex != 0)
{
buffer.append(IOConstants.TAG_OUTPUTORDER).append(':');
buffer.append(outputIndex == null ? 0 : outputIndex);
buffer.append('|');
}
for (PCClass pcc : thePC.getSkillRankClasses(skill))
{
if (pcc != null)
{
Double rank = thePC.getSkillRankForClass(skill, pcc);
buffer.append(IOConstants.TAG_CLASSBOUGHT).append(':');
buffer.append('[');
buffer.append(IOConstants.TAG_CLASS).append(':');
buffer.append(EntityEncoder.encode(pcc == null ? "None"
: pcc.getKeyName()));
buffer.append('|');
buffer.append(IOConstants.TAG_RANKS).append(':');
buffer.append(rank);
buffer.append('|');
buffer.append(IOConstants.TAG_COST).append(':');
buffer.append(Integer.toString(thePC
.getSkillCostForClass(skill, pcc).getCost()));
buffer.append('|');
buffer.append(IOConstants.TAG_CLASSSKILL).append(':');
buffer.append((thePC.isClassSkill(pcc, skill)) ? 'Y'
: 'N');
buffer.append(']');
}
}
for (String assoc : thePC.getAssociationList(skill))
{
buffer.append('|');
buffer.append(IOConstants.TAG_ASSOCIATEDDATA).append(':');
buffer.append(EntityEncoder.encode(assoc));
}
appendLevelAbilityInfo(buffer, skill);
buffer.append(IOConstants.LINE_SEP);
}
}
}
private void appendSpeechPatternLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_SPEECHPATTERN).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.SPEECHTENDENCY)));
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Spell List Information methods
* ###############################################################
*/
/*
* #Spell List Information
* SPELLLIST:sourceclassname|spelllistentry|spelllistentry
*/
private void appendSpellBookLines(StringBuilder buffer)
{
for (SpellBook book : charDisplay.getSpellBooks())
{
String bookName = book.getName();
if (!bookName.equals(Globals.getDefaultSpellBook())
&& !bookName.equals(Constants.INNATE_SPELL_BOOK_NAME))
{
buffer.append(IOConstants.TAG_SPELLBOOK).append(':');
buffer.append(book.getName());
buffer.append('|');
buffer.append(IOConstants.TAG_TYPE).append(':');
buffer.append(book.getType());
if (book.getName().equals(
thePC.getSpellBookNameToAutoAddKnown()))
{
buffer.append('|');
buffer.append(IOConstants.TAG_AUTOADDKNOWN).append(':');
buffer.append('Y');
}
buffer.append(IOConstants.LINE_SEP);
}
}
}
/*
* ###############################################################
* Character Spells Information methods
* ###############################################################
*/
/*
* #Character Spells Information
* CLASS:Wizard|CANCASTPERDAY:2,4(Totals the levels all up + includes attribute bonuses)
* SPELLNAME:Blah|SCHOOL:blah|SUBSCHOOL:blah|Etc
*
* completely changed due to new Spell API
*/
private void appendSpellLines(StringBuilder buffer)
{
String del;
for (PCClass pcClass : charDisplay.getClassSet())
{
Collection<? extends CharacterSpell> sp = charDisplay.getCharacterSpells(pcClass);
List<CharacterSpell> classSpells = new ArrayList<>(sp);
// Add in the spells granted by objects
thePC.addBonusKnownSpellsToList(pcClass, classSpells);
Collections.sort(classSpells);
for (CharacterSpell cSpell : classSpells)
{
for (SpellInfo spellInfo : cSpell.getInfoList())
{
CDOMObject owner = cSpell.getOwner();
List<? extends CDOMList<Spell>> lists =
charDisplay.getSpellLists(owner);
if (SpellLevel.getFirstLevelForKey(
cSpell.getSpell(), lists, thePC) < 0)
{
Logging.errorPrint("Ignoring unqualified spell " + cSpell.getSpell() + " in list for class " + pcClass + ".");
continue;
}
if (spellInfo.getBook().equals(
Globals.getDefaultSpellBook())
&& thePC.getSpellSupport(pcClass).isAutoKnownSpell(cSpell.getSpell(), SpellLevel.getFirstLevelForKey(
cSpell.getSpell(), lists, thePC), false, thePC)
&& thePC.getAutoSpells())
{
continue;
}
buffer.append(IOConstants.TAG_SPELLNAME).append(':');
buffer.append(EntityEncoder.encode(cSpell.getSpell()
.getKeyName()));
buffer.append('|');
buffer.append(IOConstants.TAG_TIMES).append(':');
buffer.append(spellInfo.getTimes());
buffer.append('|');
buffer.append(IOConstants.TAG_CLASS).append(':');
buffer.append(EntityEncoder.encode(pcClass.getKeyName()));
buffer.append('|');
buffer.append(IOConstants.TAG_SPELL_BOOK).append(':');
buffer.append(EntityEncoder.encode(spellInfo.getBook()));
buffer.append('|');
buffer.append(IOConstants.TAG_SPELLLEVEL).append(':');
buffer.append(spellInfo.getActualLevel());
if (spellInfo.getNumPages() > 0)
{
buffer.append('|');
buffer.append(IOConstants.TAG_SPELLNUMPAGES).append(':');
buffer.append(spellInfo.getNumPages());
}
final List<Ability> metaFeats = spellInfo.getFeatList();
if ((metaFeats != null) && (!metaFeats.isEmpty()))
{
buffer.append('|');
buffer.append(IOConstants.TAG_FEATLIST).append(':');
buffer.append('[');
del = Constants.EMPTY_STRING;
for (Ability feat : metaFeats)
{
buffer.append(del);
buffer.append(IOConstants.TAG_FEAT).append(':');
buffer.append(EntityEncoder.encode(feat
.getKeyName()));
del = "|"; //$NON-NLS-1$
}
buffer.append(']');
}
buffer.append('|');
appendSourceInTaggedFormat(buffer,
StringPClassUtil.getStringFor(owner.getClass()) + "|"
+ owner.getKeyName());
buffer.append(IOConstants.LINE_SEP);
}
}
}
}
/*
* ###############################################################
* Spell List Information methods
* ###############################################################
*/
/*
* #Spell List Information
* SPELLLIST:sourceclassname|spelllistentry|spelllistentry
*/
private void appendSpellListLines(StringBuilder buffer)
{
for (PCClass pcClass : charDisplay.getClassSet())
{
TransitionChoice<CDOMListObject<Spell>> csc =
pcClass.get(ObjectKey.SPELLLIST_CHOICE);
if (csc != null)
{
List<? extends CDOMList<Spell>> assocList =
charDisplay.getSpellLists(pcClass);
buffer.append(IOConstants.TAG_SPELLLIST).append(':');
buffer.append(pcClass.getKeyName());
for (CDOMList<Spell> spell : assocList)
{
buffer.append('|');
if (ClassSpellList.class.equals(spell.getClass()))
{
buffer.append("CLASS");
}
else
{
buffer.append("DOMAIN");
}
buffer.append('.').append(spell.getLSTformat());
}
buffer.append(IOConstants.LINE_SEP);
}
}
}
/*
* ###############################################################
* Character Attributes methods
* ###############################################################
*/
private void appendStatLines(StringBuilder buffer)
{
for (PCStat aStat : charDisplay.getStatSet())
{
buffer.append(IOConstants.TAG_STAT).append(':');
buffer.append(aStat.getKeyName());
buffer.append('|');
buffer.append(IOConstants.TAG_SCORE).append(':');
buffer.append(charDisplay.getStat(aStat));
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendTabNameLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_TABNAME).append(':');
buffer.append(EntityEncoder.encode(charDisplay.getSafeStringFor(PCStringKey.TABNAME)));
buffer.append(IOConstants.LINE_SEP);
}
private void appendTempBonuses(StringBuilder buffer)
{
final List<String> trackList = new ArrayList<>();
TreeSet<Map.Entry<BonusObj, BonusManager.TempBonusInfo>> sortedbonus = new TreeSet<>(
new Comparator<Map.Entry<BonusObj, BonusManager.TempBonusInfo>>() {
@Override
public int compare(Map.Entry<BonusObj, BonusManager.TempBonusInfo> a, Map.Entry<BonusObj, BonusManager.TempBonusInfo> b) {
BonusObj keyA = a.getKey();
BonusObj keyB = b.getKey();
if (!keyA.getBonusName().equals(keyB.getBonusName())) {
return keyA.getBonusName().compareTo(keyB.getBonusName());
}
if (!keyA.getBonusInfo().equals(keyB.getBonusInfo())) {
return keyA.getBonusInfo().compareTo(keyB.getBonusInfo());
}
return keyA.getPCCText().compareTo(keyB.getPCCText());
}
});
sortedbonus.addAll(thePC.getTempBonusMap().entrySet());
//for (BonusManager.TempBonusInfo tbi : thePC.getTempBonusMap().values())
for (Map.Entry<BonusObj, BonusManager.TempBonusInfo> me : sortedbonus)
{
BonusObj bonus = me.getKey();
TempBonusInfo tbi = me.getValue();
Object creObj = tbi.source;
Object tarObj = tbi.target;
final String outString = tempBonusName(creObj, tarObj);
if (trackList.contains(outString))
{
continue;
}
trackList.add(outString);
buffer.append(outString);
String bonusName = BonusDisplay.getBonusDisplayName(tbi);
if (thePC.getTempBonusFilters().contains(bonusName))
{
buffer.append('|');
buffer.append(IOConstants.TAG_TEMPBONUSACTIVE).append(":N");
}
/*
* Why do we loop through the bonuses again? It is looped through
* again so that only items associated with this source (e.g.
* Template and Target object) are written, but that ALL of the
* items are written on one line.
*/
for (Map.Entry<BonusObj, BonusManager.TempBonusInfo> subme : sortedbonus)
{
BonusObj subBonus = subme.getKey();
TempBonusInfo subtbi = subme.getValue();
Object cObj = subtbi.source;
Object tObj = subtbi.target;
final String inString = tempBonusName(cObj, tObj);
if (inString.equals(outString))
{
buffer.append('|');
buffer.append(IOConstants.TAG_TEMPBONUSBONUS).append(':');
buffer.append(EntityEncoder.encode(subBonus.getPCCText()));
}
}
buffer.append(IOConstants.LINE_SEP);
}
}
/*
* ###############################################################
* Character Templates methods
* ###############################################################
*/
private void appendTemplateLines(StringBuilder buffer)
{
for (PCTemplate template : charDisplay.getTemplateSet())
{
//
// TEMPLATESAPPLIED:[NAME:<template_name>]
// TEMPLATESAPPLIED:[NAME:<template_name>|CHOSENFEAT:[KEY:<key>|VALUE:<value>]CHOSENFEAT:[KEY:<key>|VALUE:<value>]...CHOSENFEAT:[KEY:<key>|VALUE:<value>]]
//
buffer.append(IOConstants.TAG_TEMPLATESAPPLIED).append(':').append('[');
buffer.append(IOConstants.TAG_NAME).append(':').append(
EntityEncoder.encode(template.getKeyName()));
final String chosenFeats = chosenFeats(template);
if (!chosenFeats.isEmpty())
{
buffer.append('|').append(chosenFeats);
}
//
// Save list of template names 'owned' by current template
//
Collection<PCTemplate> templatesAdded =
thePC.getTemplatesAdded(template);
if (templatesAdded != null)
{
for (PCTemplate ownedTemplate : templatesAdded)
{
buffer.append('|').append(IOConstants.TAG_CHOSENTEMPLATE).append(':')
.append('[');
buffer.append(IOConstants.TAG_NAME).append(':').append(
EntityEncoder.encode(ownedTemplate.getKeyName()));
buffer.append(']');
}
}
List<String> assocList = thePC.getAssociationList(template);
if (assocList != null && !assocList.isEmpty())
{
buffer.append(IOConstants.TAG_SEPARATOR);
buffer.append(IOConstants.TAG_APPLIEDTO).append(IOConstants.TAG_END);
boolean first = true;
for (String assoc : assocList)
{
if (!first)
{
buffer.append(Constants.COMMA);
}
first = false;
buffer.append(EntityEncoder.encode(assoc));
}
}
buffer.append(']');
appendAddTokenInfo(buffer, template);
buffer.append(IOConstants.LINE_SEP);
}
}
private void appendUseTempModsLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_USETEMPMODS).append(':');
buffer.append(thePC.getUseTempMods() ? 'Y' : 'N');
buffer.append(IOConstants.LINE_SEP);
}
/*
* ###############################################################
* Character Weapon proficiencies methods
* ###############################################################
*/
private void appendWeaponProficiencyLines(StringBuilder buffer)
{
final int size = charDisplay.getWeaponProfSet().size();
if (size > 0)
{
/*
* since aPC.getWeaponProfList() returns a TreeSet,
* we have to put them into an array first.
* we do not use TreeSet's toArray()-method since it
* makes no guarantees on element order.
*
* author: Thomas Behr 08-09-02
*/
final String[] weaponProficiencies = new String[size];
int j = 0;
for (WeaponProf wp : charDisplay.getSortedWeaponProfs())
{
weaponProficiencies[j++] = wp.getKeyName();
}
// as per Mynex's request do not write more than 10 weapons per line
final int step = 10;
final int times = (size / step) + (((size % step) > 0) ? 1 : 0);
for (int k = 0; k < times; ++k)
{
buffer.append(IOConstants.TAG_WEAPONPROF).append(':');
buffer.append('[');
String del = Constants.EMPTY_STRING;
int stop = Math.min(size, (k * step) + 10);
for (int i = k * step; i < stop; ++i)
{
buffer.append(del);
buffer.append(IOConstants.TAG_WEAPON).append(':');
buffer.append(EntityEncoder.encode(weaponProficiencies[i]));
del = "|"; //$NON-NLS-1$
}
buffer.append(']');
buffer.append(IOConstants.LINE_SEP);
}
}
//
// Save any selected racial bonus weapons
//
appendWeaponProficiencyLines(buffer, charDisplay.getRace());
//
// Save any selected template bonus weapons
//
for (PCTemplate pct : charDisplay.getTemplateSet())
{
appendWeaponProficiencyLines(buffer, pct);
}
//
// Save any selected class bonus weapons
//
for (final PCClass pcClass : charDisplay.getClassSet())
{
appendWeaponProficiencyLines(buffer, pcClass);
}
}
private void appendWeaponProficiencyLines(StringBuilder buffer,
CDOMObject source)
{
if (source == null)
{
return;
}
final List<? extends WeaponProf> profs = thePC.getBonusWeaponProfs(source);
if (profs == null || profs.isEmpty())
{
return;
}
// TODO refactor this section and the code above that calls it so share this
// As per Mynex's request do not write more than 10 weapons per line
final int step = 10;
final int times = (profs.size() / step) + 1;
for (int k = 0; k < times; ++k)
{
buffer.append(IOConstants.TAG_WEAPONPROF).append(':');
buffer.append('[');
String del = Constants.EMPTY_STRING;
int stop = Math.min(profs.size(), (k * step) + 10);
for (int i = k * step; i < stop; ++i)
{
buffer.append(del);
buffer.append(IOConstants.TAG_WEAPON).append(':');
buffer.append(EntityEncoder.encode(profs.get(i).getLSTformat()));
del = "|"; //$NON-NLS-1$
}
buffer.append(']');
buffer.append('|');
appendSourceInTaggedFormat(buffer, source);
buffer.append(IOConstants.LINE_SEP);
}
}
/*
* ###############################################################
* Character Equipment methods
* ###############################################################
*/
private void appendMoneyLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_MONEY).append(':');
buffer.append(thePC.getGold().toString());
buffer.append(IOConstants.LINE_SEP);
}
private void appendWeightLine(StringBuilder buffer)
{
buffer.append(IOConstants.TAG_WEIGHT).append(':');
buffer.append(charDisplay.getWeight());
buffer.append(IOConstants.LINE_SEP);
}
private String chosenFeats(PCTemplate pct)
{
final StringBuilder aString = new StringBuilder(50);
for (PCTemplate rlt : pct.getSafeListFor(ListKey.REPEATLEVEL_TEMPLATES))
{
for (PCTemplate lt : rlt.getSafeListFor(ListKey.LEVEL_TEMPLATES))
{
List<? extends CNAbilitySelection> featList = thePC
.getTemplateFeatList(lt);
if (featList != null)
{
writeTemplateFeat(aString, lt, featList);
}
}
}
for (PCTemplate lt : pct.getSafeListFor(ListKey.LEVEL_TEMPLATES))
{
List<? extends CNAbilitySelection> featList = thePC
.getTemplateFeatList(lt);
if (featList != null)
{
writeTemplateFeat(aString, lt, featList);
}
}
for (PCTemplate lt : pct.getSafeListFor(ListKey.HD_TEMPLATES))
{
List<? extends CNAbilitySelection> featList = thePC
.getTemplateFeatList(lt);
if (featList != null)
{
writeTemplateFeat(aString, lt, featList);
}
}
return aString.toString();
}
private void writeTemplateFeat(StringBuilder aString, PCTemplate pct,
List<? extends CNAbilitySelection> featList)
{
for (CNAbilitySelection s : featList)
{
if (aString.length() != 0)
{
aString.append('|');
}
String featKey = Compatibility.getKeyFor(pct);
aString.append(IOConstants.TAG_CHOSENFEAT).append(':');
aString.append('[');
aString.append(IOConstants.TAG_MAPKEY).append(':').append(
EntityEncoder.encode(featKey)).append('|');
aString.append(IOConstants.TAG_MAPVALUE).append(':').append(
EntityEncoder.encode(s.getPersistentFormat()));
aString.append(']');
}
}
/**
* Convenience Method
*
* <br>author: Thomas Behr 19-03-02
*
* @param s the String which will be converted into a comment;
* i.e. '#','\r' will be removed,
* '\t','\f' will be replaced with ' ',
* and each line will start with "# "
* @return the newly created comment
*/
private static String createComment(String s)
{
String work = s + IOConstants.LINE_SEP;
work = work.replace('\t', ' ');
work = work.replace('\f', ' ');
StringBuilder buffer = new StringBuilder(work.length() + 100);
StringTokenizer tokens = new StringTokenizer(work, "#"); //$NON-NLS-1$
while (tokens.hasMoreTokens())
{
buffer.append(tokens.nextToken());
}
work = buffer.toString();
buffer.setLength(0);
/*
* Need to keep the Windows line separator as newline delimiter to ensure
* cross-platform portability.
*
* author: Thomas Behr 2002-11-13
*/
tokens = new StringTokenizer(work, "\r\n"); //$NON-NLS-1$
while (tokens.hasMoreTokens())
{
buffer.append("# ").append(tokens.nextToken()).append(IOConstants.LINE_SEP); //$NON-NLS-1$
}
return buffer.toString();
}
/**
* creates a unique tuple based on the creator and target getName()
* @param creator
* @param target
* @return temp bonus name
**/
private String tempBonusName(final Object creator, Object target)
{
final StringBuilder cb = new StringBuilder(100);
cb.append(IOConstants.TAG_TEMPBONUS).append(':');
if (creator instanceof CDOMObject)
{
final CDOMObject oCreator = (CDOMObject) creator;
if (oCreator instanceof Ability)
{
cb.append(IOConstants.TAG_FEAT).append('=');
}
else if (oCreator instanceof Spell)
{
cb.append(IOConstants.TAG_SPELL).append('=');
}
else if (oCreator instanceof Equipment)
{
cb.append(IOConstants.TAG_EQUIPMENT).append('=');
}
else if (oCreator instanceof PCClass)
{
cb.append(IOConstants.TAG_CLASS).append('=');
}
else if (oCreator instanceof PCTemplate)
{
cb.append(IOConstants.TAG_TEMPLATE).append('=');
}
else if (oCreator instanceof Skill)
{
cb.append(IOConstants.TAG_SKILL).append('=');
}
else
{
cb.append(IOConstants.TAG_ERROR).append('=');
}
cb.append(EntityEncoder.encode(oCreator.getKeyName()));
// Hmm, need to get the Type of oCreater also?
// Might be required so the PCGVer2Parser can search correct type to re-create
}
else
{
return Constants.EMPTY_STRING;
}
cb.append('|');
cb.append(IOConstants.TAG_TEMPBONUSTARGET).append(':');
if (target instanceof PlayerCharacter)
{
cb.append(IOConstants.TAG_PC);
}
else if (target instanceof Equipment)
{
cb.append(EntityEncoder.encode(((Equipment) target).getName()));
}
return cb.toString();
}
//
// Remember what choices were made for each of the ADD: tags
//
private void appendLevelAbilityInfo(StringBuilder buffer, CDOMObject pObj)
{
appendAddTokenInfo(buffer, pObj);
}
private void appendAddTokenInfo(StringBuilder buffer, CDOMObject pObj)
{
List<PersistentTransitionChoice<?>> addList =
pObj.getListFor(ListKey.ADD);
if (addList == null)
{
return;
}
for (PersistentTransitionChoice<?> tc : addList)
{
addChoices(buffer, tc);
}
}
private <T> void addChoices(StringBuilder buffer,
PersistentTransitionChoice<T> tc)
{
List<Object> assocList = thePC.getAssocList(tc, AssociationListKey.ADD);
if (assocList == null)
{
return;
}
//
// |ADD:[PROMPT:SUBTOKEN|blah|CHOICE:choice1|CHOICE:choice2|CHOICE:choice3...]
//
SelectableSet<?> choices = tc.getChoices();
buffer.append('|').append(IOConstants.TAG_ADDTOKEN).append(':').append('[');
buffer.append(EntityEncoder.encode(choices.getName())).append(':');
buffer.append(EntityEncoder.encode(choices.getLSTformat()));
for (Object assoc : assocList)
{
buffer.append('|').append(IOConstants.TAG_CHOICE).append(':')
.append(EntityEncoder.encode(tc.encodeChoice(tc
.castChoice(assoc))));
}
buffer.append(']');
}
}