/* * Copyright 2008 (C) Tom Parker <thpr@users.sourceforge.net> * Derived from Domain.java and PCClass.java * Copyright 2001 (C) Bryan McRoberts <merton_monk@yahoo.com> * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package pcgen.core.analysis; import java.util.Collection; import java.util.Collections; import java.util.List; import pcgen.base.formula.Formula; import pcgen.cdom.base.AssociatedPrereqObject; import pcgen.cdom.base.CDOMReference; import pcgen.cdom.enumeration.AssociationKey; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.helper.ClassSource; import pcgen.cdom.inst.PCClassLevel; import pcgen.cdom.list.DomainSpellList; import pcgen.cdom.reference.CDOMSingleRef; import pcgen.core.Domain; import pcgen.core.Globals; import pcgen.core.PCClass; import pcgen.core.PlayerCharacter; import pcgen.core.QualifiedObject; import pcgen.core.character.CharacterSpell; import pcgen.core.prereq.PrereqHandler; import pcgen.core.spell.Spell; public class DomainApplication { /** * Sets the locked flag on a PC * * @param pc */ public static void applyDomain(PlayerCharacter pc, Domain d) { ClassSource source = pc.getDomainSource(d); PCClass aClass = pc.getClassKeyed(source.getPcclass().getKeyName()); if (aClass != null) { int maxLevel; for (maxLevel = 0; maxLevel < 10; maxLevel++) { if (pc.getSpellSupport(aClass).getCastForLevel(maxLevel, pc) == 0) { break; } } if (maxLevel > 0) { addSpellsToClassForLevels(pc, d, aClass, 0, maxLevel - 1); } if ((maxLevel > 1) && (aClass.getSafe(IntegerKey.KNOWN_SPELLS_FROM_SPECIALTY) == 0)) { DomainSpellList domainSpellList = d .get(ObjectKey.DOMAIN_SPELLLIST); final List<Spell> aList = pc.getAllSpellsInLists(Collections .singletonList(domainSpellList)); for (Spell gcs : aList) { if (SpellLevel.getFirstLvlForKey(gcs, domainSpellList, pc) < maxLevel) { pc.setDomainSpellCount(aClass, 1); break; } } } } Collection<CDOMReference<Spell>> mods = d.getSafeListMods(Spell.SPELLS); for (CDOMReference<Spell> ref : mods) { Collection<Spell> spells = ref.getContainedObjects(); Collection<AssociatedPrereqObject> assoc = d.getListAssociations(Spell.SPELLS, ref); for (AssociatedPrereqObject apo : assoc) { if (!PrereqHandler.passesAll(apo.getPrerequisiteList(), pc, d)) { continue; } for (Spell s : spells) { String book = apo.getAssociation(AssociationKey.SPELLBOOK); List<CharacterSpell> aList = pc .getCharacterSpells(aClass, s, book, -1); if (aList.isEmpty()) { Formula times = apo .getAssociation(AssociationKey.TIMES_PER_UNIT); CharacterSpell cs = new CharacterSpell(d, s); int resolvedTimes = times.resolve(pc, d.getQualifiedKey()).intValue(); cs.addInfo(1, resolvedTimes, book); pc.addCharacterSpell(aClass, cs); } } } } } /** * Remove a domain from the character. * @param pc The character * @param domain The domain. */ public static void removeDomain(PlayerCharacter pc, Domain domain) { ClassSource source = pc.getDomainSource(domain); PCClass aClass = pc.getClassKeyed(source.getPcclass().getKeyName()); if (aClass != null) { int maxLevel; for (maxLevel = 0; maxLevel < 10; maxLevel++) { if (pc.getSpellSupport(aClass).getCastForLevel(maxLevel, pc) == 0) { break; } } if (maxLevel > 0) { removeSpellsFromClassForLevels(pc, domain, aClass); } if ((maxLevel > 1) && (aClass.getSafe(IntegerKey.KNOWN_SPELLS_FROM_SPECIALTY) == 0)) { DomainSpellList domainSpellList = domain .get(ObjectKey.DOMAIN_SPELLLIST); final List<Spell> aList = pc.getAllSpellsInLists(Collections .singletonList(domainSpellList)); for (Spell gcs : aList) { if (SpellLevel.getFirstLvlForKey(gcs, domainSpellList, pc) < maxLevel) { pc.removeDomainSpellCount(aClass); break; } } } } if (!pc.isImporting()) { BonusActivation.deactivateBonuses(domain, pc); } } /** * Remove any spells granted by the domain to the class. * @param pc The character. * @param domain The domain. * @param aClass The class which would have the spells allocated. */ public static void removeSpellsFromClassForLevels(PlayerCharacter pc, Domain domain, PCClass aClass) { if (aClass == null) { return; } Collection<? extends CharacterSpell> characterSpells = pc.getCharacterSpells(aClass); for (CharacterSpell characterSpell : characterSpells) { if (characterSpell.getOwner() == domain) { pc.removeCharacterSpell(aClass, characterSpell); } } } public static void addSpellsToClassForLevels(PlayerCharacter pc, Domain d, PCClass aClass, int minLevel, int maxLevel) { if (aClass == null) { return; } for (int aLevel = minLevel; aLevel <= maxLevel; aLevel++) { Collection<Spell> domainSpells = pc.getSpellsIn(d.get(ObjectKey.DOMAIN_SPELLLIST), aLevel); for (Spell spell : domainSpells) { List<CharacterSpell> slist = pc.getCharacterSpells(aClass, spell, Globals .getDefaultSpellBook(), aLevel); boolean flag = true; for (CharacterSpell cs1 : slist) { flag = !(cs1.getOwner().equals(d)); if (!flag) { break; } } if (flag) { CharacterSpell cs = new CharacterSpell(d, spell); cs.addInfo(aLevel, 1, Globals.getDefaultSpellBook()); pc.addCharacterSpell(aClass, cs); } } } } private static void addDomain(final PlayerCharacter aPC, PCClass cl, Domain d) { if (d.qualifies(aPC, d)) { // TODO Not entirely correct, as this takes this level, not // the level where BONUS DOMAINS was present ClassSource cs = new ClassSource(cl, aPC.getLevel(cl)); aPC.addDomain(d, cs); applyDomain(aPC, d); } } public static void addDomainsUpToLevel(PCClass cl, final int aLevel, final PlayerCharacter aPC) { // any domains set by level would have already been saved // and don't need to be re-set at level up time if (aPC.isImporting()) { return; } /* * Note this uses ALL of the domains up to and including this level, * because there is the possibility (albeit strange) that the PC was not * qualified at a previous level change, but the PlayerCharacter is now * qualified for the given Domain. Even this has quirks, since it is * only applied at the time of level increase, but I think that quirk * should be resolved by a CDOM system around 6.0 - thpr 10/23/06 */ for (QualifiedObject<CDOMSingleRef<Domain>> qo : cl.getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if (ref != null) { addDomain(aPC, cl, ref.get()); } } for (int i = 0 ; i <= aLevel; i++) { // TODO This stinks for really high level characters - can this ever // get null back? PCClassLevel pcl = aPC.getActiveClassLevel(cl, i); for (QualifiedObject<CDOMSingleRef<Domain>> qo : pcl .getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if (ref != null) { addDomain(aPC, cl, ref.get()); } } } } public static void removeDomainsForLevel(PCClass cl, final int removedLevel, final PlayerCharacter aPC) { /* * Note this uses ALL of the domains up to and including this level, * because there is the possibility (albeit strange) that the PC was * qualified at a previous level change, but the PlayerCharacter is now * not qualified for the given Domain. Even this has quirks, since it is * only applied at the time of level increase, but I think that quirk * should be resolved by a CDOM system around 6.0 - thpr 10/23/06 */ for (QualifiedObject<CDOMSingleRef<Domain>> qo : cl.getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if (ref == null) { ref = qo.getRawObject(); aPC.removeDomain(ref.get()); } } for (int i = 0 ; i <= removedLevel; i++) { // TODO This stinks for really high level characters - can this ever // get null back? PCClassLevel pcl = aPC.getActiveClassLevel(cl, i); for (QualifiedObject<CDOMSingleRef<Domain>> qo : pcl .getSafeListFor(ListKey.DOMAIN)) { CDOMSingleRef<Domain> ref = qo.getObject(aPC, cl); if ((ref == null) || (i == removedLevel)) { ref = qo.getRawObject(); aPC.removeDomain(ref.get()); } } } } }