/* * PCClassTest.java * * Copyright 2003 (C) Chris Ward <frugal@purplewombat.co.uk> * * 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 13-Jan-2004 * * Current Ver: $Revision$ * * * */ package pcgen.core; import java.net.URI; import java.net.URISyntaxException; import java.util.Collection; import java.util.List; import java.util.StringTokenizer; import junit.framework.Test; import junit.framework.TestSuite; import pcgen.AbstractCharacterTestCase; import pcgen.base.format.StringManager; import pcgen.base.lang.UnreachableError; import pcgen.cdom.base.AssociatedPrereqObject; import pcgen.cdom.base.CDOMReference; import pcgen.cdom.base.FormulaFactory; import pcgen.cdom.content.BonusSpellInfo; import pcgen.cdom.content.LevelCommandFactory; import pcgen.cdom.enumeration.FactKey; import pcgen.cdom.enumeration.FormulaKey; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.Nature; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.enumeration.StringKey; import pcgen.cdom.enumeration.VariableKey; import pcgen.cdom.formula.FixedSizeFormula; import pcgen.cdom.inst.PCClassLevel; import pcgen.cdom.list.AbilityList; import pcgen.cdom.reference.CDOMDirectSingleRef; import pcgen.cdom.reference.CDOMSingleRef; import pcgen.cdom.reference.Qualifier; import pcgen.core.analysis.PCClassKeyChange; import pcgen.core.bonus.Bonus; import pcgen.core.bonus.BonusObj; import pcgen.core.pclevelinfo.PCLevelInfo; import pcgen.core.prereq.Prerequisite; import pcgen.core.spell.Spell; import pcgen.persistence.PersistenceLayerException; import pcgen.persistence.lst.CampaignSourceEntry; import pcgen.persistence.lst.FeatLoader; import pcgen.persistence.lst.PCClassLoader; import pcgen.rules.context.LoadContext; import plugin.lsttokens.testsupport.BuildUtilities; import plugin.pretokens.parser.PreVariableParser; /** * @author wardc */ @SuppressWarnings("nls") public class PCClassTest extends AbstractCharacterTestCase { PCClass humanoidClass; SizeAdjustment sizeL; Race bugbearRace; Race bigBugbearRace; PCClass nymphClass; Race nymphRace; Prerequisite prereq; RuleCheck classPreRule; PCClass prClass; PCClass qClass; PCClass nqClass; /** * Constructs a new <code>PCClassTest</code>. * * @see PCGenTestCase#PCGenTestCase() */ public PCClassTest() { // Do Nothing } /** * Constructs a new <code>PCClassTest</code> with the given <var>name</var>. * * @param name the test case name * * @see PCGenTestCase#PCGenTestCase(String) */ public PCClassTest(final String name) { super(name); } /** * Run the test * @param args */ public static void main(final String[] args) { junit.textui.TestRunner.run(PCClassTest.class); } /** * Returns all test methods in this class. * @return A <tt>TestSuite</tt> */ public static Test suite() { // quick method, adds all methods beginning with "test" return new TestSuite(PCClassTest.class); } /** * Test name change */ public void testFireNameChangedVariable() { final PCClass myClass = new PCClass(); myClass.setName("myClass"); myClass.put(StringKey.KEY_NAME, "KEY_myClass"); PCClassLevel cl2 = myClass.getOriginalClassLevel(2); cl2.put(VariableKey.getConstant("someVar"), FormulaFactory .getFormulaFor("(CL=KEY_myClass/2) + CL=KEY_myClass")); assertEquals(1, cl2.getVariableKeys().size()); assertEquals("someVar", cl2.getVariableKeys().iterator().next() .toString()); assertNotNull(cl2.get(VariableKey.getConstant("someVar"))); assertEquals("(CL=KEY_myClass/2) + CL=KEY_myClass", cl2.get( VariableKey.getConstant("someVar")).toString()); myClass.setName("someOtherClass"); PCClassKeyChange.changeReferences("myClass", myClass); assertEquals(1, cl2.getVariableKeys().size()); assertEquals("someVar", cl2.getVariableKeys().iterator().next() .toString()); assertEquals("(CL=KEY_myClass/2) + CL=KEY_myClass", cl2.get( VariableKey.getConstant("someVar")).toString()); PCClassKeyChange.changeReferences("KEY_myClass", myClass); assertEquals(1, cl2.getVariableKeys().size()); assertEquals("someVar", cl2.getVariableKeys().iterator().next() .toString()); assertEquals("(CL=someOtherClass/2) + CL=someOtherClass", cl2.get( VariableKey.getConstant("someVar")).toString()); } /** * Test monster classes generating the correct number of skill points. */ public void testMonsterSkillPoints() { // Create a medium bugbear first level PlayerCharacter bugbear = new PlayerCharacter(); bugbear.setRace(bugbearRace); setPCStat(bugbear, intel, 12); // Test skills granted for each level bugbear.incrementClassLevel(1, humanoidClass); PCLevelInfo levelInfo = bugbear.getLevelInfo(0); assertEquals("First level of bugbear", 7, levelInfo .getSkillPointsGained(bugbear)); bugbear.incrementClassLevel(1, humanoidClass); levelInfo = bugbear.getLevelInfo(1); assertEquals("2nd level of bugbear", 1, levelInfo .getSkillPointsGained(bugbear)); bugbear.incrementClassLevel(1, humanoidClass); levelInfo = bugbear.getLevelInfo(2); assertEquals("3rd level of bugbear", 1, levelInfo .getSkillPointsGained(bugbear)); // Craete a huge bugbear first level bugbear = new PlayerCharacter(); bugbear.setRace(bigBugbearRace); assertEquals("big bugbear", "L", bugbear.getDisplay().getSize()); setPCStat(bugbear, intel, 10); bugbear.incrementClassLevel(1, humanoidClass); // Test skills granted for each level levelInfo = bugbear.getLevelInfo(0); assertEquals("First level of big bugbear", 6, levelInfo .getSkillPointsGained(bugbear)); bugbear.incrementClassLevel(1, humanoidClass); levelInfo = bugbear.getLevelInfo(1); assertEquals("2nd level of big bugbear", 0, levelInfo .getSkillPointsGained(bugbear)); bugbear.incrementClassLevel(1, humanoidClass); levelInfo = bugbear.getLevelInfo(2); assertEquals("3rd level of big bugbear", 1, levelInfo .getSkillPointsGained(bugbear)); // Create a nymph - first level PlayerCharacter nymph = new PlayerCharacter(); nymph.setRace(nymphRace); assertEquals("nymph", "M", nymph.getDisplay().getSize()); setPCStat(nymph, intel, 10); nymph.incrementClassLevel(1, nymphClass); // Test skills granted for each level levelInfo = nymph.getLevelInfo(0); assertEquals("First level of nymph", 24, levelInfo .getSkillPointsGained(bugbear)); nymph.incrementClassLevel(1, nymphClass); levelInfo = nymph.getLevelInfo(1); assertEquals("2nd level of nymph", 6, levelInfo.getSkillPointsGained(bugbear)); } /** * Test the interaction of prerequisites on PCClasses and bonuses and the * Bypass Class Prereqs flag. * @throws Exception */ public void testBypassClassPrereqs() throws Exception { LoadContext context = Globals.getContext(); // Setup class with prereqs and var based abilities with prereqs. final PreVariableParser parser = new PreVariableParser(); final Prerequisite aPrereq = parser.parse("VARGTEQ", "Foo,1", false, false); final GameMode gameMode = SettingsHandler.getGame(); RuleCheck aClassPreRule = gameMode.getModeContext().getReferenceContext() .silentlyGetConstructedCDOMObject(RuleCheck.class, "CLASSPRE"); if (aClassPreRule == null) { aClassPreRule = new RuleCheck(); aClassPreRule.setName("CLASSPRE"); gameMode.getModeContext().getReferenceContext().importObject(aClassPreRule); } aClassPreRule.setDefault(false); final PCClass aPrClass = new PCClass(); aPrClass.setName("PreReqClass"); aPrClass.put(StringKey.KEY_NAME, "KEY_PreReqClass"); final BonusObj aBonus = Bonus.newBonus(context, "MISC|SR|10|PREVARGTEQ:Foo,2"); if (aBonus != null) { aPrClass.addToListFor(ListKey.BONUS, aBonus); } aPrClass.addPrerequisite(aPrereq); final PCClass aQClass = new PCClass(); aQClass.setName("QualClass"); aQClass.put(StringKey.KEY_NAME, "KEY_QualClass"); CDOMDirectSingleRef<PCClass> ref = CDOMDirectSingleRef.getRef(aPrClass); aQClass.addToListFor(ListKey.QUALIFY, new Qualifier(PCClass.class, ref)); //aQClass.setQualifyString("KEY_PreReqClass|PreReqVar"); final PCClass aNqClass = new PCClass(); aNqClass.setName("NonQualClass"); aNqClass.put(StringKey.KEY_NAME, "KEY_NonQualClass"); aNqClass.put(VariableKey.getConstant("Foo"), FormulaFactory.ONE); aNqClass.getOriginalClassLevel(2).put(VariableKey.getConstant("Foo"), FormulaFactory.getFormulaFor(2)); // Setup character without prereqs final PlayerCharacter character = getCharacter(); // Test no prereqs and no bypass fails class and var assertFalse("PC with no prereqs should fail class qual test.", aPrClass .qualifies(character, aPrClass)); assertEquals("PC with no prereqs should fail var qual test.", 0.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test no prereqs and bypass passes class and fails var aClassPreRule.setDefault(true); assertTrue( "PC with no prereqs should pass class qual test when bypassing prereqs is on.", aPrClass.qualifies(character, aPrClass)); assertEquals( "PC with no prereqs should fail var qual test when bypass prereqs is on.", 0.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test prereqs and bypass pass class and var character.incrementClassLevel(1, aNqClass); assertTrue("PC with prereqs and bypass should pass class qual test.", aPrClass.qualifies(character, aPrClass)); character.incrementClassLevel(1, aNqClass); assertEquals("PC with prereqs and bypass should pass var qual test.", 10.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test prereqs and no bypass passes class and var aClassPreRule.setDefault(false); assertTrue( "PC with prereqs and no bypass should pass class qual test.", aPrClass.qualifies(character, aPrClass)); assertEquals( "PC with prereqs and no bypass should pass var qual test.", 10.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); } /** * Test the interaction of prerequisites on PCClasses and bonuses and the * Bypass Class Prereqs flag. * @throws Exception */ public void testBypassClassPrereqsDeprecated() throws Exception { LoadContext context = Globals.getContext(); // Setup class with prereqs and var based abilities with prereqs. final PreVariableParser parser = new PreVariableParser(); final Prerequisite aPrereq = parser.parse("VARGTEQ", "Foo,1", false, false); final GameMode gameMode = SettingsHandler.getGame(); RuleCheck aClassPreRule = gameMode.getModeContext().getReferenceContext() .silentlyGetConstructedCDOMObject(RuleCheck.class, "CLASSPRE"); if (aClassPreRule == null) { aClassPreRule = new RuleCheck(); aClassPreRule.setName("CLASSPRE"); gameMode.getModeContext().getReferenceContext().importObject(aClassPreRule); } aClassPreRule.setDefault(false); final PCClass aPrClass = new PCClass(); aPrClass.setName("PreReqClass"); aPrClass.put(StringKey.KEY_NAME, "KEY_PreReqClass"); final BonusObj aBonus = Bonus.newBonus(context, "MISC|SR|10|PREVARGTEQ:Foo,2"); if (aBonus != null) { aPrClass.addToListFor(ListKey.BONUS, aBonus); } aPrClass.addPrerequisite(aPrereq); final PCClass aQClass = new PCClass(); aQClass.setName("QualClass"); aQClass.put(StringKey.KEY_NAME, "KEY_QualClass"); CDOMDirectSingleRef<PCClass> ref = CDOMDirectSingleRef.getRef(aPrClass); aQClass.addToListFor(ListKey.QUALIFY, new Qualifier(PCClass.class, ref)); final PCClass aNqClass = new PCClass(); aNqClass.setName("NonQualClass"); aNqClass.put(StringKey.KEY_NAME, "KEY_NonQualClass"); aNqClass.put(VariableKey.getConstant("Foo"), FormulaFactory.ONE); aNqClass.getOriginalClassLevel(2).put(VariableKey.getConstant("Foo"), FormulaFactory.getFormulaFor(2)); // Setup character without prereqs final PlayerCharacter character = getCharacter(); // Test no prereqs and no bypass fails class and var assertFalse("PC with no prereqs should fail class qual test.", aPrClass .qualifies(character, aPrClass)); assertEquals("PC with no prereqs should fail var qual test.", 0.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test no prereqs and bypass passes class and fails var aClassPreRule.setDefault(true); assertTrue( "PC with no prereqs should pass class qual test when bypassing prereqs is on.", aPrClass.qualifies(character, aPrClass)); assertEquals( "PC with no prereqs should fail var qual test when bypass prereqs is on.", 0.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test prereqs and bypass pass class and var character.incrementClassLevel(1, aNqClass); assertTrue("PC with prereqs and bypass should pass class qual test.", aPrClass.qualifies(character, aPrClass)); character.incrementClassLevel(1, aNqClass); assertEquals("PC with prereqs and bypass should pass var qual test.", 10.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test prereqs and no bypass passes class and var aClassPreRule.setDefault(false); assertTrue( "PC with prereqs and no bypass should pass class qual test.", aPrClass.qualifies(character, aPrClass)); assertEquals( "PC with prereqs and no bypass should pass var qual test.", 10.0, aPrClass.getBonusTo("MISC", "SR", 1, character), 0.1); } /** * Test the interaction of prerequisites on PCClasses and bonuses and the * Qualifies functionality associated with a class. * @throws Exception */ public void testQualifies() throws Exception { // Setup character without prereqs final PlayerCharacter character = getCharacter(); // Test no prereqs and no qualifies fails class and var assertFalse("PC with no prereqs should fail class qual test.", prClass .qualifies(character, prClass)); assertEquals("PC with no prereqs should fail var qual test.", 0.0, prClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test no prereqs and qualifies passes class and fails var character.incrementClassLevel(1, qClass); assertTrue( "PC with no prereqs but a qualifies should pass class qual test.", prClass.qualifies(character, prClass)); assertEquals( "PC with no prereqs but a qualifies should fail var qual test.", 0.0, prClass.getBonusTo("MISC", "SR", 1, character), 0.1); // Test prereqs and qualifies pass class and var character.incrementClassLevel(1, nqClass); assertTrue( "PC with prereqs and qualifies should pass class qual test.", prClass.qualifies(character, prClass)); character.incrementClassLevel(1, nqClass); assertEquals( "PC with prereqs and qualifies should pass var qual test.", 10.0, prClass.getBonusTo("MISC", "SR", 1, character), 0.1); } /** * Test the processing of getPCCText to ensure that it correctly produces * an LST representation of an object and that the LST can then be reloaded * to recrete the object. * * @throws PersistenceLayerException */ public void testGetPCCText() throws PersistenceLayerException { FactKey.getConstant("Abb", new StringManager()); // Test a basic class String classPCCText = humanoidClass.getPCCText(); assertNotNull("PCC Text for race should not be null", classPCCText); CampaignSourceEntry source; try { source = new CampaignSourceEntry(new Campaign(), new URI("file:/" + getClass().getName() + ".java")); } catch (URISyntaxException e) { throw new UnreachableError(e); } PCClass reconstClass = null; System.out.println("Got text:" + classPCCText); reconstClass = parsePCClassText(classPCCText, source); assertEquals( "getPCCText should be the same after being encoded and reloaded", classPCCText, reconstClass.getPCCText()); assertEquals( "Class abbrev was not restored after saving and reloading.", humanoidClass.getAbbrev(), reconstClass.getAbbrev()); // Test a class with some innate spells String b = "1" + "\t" + "SPELLS:" + "Humanoid|TIMES=1|CASTERLEVEL=var(\"TCL\")|Create undead,11+WIS"; PCClassLoader classLoader = new PCClassLoader(); classLoader.parseLine(Globals.getContext(), humanoidClass, b, source); classPCCText = humanoidClass.getPCCText(); assertNotNull("PCC Text for race should not be null", classPCCText); reconstClass = null; System.out.println("Got text:" + classPCCText); reconstClass = parsePCClassText(classPCCText, source); assertEquals( "getPCCText should be the same after being encoded and reloaded", classPCCText, reconstClass.getPCCText()); assertEquals( "Class abbrev was not restored after saving and reloading.", humanoidClass.getAbbrev(), reconstClass.getAbbrev()); Collection<CDOMReference<Spell>> startSpells = humanoidClass.getOriginalClassLevel(1).getListMods(Spell.SPELLS); Collection<CDOMReference<Spell>> reconstSpells = reconstClass.getOriginalClassLevel(1).getListMods(Spell.SPELLS); assertEquals("All spell should have been reconstituted.", startSpells .size(), reconstSpells.size()); assertEquals("Spell names should been preserved.", startSpells, reconstSpells); } /** * Test the function of the getHighestLevelSpell method. * @throws PersistenceLayerException */ public void testGetHighestLevelSpell() throws PersistenceLayerException { LoadContext context = Globals.getContext(); PCClass megaCasterClass = new PCClass(); megaCasterClass.setName("MegaCaster"); BuildUtilities.setFact(megaCasterClass, "SpellType", "Arcane"); context.unconditionallyProcess(megaCasterClass, "SPELLSTAT", "CHA"); megaCasterClass.put(ObjectKey.SPELLBOOK, false); megaCasterClass.put(ObjectKey.MEMORIZE_SPELLS, false); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(1), "KNOWN", "4,2,2,3,4,5"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(1), "CAST", "3,1,2,3,4,5"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(2), "KNOWN", "4,2,2,3,4,5,6,7,8,9,10"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(2), "CAST", "3,1,2,3,4,5,6,7,8,9,10"); Globals.getContext().getReferenceContext().importObject(megaCasterClass); final PlayerCharacter character = getCharacter(); assertEquals("Highest spell level for class", 10, character.getSpellSupport(megaCasterClass).getHighestLevelSpell()); character.incrementClassLevel(1, megaCasterClass); PCClass charClass = character.getClassKeyed(megaCasterClass.getKeyName()); assertEquals("Highest spell level for character's class", 10, character.getSpellSupport(charClass).getHighestLevelSpell()); String sbook = Globals.getDefaultSpellBook(); String cast = character.getSpellSupport(charClass).getCastForLevel(10, sbook, true, false, character) + character.getSpellSupport(charClass).getBonusCastForLevelString(10, sbook, character); assertEquals( "Should not be able to cast 10th level spells at 1st level", "0", cast); cast = character.getSpellSupport(charClass).getCastForLevel(5, sbook, true, false, character) + character.getSpellSupport(charClass).getBonusCastForLevelString(5, sbook, character); assertEquals("Should be able to cast 5th level spells at 1st level", "5", cast); Ability casterFeat = new Ability(); FeatLoader featLoader = new FeatLoader(); CampaignSourceEntry source; try { source = new CampaignSourceEntry(new Campaign(), new URI("file:/" + getClass().getName() + ".java")); } catch (URISyntaxException e) { throw new UnreachableError(e); } featLoader .parseLine( Globals.getContext(), casterFeat, "CasterBoost TYPE:General BONUS:SPELLCAST|CLASS=MegaCaster;LEVEL=11|1", source); casterFeat.setCDOMCategory(AbilityCategory.FEAT); context.getReferenceContext().importObject(casterFeat); AbstractCharacterTestCase.applyAbility(character, AbilityCategory.FEAT, casterFeat, null); cast = character.getSpellSupport(charClass).getCastForLevel(11, sbook, true, false, character) + character.getSpellSupport(charClass).getBonusCastForLevelString(11, sbook, character); assertEquals("Should be able to cast 11th level spells with feat", "1", cast); assertEquals("Should be able to cast 11th level spells with feat", 11, character.getSpellSupport(charClass).getHighestLevelSpell(character)); } public void testGetKnownForLevel() { LoadContext context = Globals.getContext(); PCClass megaCasterClass = new PCClass(); megaCasterClass.setName("MegaCaster"); BuildUtilities.setFact(megaCasterClass, "SpellType", "Arcane"); context.unconditionallyProcess(megaCasterClass, "SPELLSTAT", "CHA"); megaCasterClass.put(ObjectKey.SPELLBOOK, false); megaCasterClass.put(ObjectKey.MEMORIZE_SPELLS, false); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(1), "KNOWN", "4,2,2,3,4,5,0"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(1), "CAST", "3,1,2,3,4,5,0,0"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(2), "KNOWN", "4,2,2,3,4,5,6,7,8,9,10"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(2), "CAST", "3,1,2,3,4,5,6,7,8,9,10"); Globals.getContext().getReferenceContext().importObject(megaCasterClass); context.getReferenceContext().buildDerivedObjects(); context.loadCampaignFacets(); final PlayerCharacter character = getCharacter(); // Test retrieval for a non-spell casting class. character.incrementClassLevel(1, nqClass); PCClass charClass = character.getClassKeyed(nqClass.getKeyName()); assertEquals("Known 0th level for non spell casting class", 0, character.getSpellSupport(charClass).getKnownForLevel(0, character)); // Test retrieval for a spell casting class. character.incrementClassLevel(1, megaCasterClass); charClass = character.getClassKeyed(megaCasterClass.getKeyName()); setPCStat(character, cha, 10); assertEquals("Known 0th level for character's class", 4, character.getSpellSupport(charClass).getKnownForLevel(0, character)); assertEquals("Known 1st level where stat is too low", 0, character.getSpellSupport(charClass).getKnownForLevel(1, character)); setPCStat(character, cha, 11); character.calcActiveBonuses(); assertEquals("Known 1st level where stat is high enough, but no bonus", 2, character.getSpellSupport(charClass).getKnownForLevel(1, character)); setPCStat(character, cha, 18); character.calcActiveBonuses(); assertEquals("Known 1st level where stat gives bonus but not active", 2, character.getSpellSupport(charClass).getKnownForLevel(1, character)); RuleCheck bonusKnownRule = new RuleCheck(); bonusKnownRule.setName(RuleConstants.BONUSSPELLKNOWN); bonusKnownRule.setDefault(true); GameMode gameMode = SettingsHandler.getGame(); gameMode.getModeContext().getReferenceContext().importObject(bonusKnownRule); BonusSpellInfo bsi = new BonusSpellInfo(); bsi.setName("1"); bsi.setStatScore(12); bsi.setStatRange(8); context.getReferenceContext().importObject(bsi); bsi = new BonusSpellInfo(); bsi.setName("5"); bsi.setStatScore(20); bsi.setStatRange(8); assertEquals("Known 1st level where stat gives bonus and active", 3, character.getSpellSupport(charClass).getKnownForLevel(1, character)); assertEquals("Known 2nd level for character's class", 2, character.getSpellSupport(charClass).getKnownForLevel(2, character)); assertEquals("Known 3rd level for character's class", 3, character.getSpellSupport(charClass).getKnownForLevel(3, character)); assertEquals("Known 4th level for character's class", 4, character.getSpellSupport(charClass).getKnownForLevel(4, character)); charClass.put(IntegerKey.KNOWN_SPELLS_FROM_SPECIALTY, 1); assertEquals("Known 5th level for character's class", 6, character.getSpellSupport(charClass).getKnownForLevel(5, character)); assertEquals("Known 6th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(6, character)); assertEquals("Known 7th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(7, character)); // Add spell bonus for level above known max bsi = new BonusSpellInfo(); bsi.setName("7"); bsi.setStatScore(12); bsi.setStatRange(8); assertEquals("Known 7th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(7, character)); assertEquals("Known 8th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(8, character)); } public void testGetKnownForLevelSpellstatOther() { LoadContext context = Globals.getContext(); PCClass megaCasterClass = new PCClass(); megaCasterClass.setName("MegaCasterOther"); BuildUtilities.setFact(megaCasterClass, "SpellType", "Arcane"); context.unconditionallyProcess(megaCasterClass, "SPELLSTAT", "OTHER"); megaCasterClass.put(ObjectKey.SPELLBOOK, false); megaCasterClass.put(ObjectKey.MEMORIZE_SPELLS, false); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(1), "KNOWN", "4,2,2,3,4,5,0"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(1), "CAST", "3,1,2,3,4,5,0,0"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(2), "KNOWN", "4,2,2,3,4,5,6,7,8,9,10"); context.unconditionallyProcess(megaCasterClass.getOriginalClassLevel(2), "CAST", "3,1,2,3,4,5,6,7,8,9,10"); Globals.getContext().getReferenceContext().importObject(megaCasterClass); context.getReferenceContext().buildDerivedObjects(); context.loadCampaignFacets(); final PlayerCharacter character = getCharacter(); // Test retrieval for a non-spell casting class. character.incrementClassLevel(1, nqClass); PCClass charClass = character.getClassKeyed(nqClass.getKeyName()); assertEquals("Known 0th level for non spell casting class", 0, character.getSpellSupport(charClass).getKnownForLevel(0, character)); // Test retrieval for a spell casting class. character.incrementClassLevel(1, megaCasterClass); charClass = character.getClassKeyed(megaCasterClass.getKeyName()); setPCStat(character, cha, 10); assertEquals("Known 0th level for character's class", 4, character.getSpellSupport(charClass).getKnownForLevel(0, character)); character.calcActiveBonuses(); assertEquals("Known 1st level where stat is high enough, but no bonus", 2, character.getSpellSupport(charClass).getKnownForLevel(1, character)); RuleCheck bonusKnownRule = new RuleCheck(); bonusKnownRule.setName(RuleConstants.BONUSSPELLKNOWN); bonusKnownRule.setDefault(true); GameMode gameMode = SettingsHandler.getGame(); gameMode.getModeContext().getReferenceContext().importObject(bonusKnownRule); BonusSpellInfo bsi = new BonusSpellInfo(); bsi.setName("1"); bsi.setStatScore(12); bsi.setStatRange(8); context.getReferenceContext().importObject(bsi); bsi = new BonusSpellInfo(); bsi.setName("5"); bsi.setStatScore(20); bsi.setStatRange(8); assertEquals("Known 1st level where stat would give bonus and active", 2, character.getSpellSupport(charClass).getKnownForLevel(1, character)); assertEquals("Known 2nd level for character's class", 2, character.getSpellSupport(charClass).getKnownForLevel(2, character)); assertEquals("Known 3rd level for character's class", 3, character.getSpellSupport(charClass).getKnownForLevel(3, character)); assertEquals("Known 4th level for character's class", 4, character.getSpellSupport(charClass).getKnownForLevel(4, character)); charClass.put(IntegerKey.KNOWN_SPELLS_FROM_SPECIALTY, 1); assertEquals("Known 5th level for character's class", 6, character.getSpellSupport(charClass).getKnownForLevel(5, character)); assertEquals("Known 6th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(6, character)); assertEquals("Known 7th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(7, character)); // Add spell bonus for level above known max bsi = new BonusSpellInfo(); bsi.setName("7"); bsi.setStatScore(12); bsi.setStatRange(8); assertEquals("Known 7th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(7, character)); assertEquals("Known 8th level for character's class", 0, character.getSpellSupport(charClass).getKnownForLevel(8, character)); } /** * Test the definition and application of abilities. * @throws PersistenceLayerException */ public void testAddAbility() throws PersistenceLayerException { LoadContext context = Globals.getContext(); // Create some abilities to be added AbilityCategory cat = context.getReferenceContext().constructCDOMObject( AbilityCategory.class, "TestCat"); Ability ab1 = new Ability(); ab1.setName("Ability1"); ab1.setCDOMCategory(cat); context.getReferenceContext().importObject(ab1); Ability ab2 = new Ability(); ab2.setName("Ability2"); ab2.setCDOMCategory(cat); context.getReferenceContext().importObject(ab2); // Link them to a template CampaignSourceEntry source; try { source = new CampaignSourceEntry(new Campaign(), new URI("file:/" + getClass().getName() + ".java")); } catch (URISyntaxException e) { throw new UnreachableError(e); } String classPCCText = "CLASS:Cleric HD:8 CLASSTYPE:PC TYPE:Base.PC ABB:Clr ABILITY:TestCat|AUTOMATIC|Ability1\n" + "CLASS:Cleric STARTSKILLPTS:2\n" + "2 ABILITY:TestCat|AUTOMATIC|Ability2"; PCClass pcclass = parsePCClassText(classPCCText, source); ab1.setCDOMCategory(cat); ab2.setCDOMCategory(cat); context.getReferenceContext().importObject(ab1); context.getReferenceContext().importObject(ab2); CDOMSingleRef<AbilityCategory> acRef = context.getReferenceContext().getCDOMReference( AbilityCategory.class, "TestCat"); assertTrue(context.getReferenceContext().resolveReferences(null)); CDOMReference<AbilityList> autoList = AbilityList.getAbilityListReference(acRef, Nature.AUTOMATIC); Collection<CDOMReference<Ability>> mods = pcclass.getListMods(autoList); assertEquals(1, mods.size()); CDOMReference<Ability> ref = mods.iterator().next(); Collection<Ability> abilities = ref.getContainedObjects(); assertEquals(1, abilities.size()); assertEquals(ab1, abilities.iterator().next()); Collection<AssociatedPrereqObject> assocs = pcclass.getListAssociations(autoList, ref); assertEquals(1, assocs.size()); PCClassLevel level = pcclass.getOriginalClassLevel(2); mods = level.getListMods(autoList); assertEquals(1, mods.size()); ref = mods.iterator().next(); abilities = ref.getContainedObjects(); assertEquals(1, abilities.size()); assertEquals(ab2, abilities.iterator().next()); assocs = level.getListAssociations(autoList, ref); assertEquals(1, assocs.size()); // Add the class to the character PlayerCharacter pc = getCharacter(); pc.incrementClassLevel(1, pcclass, true); assertTrue("Character should have ability1.", hasAbility(pc, cat, Nature.AUTOMATIC, ab1)); assertFalse("Character should not have ability2.", hasAbility(pc, cat, Nature.AUTOMATIC, ab2)); pc.incrementClassLevel(1, pcclass, true); assertTrue("Character should have ability1.", hasAbility(pc, cat, Nature.AUTOMATIC, ab1)); assertTrue("Character should have ability2.", hasAbility(pc, cat, Nature.AUTOMATIC, ab2)); } /** * Test the function of the LEVELSPERFEAT in setLevel() * Monster class without a levels per feat setting. */ public void testDefaultLevelsPerFeatMonster() { PlayerCharacter pc = getCharacter(); pc.setRace(nymphRace); List<BonusObj> bonusList = nymphClass.getRawBonusList(pc); assertEquals("Bonus list empty", 0, bonusList.size()); pc.incrementClassLevel(1, nymphClass); bonusList = pc.getClassKeyed(nymphClass.getKeyName()).getRawBonusList(pc); assertEquals("Only one bonus", 1, bonusList.size()); assertEquals("Bonus added ", "FEAT|PCPOOL|MAX(CL-3+3,0)/3", bonusList.get(0).toString()); } /** * Test the function of the LEVELSPERFEAT in setLevel() * Monster class with a levels per feat setting. */ public void testLevelsPerFeatMonster() { PlayerCharacter pc = getCharacter(); nymphClass.put(IntegerKey.LEVELS_PER_FEAT, 4); List<BonusObj> bonusList = nymphClass.getRawBonusList(pc); assertEquals("Bonus list empty", 0, bonusList.size()); pc.setRace(nymphRace); bonusList = nymphClass.getRawBonusList(pc); assertEquals("Bonus list empty", 0, bonusList.size()); pc.incrementClassLevel(1, nymphClass); bonusList = pc.getClassKeyed(nymphClass.getKeyName()).getRawBonusList(pc); assertEquals("No bonus due to the LEVELSPERFEAT", 0, bonusList.size()); } /** * Test the function of the LEVELSPERFEAT in setLevel() * Non monster class without a levels per feat setting. */ public void testDefaultLevelsPerFeatNonMonster() { PlayerCharacter pc = getCharacter(); pc.setRace(nymphRace); List<BonusObj> bonusList = humanoidClass.getRawBonusList(pc); assertEquals("Bonus list starting size", 3, bonusList.size()); pc.incrementClassLevel(1, humanoidClass); bonusList = pc.getClassKeyed(humanoidClass.getKeyName()).getRawBonusList(pc); assertEquals("Bonus added ", "FEAT|PCPOOL|MAX(CL-3+3,0)/3", bonusList.get(3).toString()); assertEquals("Only one new bonus", 4, bonusList.size()); } /** * Test the function of the LEVELSPERFEAT in setLevel() * Non monster class with a levels per feat setting. */ public void testLevelsPerFeatNonMonster() { PlayerCharacter pc = getCharacter(); pc.setRace(nymphRace); humanoidClass.put(IntegerKey.LEVELS_PER_FEAT, 4); List<BonusObj> bonusList = humanoidClass.getRawBonusList(pc); assertEquals("Bonus list starting size", 3, bonusList.size()); pc.incrementClassLevel(1, humanoidClass); bonusList = pc.getClassKeyed(humanoidClass.getKeyName()).getRawBonusList(pc); assertEquals("No new bonus due to the LEVELSPERFEAT", 3, bonusList.size()); } /** * Parse a class definition and return the populated PCClass object. * * @param classPCCText The textual definition of the class. * @param source The source that the class is from. * @return The populated class. * @throws PersistenceLayerException */ private PCClass parsePCClassText(String classPCCText, CampaignSourceEntry source) throws PersistenceLayerException { PCClassLoader pcClassLoader = new PCClassLoader(); PCClass reconstClass = null; StringTokenizer tok = new StringTokenizer(classPCCText, "\n"); while (tok.hasMoreTokens()) { String line = tok.nextToken(); if (!line.trim().isEmpty()) { System.out.println("Processing line:'" + line + "'."); reconstClass = pcClassLoader.parseLine(Globals.getContext(), reconstClass, line, source); } } return reconstClass; } /** * @see pcgen.AbstractCharacterTestCase#setUp() */ @Override protected void setUp() throws Exception { super.setUp(); Campaign customCampaign = new Campaign(); customCampaign.setName("Unit Test"); customCampaign.setName("KEY_Unit Test"); customCampaign.addToListFor(ListKey.DESCRIPTION, new Description("Unit Test data")); CampaignSourceEntry source; try { source = new CampaignSourceEntry(customCampaign, new URI("file:/" + getClass().getName() + ".java")); } catch (URISyntaxException e) { throw new UnreachableError(e); } // Create the monseter class type GameMode gamemode = SettingsHandler.getGame(); gamemode.addClassType( "Monster CRFORMULA:0 ISMONSTER:YES XPPENALTY:NO"); gamemode.setSkillMultiplierLevels("4"); // Create the humanoid class String classDef = "CLASS:Humanoid KEY:KEY_Humanoid HD:8 CLASSTYPE:Monster STARTSKILLPTS:1 " + "MODTOSKILLS:NO MONSKILL:6+INT MONNONSKILLHD:1|PRESIZELTEQ:M " + "MONNONSKILLHD:2|PRESIZEEQ:L"; PCClassLoader classLoader = new PCClassLoader(); LoadContext context = Globals.getContext(); humanoidClass = classLoader.parseLine(context, null, classDef, source); Globals.getContext().getReferenceContext().importObject(humanoidClass); classDef = "CLASS:Nymph KEY:KEY_Nymph CLASSTYPE:Monster HD:6 STARTSKILLPTS:6 MODTOSKILLS:YES "; classLoader = new PCClassLoader(); nymphClass = classLoader.parseLine(context, null, classDef, source); Globals.getContext().getReferenceContext().importObject(nymphClass); CDOMDirectSingleRef<SizeAdjustment> mediumRef = CDOMDirectSingleRef.getRef(medium); CDOMDirectSingleRef<SizeAdjustment> largeRef = CDOMDirectSingleRef.getRef(large); // Create the large size mod // Create the BugBear race bugbearRace = new Race(); bugbearRace.setName("Bugbear"); bugbearRace.put(StringKey.KEY_NAME, "KEY_Bugbear"); bugbearRace.put(FormulaKey.SIZE, new FixedSizeFormula(mediumRef)); bugbearRace.addToListFor(ListKey.HITDICE_ADVANCEMENT, Integer.MAX_VALUE); bugbearRace.put(IntegerKey.INITIAL_SKILL_MULT, 1); Globals.getContext().getReferenceContext().importObject(bugbearRace); bigBugbearRace = new Race(); bigBugbearRace.setName("BigBugbear"); bigBugbearRace.put(StringKey.KEY_NAME, "KEY_BigBugbear"); bigBugbearRace.put(FormulaKey.SIZE, new FixedSizeFormula(largeRef)); bigBugbearRace.addToListFor(ListKey.HITDICE_ADVANCEMENT, Integer.MAX_VALUE); bigBugbearRace.put(IntegerKey.INITIAL_SKILL_MULT, 1); Globals.getContext().getReferenceContext().importObject(bigBugbearRace); // Create the Nymph race nymphRace = new Race(); nymphRace.setName("Nymph"); nymphRace.put(StringKey.KEY_NAME, "KEY_Nymph"); nymphRace.put(FormulaKey.SIZE, new FixedSizeFormula(mediumRef)); nymphRace.addToListFor(ListKey.HITDICE_ADVANCEMENT, Integer.MAX_VALUE); nymphRace.put(ObjectKey.MONSTER_CLASS, new LevelCommandFactory( CDOMDirectSingleRef.getRef(nymphClass), FormulaFactory .getFormulaFor(0))); Globals.getContext().getReferenceContext().importObject(nymphRace); // Setup class with prereqs and var based abilities with prereqs. PreVariableParser parser = new PreVariableParser(); prereq = parser.parse("VARGTEQ", "Foo,1", false, false); classPreRule = new RuleCheck(); classPreRule.setName("CLASSPRE"); classPreRule.setDefault(false); gamemode.getModeContext().getReferenceContext().importObject(classPreRule); prClass = new PCClass(); prClass.setName("PreReqClass"); prClass.put(StringKey.KEY_NAME, "KEY_PreReqClass"); final BonusObj aBonus = Bonus.newBonus(context, "MISC|SR|10|PREVARGTEQ:Foo,2"); if (aBonus != null) { prClass.addToListFor(ListKey.BONUS, aBonus); } prClass.addPrerequisite(prereq); qClass = new PCClass(); qClass.setName("QualClass"); qClass.put(StringKey.KEY_NAME, "KEY_QualClass"); CDOMDirectSingleRef<PCClass> ref = CDOMDirectSingleRef.getRef(prClass); qClass.addToListFor(ListKey.QUALIFY, new Qualifier(PCClass.class, ref)); nqClass = new PCClass(); nqClass.setName("NonQualClass"); nqClass.put(StringKey.KEY_NAME, "KEY_NonQualClass"); nqClass.put(VariableKey.getConstant("Foo"), FormulaFactory.ONE); nqClass.getOriginalClassLevel(2).put(VariableKey.getConstant("Foo"), FormulaFactory.getFormulaFor(2)); } }