/*
* SpellMemToken.java
* Copyright 2004 (C) James Dempsey <jdempsey@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Created on Jul 16, 2004
*
* $Id$
*
*/
package plugin.exporttokens;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import pcgen.base.util.HashMapToList;
import pcgen.cdom.base.CDOMList;
import pcgen.cdom.base.Constants;
import pcgen.cdom.enumeration.FactKey;
import pcgen.cdom.enumeration.IntegerKey;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.enumeration.SourceFormat;
import pcgen.cdom.enumeration.StringKey;
import pcgen.core.Ability;
import pcgen.core.Globals;
import pcgen.core.PCClass;
import pcgen.core.PObject;
import pcgen.core.PlayerCharacter;
import pcgen.core.SettingsHandler;
import pcgen.core.analysis.OutputNameFormatting;
import pcgen.core.character.CharacterSpell;
import pcgen.core.character.SpellInfo;
import pcgen.core.spell.Spell;
import pcgen.io.ExportHandler;
import pcgen.io.exporttoken.Token;
import pcgen.util.Delta;
/**
* {@code SpellMemToken} displays information about the spells
* in the character spellbooks..
*
*
* @author James Dempsey <jdempsey@users.sourceforge.net>
*/
// SPELLMEM.x.x.x.x.LABEL classNum.bookNum.level.spellnumber
// LABEL is TIMES,NAME,RANGE,etc. if not supplied it defaults to NAME
public class SpellMemToken extends Token
{
/** token name */
public static final String TOKENNAME = "SPELLMEM";
/**
* @see pcgen.io.exporttoken.Token#getTokenName()
*/
@Override
public String getTokenName()
{
return TOKENNAME;
}
/**
* @see pcgen.io.exporttoken.Token#getToken(java.lang.String, pcgen.core.PlayerCharacter, pcgen.io.ExportHandler)
*/
@Override
public String getToken(String tokenSource, PlayerCharacter aPC,
ExportHandler eh)
{
StringBuilder retValue = new StringBuilder();
// New Token syntax - SPELLMEM.x instead of SPELLMEMx
final StringTokenizer aTok = new StringTokenizer(tokenSource, ".");
aTok.nextToken();
final int classNum;
classNum = Integer.parseInt(aTok.nextToken());
final int bookNum = Integer.parseInt(aTok.nextToken());
final int spellLevel = Integer.parseInt(aTok.nextToken());
final int spellNumber = Integer.parseInt(aTok.nextToken());
boolean found = false;
String aLabel = "NAME";
if (aTok.hasMoreTokens())
{
aLabel = aTok.nextToken();
}
String altLabel = "";
if (aTok.hasMoreTokens())
{
altLabel = aTok.nextToken();
}
final PObject aObject = aPC.getSpellClassAtIndex(classNum);
if ((aObject == null) && eh != null && eh.getExistsOnly()
&& (classNum != -1))
{
eh.setNoMoreItems(true);
}
String bookName = Globals.getDefaultSpellBook();
if (bookNum > 0)
{
bookName = aPC.getDisplay().getSpellBookNames().get(bookNum);
}
if ((aObject != null) || (classNum == -1))
{
if (classNum == -1)
{
bookName = Globals.getDefaultSpellBook();
}
CharacterSpell selectedCSpell = null;
if (!"".equals(bookName))
{
Spell aSpell = null;
if (classNum == -1)
{
// List of all the character's spells (including SLAs)
final List<CharacterSpell> charSpellList =
new ArrayList<>();
// For each class
for (PCClass pcClass : aPC.getDisplay().getClassSet())
{
// Get the spells provided by the class
List<CharacterSpell> aList =
aPC.getCharacterSpells(
pcClass, null, bookName, spellLevel);
for (CharacterSpell cs : aList)
{
// Add to the list if they are not there already
if (!charSpellList.contains(cs))
{
charSpellList.add(cs);
}
}
}
// Sort the list
Collections.sort(charSpellList);
// Set cs to the spell asked for
if (spellNumber < charSpellList.size())
{
selectedCSpell = charSpellList.get(spellNumber);
aSpell = selectedCSpell.getSpell();
found = true;
}
}
else if (aObject != null)
{
// List of spells provided by this PObject
final List<CharacterSpell> charSpells =
aPC.getCharacterSpells(aObject, null, bookName, spellLevel);
if (spellNumber < charSpells.size())
{
selectedCSpell = charSpells.get(spellNumber);
aSpell = selectedCSpell.getSpell();
found = true;
}
}
else if (eh != null && eh.getInLabel() && eh.getCheckBefore())
{
eh.setCanWrite(false);
}
// We never found the requested spell
if (selectedCSpell == null)
{
if (eh != null && eh.getExistsOnly())
{
eh.setNoMoreItems(true);
}
return retValue.toString();
}
// Get the SpellInfo for the selected spell
final SpellInfo si =
selectedCSpell.getSpellInfoFor(bookName, spellLevel);
if (found && (aSpell != null) && (si != null))
{
if ("NAME".equals(aLabel) || "OUTPUTNAME".equals(aLabel))
{
retValue.append(OutputNameFormatting.getOutputName(aSpell) + si.toString());
}
else if ("BASENAME".equals(aLabel))
{
retValue.append(OutputNameFormatting.getOutputName(aSpell));
}
else if ("APPLIEDNAME".equals(aLabel))
{
retValue.append(getAppliedName(si));
}
else if ("PPCOST".equals(aLabel))
{
if (si.getActualPPCost() != -1)
{
retValue.append(si.getActualPPCost());
}
}
else if ("TIMES".equals(aLabel))
{
if (si.getTimes() == SpellInfo.TIMES_AT_WILL)
{
retValue.append("At Will");
}
else
{
retValue.append(String.valueOf(si.getTimes()));
}
}
else if ("TIMEUNIT".equals(aLabel))
{
retValue.append(String.valueOf(si.getTimeUnit()));
}
else
// if (aSpell != null) can't be null
{
if ("RANGE".equals(aLabel))
{
retValue.append(aPC.getSpellRange(selectedCSpell, si));
}
else if ("CASTERLEVEL".equals(aLabel))
{
retValue.append(aPC.getCasterLevelForSpell(selectedCSpell));
}
else if ("CASTINGTIME".equals(aLabel))
{
retValue.append(aSpell.getListAsString(ListKey.CASTTIME));
}
else if ("COMPONENTS".equals(aLabel))
{
retValue.append(aSpell.getListAsString(ListKey.COMPONENTS));
}
else if ("CONCENTRATION".equals(aLabel))
{
if (SettingsHandler.getGame().getSpellBaseConcentration() != "")
{
int concentration = aPC.getConcentration(aSpell, selectedCSpell, si);
retValue.append(Delta.toString(concentration));
}
}
else if ("COST".equals(aLabel))
{
retValue.append(aSpell.getSafe(ObjectKey.COST).toString());
}
else if ("DC".equals(aLabel))
{
String SaveInfo = aSpell.getListAsString(ListKey.SAVE_INFO);
if (!"".equals(SaveInfo) && !"None".equals(SaveInfo) && !"No".equals(SaveInfo))
{
int dc = aPC.getDC(aSpell, selectedCSpell, si);
retValue.append(String.valueOf(dc));
}
}
else if ("DURATION".equals(aLabel))
{
String mString =
aPC.parseSpellString(selectedCSpell, aSpell.getListAsString(ListKey.DURATION));
retValue.append(mString);
}
else if ("DESC".equals(aLabel)
|| "EFFECT".equals(aLabel))
{
String mString =
aPC.parseSpellString(selectedCSpell, aPC
.getDescription(aSpell));
retValue.append(mString);
}
else if ("TARGET".equals(aLabel)
|| "EFFECTYPE".equals(aLabel))
{
String mString =
aPC
.parseSpellString(selectedCSpell, aSpell.getSafe(StringKey.TARGET_AREA));
retValue.append(mString);
}
else if ("SAVEINFO".equals(aLabel))
{
retValue.append(aSpell.getListAsString(ListKey.SAVE_INFO));
}
else if ("SCHOOL".equals(aLabel))
{
retValue.append(aSpell.getListAsString(ListKey.SPELL_SCHOOL));
}
else if ("SOURCELEVEL".equals(aLabel))
{
retValue.append(replaceTokenSpellMemSourceLevel(
aSpell, aPC));
}
else if ("SOURCE".equals(aLabel))
{
retValue.append(SourceFormat.getFormattedString(aSpell,
Globals.getSourceDisplay(), true));
}
else if ("SOURCESHORT".equals(aLabel))
{
retValue.append(SourceFormat.formatShort(aSpell, 8));
}
else if ("SOURCEPAGE".equals(aLabel))
{
retValue.append(aSpell.get(StringKey.SOURCE_PAGE));
}
else if ("SOURCEWEB".equals(aLabel))
{
String aTemp = aSpell.get(StringKey.SOURCE_WEB);
if (aTemp != null && !aTemp.isEmpty()) {
retValue.append(aTemp);
}
}
else if ("SOURCELINK".equals(aLabel))
{
String aTemp = aSpell.get(StringKey.SOURCE_LINK);
if (aTemp != null && !aTemp.isEmpty()) {
retValue.append(aTemp);
}
}
else if ("SUBSCHOOL".equals(aLabel))
{
retValue.append(aSpell.getListAsString(ListKey.SPELL_SUBSCHOOL));
}
else if ("DESCRIPTOR".equals(aLabel))
{
retValue.append(aSpell.getListAsString(ListKey.SPELL_DESCRIPTOR));
}
else if ("FULLSCHOOL".equals(aLabel))
{
String aTemp = aSpell.getListAsString(ListKey.SPELL_SCHOOL);
if ((!aSpell.getListAsString(ListKey.SPELL_SUBSCHOOL).isEmpty())
&& (!"NONE".equalsIgnoreCase(aSpell.getListAsString(ListKey.SPELL_SUBSCHOOL).trim())))
{
aTemp += (" (" + aSpell.getListAsString(ListKey.SPELL_SUBSCHOOL) + ')');
}
if (!aSpell.getListAsString(ListKey.SPELL_DESCRIPTOR).isEmpty())
{
aTemp += (" [" + aSpell.getListAsString(ListKey.SPELL_DESCRIPTOR) + ']');
}
retValue.append(aTemp);
}
else if ("SR".equals(aLabel))
{
retValue.append(aSpell.getListAsString(ListKey.SPELL_RESISTANCE));
}
else if ("SRSHORT".equals(aLabel))
{
if ("No".equals(aSpell.getListAsString(ListKey.SPELL_RESISTANCE)))
{
retValue.append("N");
}
else
{
retValue.append("Y");
}
}
else if ("CLASS".equals(aLabel))
{
retValue.append(OutputNameFormatting.getOutputName(aObject));
}
else if ("DCSTAT".equals(aLabel))
{
if (aObject instanceof PCClass)
{
PCClass aClass = (PCClass) aObject;
retValue.append(aClass.getSpellBaseStat());
}
}
else if ("TYPE".equals(aLabel))
{
PCClass aClass = null;
if (aObject instanceof PCClass)
{
aClass = (PCClass) aObject;
}
if (aClass != null)
{
retValue.append(aClass.getSpellType());
}
}
else if (aLabel.startsWith("DESCRIPTION"))
{
final String sString =
getItemDescription("SPELL",
aSpell.getKeyName(), aPC
.getDescription(aSpell), aPC);
if (!altLabel.isEmpty())
{
retValue.append(sString.replaceAll("\r?\n", altLabel));
}
else
{
retValue.append(sString);
}
}
else if (aLabel.startsWith("BONUSSPELL"))
{
String sString = "*";
if (aLabel.length() > 10)
{
sString = aLabel.substring(10);
}
retValue.append(getBonusSpellValue(aPC, spellLevel,
sString, altLabel, aObject, bookName,
selectedCSpell, aSpell));
}
else if ("XPCOST".equals(aLabel))
{
retValue.append(aSpell.getSafe(IntegerKey.XP_COST));
}
else if ("CT".equals(aLabel))
{
retValue.append(aSpell.getSafe(IntegerKey.CASTING_THRESHOLD));
}
}
}
else if (eh != null && eh.getExistsOnly())
{
eh.setNoMoreItems(true);
}
}
else if (eh != null && eh.getExistsOnly())
{
eh.setNoMoreItems(true);
}
}
return retValue.toString();
}
private String getAppliedName(final SpellInfo si)
{
List<Ability> featList = si.getFeatList();
if (featList == null || featList.isEmpty())
{
return "";
}
final StringBuilder aBuf = new StringBuilder(50);
for (int i = 0; i < featList.size(); i++)
{
Object an =
featList.get(i).getResolved(FactKey.valueOf("AppliedName"));
aBuf.append((an == null) ? "" : an);
if (i < featList.size())
{
aBuf.append(" ");
}
}
return aBuf.toString();
}
/**
* Display an * is the spell is a domain/specialty spell,
* display ** if it is ONLY a domain/specialty spell. A value
* may also be supplied that will be displayed for non-specialty spells.
*
* @param aPC The character being processed.
* @param spellLevel The level of the spell.
* @param sString The indicator to use for domain/specialty spells.
* @param altLabel The indicator to use for non domain/specialty spells.
* @param aObject The class containing the spell.
* @param bookName The name of the spell book.
* @param cs The spell as it applies to the character
* @param aSpell The generic spell.
* @return The annotation string indicating domain/specialty status
*/
private String getBonusSpellValue(PlayerCharacter aPC,
final int spellLevel, String sString, String altLabel,
final PObject aObject, String bookName, CharacterSpell cs, Spell aSpell)
{
StringBuilder retValue = new StringBuilder();
if ((aObject != null) && (cs != null) && cs.isSpecialtySpell(aPC)
&& (aObject instanceof PCClass))
{
final List<CharacterSpell> charSpells =
aPC.getCharacterSpells(aObject, aSpell, bookName, spellLevel);
boolean isDomainOnly = true;
for (CharacterSpell cSpell : charSpells)
{
if (!cSpell.isSpecialtySpell(aPC))
{
isDomainOnly = false;
break;
}
}
if (isDomainOnly)
{
retValue.append(sString);
}
else
{
retValue.append(sString + sString);
}
}
else
{
retValue.append(altLabel);
}
return retValue.toString();
}
private static String replaceTokenSpellMemSourceLevel(Spell aSpell,
PlayerCharacter aPC)
{
final HashMapToList<CDOMList<Spell>, Integer> tempHash = aPC.getSpellLevelInfo(aSpell);
StringBuilder tempSource = new StringBuilder();
final Set<String> levelSet = new TreeSet<>();
for (CDOMList<Spell> spellList : tempHash.getKeySet())
{
String classKey = spellList.getKeyName();
for (Integer lvl : tempHash.getListFor(spellList))
{
PCClass pcc = Globals.getContext().getReferenceContext()
.silentlyGetConstructedCDOMObject(PCClass.class,
classKey);
if (pcc != null)
{
classKey = pcc.getAbbrev();
}
levelSet.add(classKey + lvl.toString());
}
}
for (String levelString : levelSet)
{
if (tempSource.length() > 0)
{
tempSource.append(", ");
}
tempSource.append(levelString);
}
return tempSource.toString();
}
/**
* Get the item description
* @param sType
* @param sKey
* @param sAlt
* @param aPC
* @return item description
*/
public static String getItemDescription(
String sType,
String sKey,
String sAlt,
PlayerCharacter aPC)
{
if (SettingsHandler.isROG())
{
if ("EMPTY".equals(aPC.getDescriptionLst()))
{
aPC.loadDescriptionFilesInDirectory("descriptions");
}
String aDescription = sAlt;
final String aSearch =
sType.toUpperCase() + ":" + sKey + Constants.LINE_SEPARATOR;
final int pos = aPC.getDescriptionLst().indexOf(aSearch);
if (pos >= 0)
{
aDescription =
aPC.getDescriptionLst().substring(
pos + aSearch.length());
aDescription =
aDescription.substring(0,
aDescription.indexOf("####") - 1).trim();
}
return aDescription;
}
return sAlt;
}
}