/* * KitSpells.java * Copyright 2001 (C) Greg Bingleman <byngl@hotmail.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on September 23, 2002, 9:29 PM * * $Id$ */ package pcgen.core.kit; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import pcgen.base.formula.Formula; import pcgen.base.util.DoubleKeyMap; import pcgen.cdom.base.CDOMList; import pcgen.cdom.content.KnownSpellIdentifier; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.reference.CDOMSingleRef; import pcgen.cdom.reference.ReferenceUtilities; import pcgen.core.Ability; import pcgen.core.Domain; import pcgen.core.Globals; import pcgen.core.Kit; import pcgen.core.PCClass; import pcgen.core.PObject; import pcgen.core.PlayerCharacter; import pcgen.core.analysis.SpellLevel; import pcgen.core.character.CharacterSpell; import pcgen.core.spell.Spell; import pcgen.util.Logging; /** * {@code KitSpells}. * * @author Greg Bingleman <byngl@hotmail.com> */ public final class KitSpells extends BaseKit { private String spellBook; private CDOMSingleRef<PCClass> castingClass; DoubleKeyMap<KnownSpellIdentifier, List<CDOMSingleRef<Ability>>, Integer> spells = new DoubleKeyMap<>(); private Formula countFormula; private transient List<KitSpellBookEntry> theSpells = null; /** * @param formula the count formula to set */ public void setCount(Formula formula) { countFormula = formula; } /** * @return the count formula */ public Formula getCount() { return countFormula; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (castingClass != null) { sb.append(castingClass.getLSTformat(false)); } sb.append(' ').append(spellBook).append(": "); boolean needComma = false; for (KnownSpellIdentifier ksi : spells.getKeySet()) { if (needComma) { sb.append(','); } needComma = true; sb.append(ksi.getLSTformat()); Set<List<CDOMSingleRef<Ability>>> abilities = spells.getSecondaryKeySet(ksi); for (List<CDOMSingleRef<Ability>> list : abilities) { if (list != null) { sb.append(" ["); sb.append(ReferenceUtilities.joinLstFormat(list, ",")); sb.append(']'); } Integer count = spells.get(ksi, list); if (count > 1) { sb.append(" (").append(count).append(")"); } } } return sb.toString(); } @Override public boolean testApply(Kit aKit, PlayerCharacter aPC, List<String> warnings) { theSpells = null; PCClass aClass = findDefaultSpellClass(castingClass, aPC); if (aClass == null) { warnings.add("SPELLS: Character does not have " + castingClass + " spellcasting class."); return false; } String workingBook = spellBook == null ? Globals.getDefaultSpellBook() : spellBook; List<KitSpellBookEntry> aSpellList = new ArrayList<>(); if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !workingBook.equals(Globals.getDefaultSpellBook())) { warnings.add("SPELLS: " + aClass.getDisplayName() + " can only add to " + Globals.getDefaultSpellBook()); return false; } for (KnownSpellIdentifier ksi : spells.getKeySet()) { Collection<Spell> allSpells = ksi.getContainedSpells(aPC, Collections.singletonList(aClass .get(ObjectKey.CLASS_SPELLLIST))); Set<List<CDOMSingleRef<Ability>>> feats = spells.getSecondaryKeySet(ksi); for (Spell sp : allSpells) { for (List<CDOMSingleRef<Ability>> list : feats) { Integer count = spells.get(ksi, list); aSpellList.add(new KitSpellBookEntry( spellBook, sp, list, count)); } } } final Formula choiceFormula = getCount(); int numberOfChoices; if (choiceFormula == null) { numberOfChoices = aSpellList.size(); } else { numberOfChoices = choiceFormula.resolve(aPC, "").intValue(); } // // Can't choose more entries than there are... // if (numberOfChoices > aSpellList.size()) { numberOfChoices = aSpellList.size(); } if (numberOfChoices == 0) { return false; } List<KitSpellBookEntry> xs; if (numberOfChoices == aSpellList.size()) { xs = aSpellList; } else { // // Force user to make enough selections // while (true) { xs = Globals.getChoiceFromList("Choose " + aClass.getKeyName() + " spell(s) for " + workingBook, aSpellList, new ArrayList<>(), numberOfChoices, aPC); if (!xs.isEmpty()) { break; } } } // // Add to list of things to add to the character // for (KitSpellBookEntry obj : xs) { if (obj != null) { obj.setPCClass(aClass); if (theSpells == null) { theSpells = new ArrayList<>(); } theSpells.add(obj); } else { warnings.add("SPELLS: Non-existant spell chosen"); } } if (theSpells != null && !theSpells.isEmpty()) { return true; } return false; } @Override public void apply(PlayerCharacter aPC) { for (KitSpellBookEntry sbe : theSpells) { updatePCSpells(aPC, sbe, aPC.getClassKeyed(sbe.getPCClass() .getKeyName())); } } private PCClass findDefaultSpellClass(final CDOMSingleRef<PCClass> ref, PlayerCharacter aPC) { if (castingClass == null) { List<? extends PObject> spellcastingClasses = aPC.getSpellClassList(); for (PObject obj : spellcastingClasses) { if (obj instanceof PCClass) { return (PCClass) obj; } } return null; } return aPC.getClassKeyed(ref.get().getKeyName()); } /** * Add spells from this Kit to the PC * * @param pc The PC to add the spells to * @param aSpell A Spell to add to the PC * @param pcClass The class instance the spells are to be added to. */ private void updatePCSpells(final PlayerCharacter pc, final KitSpellBookEntry aSpell, final PCClass pcClass) { Spell spell = aSpell.getSpell(); int spLevel = 99; // Check to see if we have any domains that have this spell. PObject owner = null; if (pc.hasDomains()) { for (Domain domain : pc.getDomainSet()) { List<? extends CDOMList<Spell>> lists = pc.getSpellLists(domain); int newLevel = SpellLevel.getFirstLevelForKey(spell, lists, pc); if (newLevel > 0 && newLevel < spLevel) { spLevel = newLevel; owner = domain; } } } if (spLevel == 99) { spLevel = SpellLevel.getFirstLevelForKey(spell, pc .getSpellLists(pcClass), pc); owner = pcClass; } if (spLevel < 0) { Logging.errorPrint("SPELLS: " + pcClass.getDisplayName() + " cannot cast spell \"" + spell.getKeyName() + "\""); return; } final CharacterSpell cs = new CharacterSpell(owner, spell); final List<CDOMSingleRef<Ability>> modifierList = aSpell.getModifiers(); int adjustedLevel = spLevel; List<Ability> metamagicFeatList = new ArrayList<>(); for (CDOMSingleRef<Ability> feat : modifierList) { Ability anAbility = feat.get(); adjustedLevel += anAbility.getSafe(IntegerKey.ADD_SPELL_LEVEL); metamagicFeatList.add(anAbility); } if (metamagicFeatList.size() <= 0) { metamagicFeatList = null; } if (!pc.hasSpellBook(aSpell.getBookName())) { pc.addSpellBook(aSpell.getBookName()); } for (int numTimes = 0; numTimes < aSpell.getCopies(); numTimes++) { final String aString = pc.addSpell(cs, metamagicFeatList, pcClass.getKeyName(), aSpell.getBookName(), adjustedLevel, spLevel); if (!aString.isEmpty()) { Logging.errorPrint("Add spell failed:" + aString); return; } } } @Override public String getObjectName() { return "Spells"; } public void setSpellBook(String string) { spellBook = string; } public String getSpellBook() { return spellBook; } public void setCastingClass(CDOMSingleRef<PCClass> reference) { castingClass = reference; } public CDOMSingleRef<PCClass> getCastingClass() { return castingClass; } public void addSpell(KnownSpellIdentifier ksi, ArrayList<CDOMSingleRef<Ability>> featList, int count) { spells.put(ksi, featList, count); } public Collection<KnownSpellIdentifier> getSpells() { return spells.getKeySet(); } public Collection<List<CDOMSingleRef<Ability>>> getAbilities( KnownSpellIdentifier ksi) { return spells.getSecondaryKeySet(ksi); } public Integer getSpellCount(KnownSpellIdentifier ksi, List<CDOMSingleRef<Ability>> abils) { return spells.get(ksi, abils); } }