/* * CharacterLevelsFacadeImplTest.java * Copyright 2013 (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 26/11/2013 * * $Id$ */ package pcgen.gui2.facade; import java.net.URI; import pcgen.AbstractJunit4CharacterTestCase; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.enumeration.SkillArmorCheck; import pcgen.core.Campaign; import pcgen.core.GameMode; import pcgen.core.Globals; import pcgen.core.LevelInfo; import pcgen.core.PCClass; import pcgen.core.PlayerCharacter; import pcgen.core.SettingsHandler; import pcgen.core.Skill; import pcgen.core.XPTable; import pcgen.core.pclevelinfo.PCLevelInfo; import pcgen.facade.core.DataSetFacade; import pcgen.facade.core.UIDelegate; import pcgen.persistence.lst.CampaignSourceEntry; import pcgen.rules.context.LoadContext; import pcgen.util.TestHelper; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; /** * CharacterLevelsFacadeImplTest checks that CharacterLevelsFacadeImpl is working ok. * * * @author James Dempsey <jdempsey@users.sourceforge.net> */ public class CharacterLevelsFacadeImplTest extends AbstractJunit4CharacterTestCase { private UIDelegate delegate; private TodoManager todoManager; private DataSetFacade dataSetFacade; private static Skill spellcraftSkill; private static Skill climbSkill; private static Skill umdSkill; private static PCClass fighterClass; private static PCClass wizardClass; @Before public void before() throws Exception { delegate = new MockUIDelegate(); todoManager = new TodoManager(); final GameMode gameMode = SettingsHandler.getGame(); dataSetFacade = new MockDataSetFacade(gameMode); } @Override public void setUp() throws Exception { super.setUp(); Globals.getContext().resolvePostDeferredTokens(); Globals.getContext().loadCampaignFacets(); } @Override protected void additionalSetUp() throws Exception { spellcraftSkill = TestHelper.makeSkill("Spellcraft", "Base", intel, false, SkillArmorCheck.NONE); climbSkill = TestHelper.makeSkill("Climb", "Base", str, false, SkillArmorCheck.YES); umdSkill = TestHelper.makeSkill("Use Magic Device", "Base", str, false, SkillArmorCheck.NONE); umdSkill.put(ObjectKey.EXCLUSIVE, true); LoadContext context = Globals.getContext(); CampaignSourceEntry source = new CampaignSourceEntry(new Campaign(), new URI("file:/" + getClass().getName() + ".java")); fighterClass = TestHelper.makeClass("Fighter"); String classPCCText = "CLASS:Fighter HD:10 TYPE:Base.PC ABB:Ftr\n" + "CLASS:Fighter STARTSKILLPTS:2 CSKILL:KEY_Climb|KEY_Use Magic Device"; fighterClass = TestHelper.parsePCClassText(classPCCText, source); context.getReferenceContext().importObject(fighterClass); classPCCText = "CLASS:Wizard HD:4 TYPE:Base.PC ABB:Wiz\n" + "CLASS:Wizard STARTSKILLPTS:4 CSKILL:KEY_Spellcraft"; wizardClass = TestHelper.parsePCClassText(classPCCText, source); context.getReferenceContext().importObject(wizardClass); context.commit(); context.getReferenceContext().buildDerivedObjects(); context.resolveDeferredTokens(); } /** * Test method for {@link pcgen.gui2.facade.CharacterLevelsFacadeImpl#findNextLevelForSkill(pcgen.core.facade.SkillFacade, pcgen.core.facade.CharacterLevelFacade, float)} * to check level selection where class and cross-class skills cost the * same for all classes. */ @Test public void testFindNextLevelForSkillAllSameCost() { // Set game mode skill cost to be the same for class and cross-slass setGameSkillRankData(false); PlayerCharacter pc = getCharacter(); pc.incrementClassLevel(4, fighterClass); pc.incrementClassLevel(4, wizardClass); pc.incrementClassLevel(3, fighterClass); pc.incrementClassLevel(3, wizardClass); pc.incrementClassLevel(3, fighterClass); pc.incrementClassLevel(3, wizardClass); assertTrue("Climb should be class for fighter", pc.isClassSkill(fighterClass, climbSkill)); assertFalse("Spellcraft should not be class for fighter", pc.isClassSkill(fighterClass, spellcraftSkill)); assertTrue("UMD should be class for fighter", pc.isClassSkill(fighterClass, umdSkill)); assertFalse("Climb should not be class for wizard", pc.isClassSkill(wizardClass, climbSkill)); assertTrue("Spellcraft should be class for wizard", pc.isClassSkill(wizardClass, spellcraftSkill)); assertFalse("UMD should be class for fighter", pc.isClassSkill(wizardClass, umdSkill)); CharacterLevelsFacadeImpl charLvlsFI = new CharacterLevelsFacadeImpl(pc, delegate, todoManager, dataSetFacade, null); // Rules for finding the next level to spend a point: // 1. Selected level, if points available and not exceeding max ranks. assertEquals( "Level for spellcraft", charLvlsFI.getElementAt(2), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(2), 1.0f)); assertEquals( "Level for spellcraft", charLvlsFI.getElementAt(2), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(2), 2.0f)); assertEquals( "Level for spellcraft", charLvlsFI.getElementAt(2), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(2), 3.0f)); assertEquals( "Level for climb", charLvlsFI.getElementAt(2), charLvlsFI.findNextLevelForSkill(climbSkill, charLvlsFI.getElementAt(2), 3.0f)); // 2. Scan forward from selected level for first level with points spare // where the rank is not above max rank for the level and the cost is // equal to class cost assertEquals( "Level for spellcraft - scan forward", charLvlsFI.getElementAt(3), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(2), 4.0f)); assertEquals( "Level for rank 20 spellcraft - scan forward", charLvlsFI.getElementAt(19), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(2), 20.0f)); assertEquals( "Level for rank 20 climb - scan forward", charLvlsFI.getElementAt(19), charLvlsFI.findNextLevelForSkill(climbSkill, charLvlsFI.getElementAt(2), 20.0f)); charLvlsFI.getLevelInfo(charLvlsFI.getElementAt(3)).setSkillPointsRemaining(0); charLvlsFI.getLevelInfo(charLvlsFI.getElementAt(4)).setSkillPointsRemaining(0); assertEquals( "Level for rank 4 spellcraft - scan forward past spent levels", charLvlsFI.getElementAt(5), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(2), 4.0f)); // 3. Scan from level 1 for first level with points spare where the rank // is not above max rank for the level and the cost is equal to class cost for (int i = 5; i < 20; i++) { charLvlsFI.getLevelInfo(charLvlsFI.getElementAt(i)).setSkillPointsRemaining(0); } assertEquals( "Level for rank 2 spellcraft - scan from lvl 1 after selected or higher all spent", charLvlsFI.getElementAt(1), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(3), 2.0f)); // 4. Scan forward from selected level for first level with points spare // where the rank is not above max rank for the level and the skill is // not prohibited for (int i = 5; i < 10; i++) { charLvlsFI.getLevelInfo(charLvlsFI.getElementAt(i)).setSkillPointsRemaining(4); } assertEquals( "Level for 1 rank umd - scan forward from lvl 5 for non prohibited class", charLvlsFI.getElementAt(8), charLvlsFI.findNextLevelForSkill(umdSkill, charLvlsFI.getElementAt(5), 1.0f)); // 5. Scan from level 1 for first level with points spare where the rank // is not above max rank for the level and the skill is not prohibited assertEquals( "Level for 1 rank umd - scan from lvl 1 as higher all spent for non prohibited class", charLvlsFI.getElementAt(0), charLvlsFI.findNextLevelForSkill(CharacterLevelsFacadeImplTest.umdSkill, charLvlsFI.getElementAt(17), 1.0f)); // 6. Advise that the skill cannot be advanced. assertNull("Level for rank 21 spellcraft - cannot be advanced", charLvlsFI.findNextLevelForSkill(CharacterLevelsFacadeImplTest.spellcraftSkill, charLvlsFI.getElementAt(2), 21.0f)); } /** * Test method for {@link pcgen.gui2.facade.CharacterLevelsFacadeImpl#findNextLevelForSkill(pcgen.core.facade.SkillFacade, pcgen.core.facade.CharacterLevelFacade, float)} * to check level selection where cross-class skills cost 2 points and class * skills cost only 1. */ @Test public void testFindNextLevelForSkillCrossClass2Points() { // Set game mode skill cost to be double for cross-class setGameSkillRankData(true); PlayerCharacter pc = getCharacter(); pc.incrementClassLevel(4, fighterClass); pc.incrementClassLevel(4, wizardClass); pc.incrementClassLevel(3, fighterClass); pc.incrementClassLevel(3, wizardClass); pc.incrementClassLevel(3, fighterClass); pc.incrementClassLevel(3, wizardClass); assertTrue("Climb should be class for fighter", pc.isClassSkill(fighterClass, climbSkill)); assertFalse("Spellcraft should not be class for fighter", pc.isClassSkill(fighterClass, spellcraftSkill)); assertTrue("UMD should be class for fighter", pc.isClassSkill(fighterClass, umdSkill)); assertFalse("Climb should not be class for wizard", pc.isClassSkill(wizardClass, climbSkill)); assertTrue("Spellcraft should be class for wizard", pc.isClassSkill(wizardClass, spellcraftSkill)); assertFalse("UMD should be class for wizard", pc.isClassSkill(wizardClass, umdSkill)); CharacterLevelsFacadeImpl charLvlsFI = new CharacterLevelsFacadeImpl(pc, delegate, todoManager, dataSetFacade, null); // Rules for finding the next level to spend a point: // 1. Selected level, if points available and not exceeding max ranks. assertEquals( "Level for 1 rank spellcraft", charLvlsFI.getElementAt(2), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(2), 1.0f)); assertEquals( "Level for 7 ranks climb", charLvlsFI.getElementAt(3), charLvlsFI.findNextLevelForSkill(climbSkill, charLvlsFI.getElementAt(3), 7.0f)); assertEquals( "Level for 7.5 ranks climb", charLvlsFI.getElementAt(4), charLvlsFI.findNextLevelForSkill(climbSkill, charLvlsFI.getElementAt(4), 7.5f)); // 2. Scan forward from selected level for first level with points spare // where the rank is not above max rank for the level and the cost is // equal to class cost assertEquals( "Level for 3 ranks spellcraft", charLvlsFI.getElementAt(4), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(1), 3.0f)); assertEquals( "Level for 9 ranks spellcraft", charLvlsFI.getElementAt(5), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(1), 9.0f)); // 3. Scan from level 1 for first level with points spare where the rank // is not above max rank for the level and the cost is equal to class cost //Note: with the "once class always class" rule for max ranks, this // situation can no longer occur. // assertEquals( // "Level for 12 ranks climb", // charLvlsFI.getElementAt(8), // charLvlsFI.findNextLevelForSkill(climbSkill, // charLvlsFI.getElementAt(18), 12.0f)); // 4. Scan forward from selected level for first level with points spare // where the rank is not above max rank for the level and the skill is // not prohibited for (int i = 0; i < 20; i++) { PCLevelInfo pcLevelInfo = charLvlsFI.getLevelInfo(charLvlsFI.getElementAt(i)); if (pcLevelInfo.getClassKeyName().equals(wizardClass.getKeyName())) { pcLevelInfo.setSkillPointsRemaining(0); } } assertEquals( "Level for 5 ranks spellcraft", charLvlsFI.getElementAt(8), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(5), 5.0f)); // 5. Scan from level 1 for first level with points spare where the rank // is not above max rank for the level and the skill is not prohibited assertEquals( "Level for 3 ranks spellcraft", charLvlsFI.getElementAt(2), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(19), 3.0f)); // 6. Advise that the skill cannot be advanced. charLvlsFI.getLevelInfo(charLvlsFI.getElementAt(18)).setSkillPointsRemaining(4); charLvlsFI.getLevelInfo(charLvlsFI.getElementAt(19)).setSkillPointsRemaining(4); assertNull("Level for rank 23.5 climb - cannot be advanced", charLvlsFI.findNextLevelForSkill(climbSkill, charLvlsFI.getElementAt(2), 23.5f)); } /** * Test method for {@link pcgen.gui2.facade.CharacterLevelsFacadeImpl#findNextLevelForSkill(pcgen.core.facade.SkillFacade, pcgen.core.facade.CharacterLevelFacade, float)} * to check level selection where cross-class skills cost 2 points and class * skills cost only 1. */ @Test public void testFindNextLevelForSkill35eMultiClass() { // Set game mode skill cost to be the double for cross-class setGameSkillRankData(true); PlayerCharacter pc = getCharacter(); pc.incrementClassLevel(1, wizardClass); pc.incrementClassLevel(1, fighterClass); assertTrue("Climb should be class for fighter", pc.isClassSkill(fighterClass, climbSkill)); assertFalse("Spellcraft should not be class for fighter", pc.isClassSkill(fighterClass, spellcraftSkill)); assertFalse("Climb should not be class for wizard", pc.isClassSkill(wizardClass, climbSkill)); assertTrue("Spellcraft should be class for wizard", pc.isClassSkill(wizardClass, spellcraftSkill)); CharacterLevelsFacadeImpl charLvlsFI = new CharacterLevelsFacadeImpl(pc, delegate, todoManager, dataSetFacade, null); assertEquals( "Level for 1 ranks spellcraft", charLvlsFI.getElementAt(0), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(0), 1.0f)); assertEquals( "Level for 4 ranks spellcraft", charLvlsFI.getElementAt(0), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(0), 4.0f)); assertEquals( "Level for 5 ranks spellcraft", charLvlsFI.getElementAt(1), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(0), 5.0f)); } /** * Test method for {@link pcgen.gui2.facade.CharacterLevelsFacadeImpl#findNextLevelForSkill(pcgen.core.facade.SkillFacade, pcgen.core.facade.CharacterLevelFacade, float)} * to check level selection for removing a skill rank where class and cross-class skills cost the * same for all classes. */ @Test public void testFindNextLevelForSkillAllSameCostRemove() { // Set game mode skill cost to be the same for class and cross-slass setGameSkillRankData(false); PlayerCharacter pc = getCharacter(); pc.incrementClassLevel(4, fighterClass); pc.incrementClassLevel(4, wizardClass); pc.incrementClassLevel(3, fighterClass); pc.incrementClassLevel(3, wizardClass); pc.incrementClassLevel(3, fighterClass); pc.incrementClassLevel(3, wizardClass); assertTrue("Climb should be class for fighter", pc.isClassSkill(fighterClass, climbSkill)); assertFalse("Spellcraft should not be class for fighter", pc.isClassSkill(fighterClass, spellcraftSkill)); assertTrue("UMD should be class for fighter", pc.isClassSkill(fighterClass, umdSkill)); assertFalse("Climb should not be class for wizard", pc.isClassSkill(wizardClass, climbSkill)); assertTrue("Spellcraft should be class for wizard", pc.isClassSkill(wizardClass, spellcraftSkill)); assertFalse("UMD should be class for fighter", pc.isClassSkill(wizardClass, umdSkill)); CharacterLevelsFacadeImpl charLvlsFI = new CharacterLevelsFacadeImpl(pc, delegate, todoManager, dataSetFacade, null); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(0), climbSkill, 1); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(4), spellcraftSkill, 1); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(0), umdSkill, 1); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(1), umdSkill, 1); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(2), umdSkill, 1); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(3), umdSkill, 1); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(8), umdSkill, 2); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(9), umdSkill, 2); charLvlsFI.investSkillPoints(charLvlsFI.getElementAt(10), umdSkill, 2); // Rules for finding the level to remove a skill rank from: // 1. Selected level (if class had purchased a rank, level has spent // points and current rank is not above maxranks) assertEquals( "Selected level for removing rank of spellcraft", charLvlsFI.getElementAt(4), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(4), 0.0f)); assertEquals( "Selected level for removing rank of climb", charLvlsFI.getElementAt(0), charLvlsFI.findNextLevelForSkill(climbSkill, charLvlsFI.getElementAt(0), 0.0f)); // 2. Scan from level 1 for first level of the same class as currently // selected level in which the rank to be removed is below max ranks and // is a class that has bought ranks in the class assertEquals( "Wizard level for removing rank of spellcraft", charLvlsFI.getElementAt(4), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(5), 0.0f)); assertEquals( "Fighter level for removing rank of climb", charLvlsFI.getElementAt(0), charLvlsFI.findNextLevelForSkill(climbSkill, charLvlsFI.getElementAt(16), 0.0f)); assertEquals( "Fighter level for removing ranks 9, 10 of UMD", charLvlsFI.getElementAt(9), charLvlsFI.findNextLevelForSkill(umdSkill, charLvlsFI.getElementAt(16), 8.0f)); // 3. Scan from level 1 for first level of any class in which the rank // to be removed is below max ranks and is a class that has bought // ranks in the class assertEquals( "Any level for removing rank of spellcraft", charLvlsFI.getElementAt(4), charLvlsFI.findNextLevelForSkill(spellcraftSkill, charLvlsFI.getElementAt(16), 0.0f)); } private void setGameSkillRankData(boolean crossClassCostTwo) { GameMode game = SettingsHandler.getGame(); final XPTable xpTable = game.getLevelInfo(game.getDefaultXPTableName()); LevelInfo levelInfo = xpTable.getLevelInfo(1); levelInfo.setLevelString("LEVEL"); if (crossClassCostTwo) { levelInfo.setMaxClassSkillString("LEVEL+3"); levelInfo.setMaxCrossClassSkillString("(LEVEL+3)/2"); game.setSkillCost_CrossClass(2); } else { levelInfo.setMaxClassSkillString("LEVEL"); levelInfo.setMaxCrossClassSkillString("LEVEL"); game.setSkillCost_CrossClass(1); } } }