/* * Copyright 2010 (C) James Dempsey * * 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 03/06/2010 12:09:38 PM * * $Id$ */ package pcgen.gui2.facade; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import pcgen.cdom.base.Constants; import pcgen.cdom.enumeration.CharID; import pcgen.cdom.enumeration.SkillCost; import pcgen.cdom.facet.BonusChangeFacet; import pcgen.cdom.facet.BonusChangeFacet.BonusChangeEvent; import pcgen.cdom.facet.BonusChangeFacet.BonusChangeListener; import pcgen.cdom.facet.FacetLibrary; import pcgen.cdom.facet.event.DataFacetChangeEvent; import pcgen.cdom.facet.event.DataFacetChangeListener; import pcgen.cdom.facet.model.SkillFacet; import pcgen.cdom.inst.PCClassLevel; import pcgen.core.Globals; import pcgen.core.PCClass; import pcgen.core.PlayerCharacter; import pcgen.core.Skill; import pcgen.core.SkillUtilities; import pcgen.core.analysis.ChooseActivation; import pcgen.core.analysis.SkillModifier; import pcgen.core.analysis.SkillRankControl; import pcgen.core.display.CharacterDisplay; import pcgen.core.display.SkillDisplay; import pcgen.facade.core.CharacterLevelFacade; import pcgen.facade.core.CharacterLevelsFacade; import pcgen.facade.core.ClassFacade; import pcgen.facade.core.DataSetFacade; import pcgen.facade.core.SkillFacade; import pcgen.facade.core.UIDelegate; import pcgen.facade.util.AbstractListFacade; import pcgen.core.pclevelinfo.PCLevelInfo; import pcgen.system.LanguageBundle; import pcgen.util.Logging; import pcgen.util.enumeration.Tab; /** * The Class {@code CharacterLevelsFacadeImpl} is an implementation of * the CharacterLevelsFacade interface for the new user interface. It allows * the user interface to work with the class levels of a character. * * <br> * * @author James Dempsey <jdempsey@users.sourceforge.net> */ public class CharacterLevelsFacadeImpl extends AbstractListFacade<CharacterLevelFacade> implements CharacterLevelsFacade, DataFacetChangeListener<CharID, Skill>, BonusChangeListener { private PlayerCharacter theCharacter; private CharacterDisplay charDisplay; private UIDelegate delegate; private List<ClassFacade> classLevels; private List<CharacterLevelFacade> charLevels; private final TodoManager todoManager; private CharID charID; private final DataSetFacade dataSetFacade; private CharacterFacadeImpl characterFacadeImpl; /** * Create a new CharacterLevelsFacadeImpl instance for a character. * @param pc The character we are creating the instance for * @param delegate The user interface delegate that can do dialogs and choosers for us. * @param todoManager The user tasks tracker. * @param dataSetFacade The datasets that the character is using. * @param characterFacadeImpl The facade managing the character. */ CharacterLevelsFacadeImpl(PlayerCharacter pc, UIDelegate delegate, TodoManager todoManager, DataSetFacade dataSetFacade, CharacterFacadeImpl characterFacadeImpl) { this.theCharacter = pc; this.characterFacadeImpl = characterFacadeImpl; this.charDisplay = pc.getDisplay(); this.delegate = delegate; this.todoManager = todoManager; this.dataSetFacade = dataSetFacade; initForCharacter(); } /** * Tidy up character listeners when closing the character. */ protected void closeCharacter() { SkillFacet skillFacet = FacetLibrary.getFacet(SkillFacet.class); skillFacet.removeDataFacetChangeListener(this); BonusChangeFacet bcf = FacetLibrary.getFacet(BonusChangeFacet.class); for (SkillFacade skillFacade : dataSetFacade.getSkills()) { bcf.removeBonusChangeListener(this, "SKILLRANK", skillFacade .getKeyName().toUpperCase()); } theCharacter = null; charDisplay = null; charID = null; } /** * Initialise the instance for the current character. */ private void initForCharacter() { classLevels = new ArrayList<>(); charLevels = new ArrayList<>(); refreshClassList(); charID = theCharacter.getCharID(); SkillFacet skillFacet = FacetLibrary.getFacet(SkillFacet.class); skillFacet.addDataFacetChangeListener(this); BonusChangeFacet bcf = FacetLibrary.getFacet(BonusChangeFacet.class); for (SkillFacade skillFacade : dataSetFacade.getSkills()) { bcf.addBonusChangeListener(this, "SKILLRANK", skillFacade .getKeyName().toUpperCase()); } } @Override public CharacterLevelFacade getElementAt(int index) { if (index < 0 || index >= charLevels.size()) { return null; } return charLevels.get(index); } @Override public int getSize() { return charLevels.size(); } private void clearContents() { charLevels.clear(); fireElementsChanged(this); } private void addElement(CharacterLevelFacadeImpl levelFI) { int index = charLevels.size(); charLevels.add(levelFI); fireElementAdded(this, levelFI, index); } private void removeElement(int i) { fireElementRemoved(this, charLevels.remove(i), i); } /** * Update the list of class levels from scratch to match the current * state of the character. */ private void refreshClassList() { List<PCClass> newClasses = charDisplay.getClassList(); Collection<PCLevelInfo> levelInfo = charDisplay.getLevelInfo(); Map<String, Integer> levelCount = new HashMap<>(); Map<String, PCClass> classMap = new HashMap<>(); for (PCClass pcClass : newClasses) { levelCount.put(pcClass.getKeyName(), 1); classMap.put(pcClass.getKeyName(), pcClass); } classLevels.clear(); clearContents(); for (PCLevelInfo lvlInfo : levelInfo) { final String classKeyName = lvlInfo.getClassKeyName(); PCClass currClass = classMap.get(classKeyName); if (currClass == null) { Logging.errorPrint("No PCClass found for '" + classKeyName + "' in character's class list: " + newClasses); return; } int clsLvlNum = levelCount.get(classKeyName); levelCount.put(classKeyName, clsLvlNum + 1); classLevels.add(currClass); CharacterLevelFacadeImpl levelFI = new CharacterLevelFacadeImpl(currClass, classLevels.size()); addElement(levelFI); //PCClassLevel classLevel = currClass.getClassLevel(clsLvlNum); } updateSkillsTodo(); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getClassTaken(int) */ @Override public ClassFacade getClassTaken(CharacterLevelFacade level) { if (level == null || !(level instanceof CharacterLevelFacadeImpl)) { return null; } return classLevels.get(getLevelIndex(level)); } private PCClassLevel getClassLevel(CharacterLevelFacade level) { if (level == null || !(level instanceof CharacterLevelFacadeImpl)) { return null; } CharacterLevelFacadeImpl levelImpl = (CharacterLevelFacadeImpl) level; int lvlIdx = levelImpl.getCharacterLevel() - 1; final String classKeyName = charDisplay.getLevelInfoClassKeyName(lvlIdx); PCClass aClass = theCharacter.getClassKeyed(classKeyName); if (aClass != null) { final int clsLvl = charDisplay.getLevelInfoClassLevel(lvlIdx); PCClassLevel pcl = charDisplay.getActiveClassLevel(aClass, clsLvl-1); return pcl; } return null; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getHPGained(int) */ @Override public int getHPGained(CharacterLevelFacade level) { int numHp = getHPRolled(level); numHp += (int) charDisplay.getStatBonusTo("HP", "BONUS"); if (numHp < 1) { numHp = 1; } return numHp; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getHPRolled(int) */ @Override public int getHPRolled(CharacterLevelFacade level) { PCClassLevel classLevel = getClassLevel(level); if (classLevel == null) { return 0; } return charDisplay.getHP(classLevel); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#setHPRolled(int, int) */ @Override public void setHPRolled(CharacterLevelFacade level, int hp) { PCClassLevel classLevel = getClassLevel(level); if (classLevel != null) { theCharacter.setHP(classLevel, hp); fireHitPointEvent(this, getLevelIndex(level), false); } } PCLevelInfo getLevelInfo(CharacterLevelFacade level) { if (level == null || !(level instanceof CharacterLevelFacadeImpl)) { return null; } return charDisplay.getLevelInfo(getLevelIndex(level)); } private int getLevelIndex(CharacterLevelFacade level) { CharacterLevelFacadeImpl levelImpl = (CharacterLevelFacadeImpl) level; if (levelImpl == null) { return 0; } return levelImpl.getCharacterLevel()-1; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getGainedSkillPoints(int) */ @Override public int getGainedSkillPoints(CharacterLevelFacade level) { PCLevelInfo classLevel = getLevelInfo(level); if (classLevel == null) { return 0; } return classLevel.getSkillPointsGained(theCharacter); } @Override public float getMaxRanks(CharacterLevelFacade level, SkillCost cost, boolean isClassForMaxRanks) { if (cost == null || level == null || !(level instanceof CharacterLevelFacadeImpl)) { return 0.0f; } if (cost.getCost() == 0) { return Float.NaN; } SkillCost costForMaxRanks = isClassForMaxRanks ? SkillCost.CLASS : cost; CharacterLevelFacadeImpl levelImpl = (CharacterLevelFacadeImpl) level; if (costForMaxRanks == SkillCost.CLASS) { return SkillUtilities.maxClassSkillForLevel( levelImpl.getCharacterLevel(), theCharacter).floatValue(); } else if (costForMaxRanks == SkillCost.CROSS_CLASS) { return SkillUtilities.maxCrossClassSkillForLevel( levelImpl.getCharacterLevel(), theCharacter).floatValue(); } else if (costForMaxRanks == SkillCost.EXCLUSIVE) { // We can't test if the skill in question is valid for all classes // So just assume it is for the time being. A check on the total // levels for the skill itself will need to be made elsewhere return SkillUtilities.maxClassSkillForLevel( levelImpl.getCharacterLevel(), theCharacter).floatValue(); } return Float.NaN; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getRankCost(int, pcgen.cdom.enumeration.SkillCost) */ @Override public int getRankCost(CharacterLevelFacade level, SkillCost cost) { return cost.getCost(); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getSkillCost(int, pcgen.core.facade.SkillFacade) */ @Override public SkillCost getSkillCost(CharacterLevelFacade level, SkillFacade skill) { if (level != null && level instanceof CharacterLevelFacadeImpl && charDisplay != null) { final String classKeyName = charDisplay.getLevelInfoClassKeyName(getLevelIndex(level)); PCClass aClass = theCharacter.getClassKeyed(classKeyName); if (skill instanceof Skill) { return theCharacter.getSkillCostForClass((Skill) skill, aClass); } } return null; } @Override public boolean isClassSkillForMaxRanks(CharacterLevelFacade level, SkillFacade skill) { for (int i = 0; i < charLevels.size(); i++) { CharacterLevelFacade testLevel = getElementAt(i); if (getSkillCost(testLevel, skill) == SkillCost.CLASS) { return true; } if (testLevel == level) { // Break as we have reached the level to be checked and it hasn't been class yet return false; } } return false; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getSkillModifier(int, pcgen.core.facade.SkillFacade) */ @Override public int getSkillModifier(CharacterLevelFacade level, SkillFacade skill) { if (skill instanceof Skill) { return SkillModifier.modifier((Skill) skill, theCharacter); } return 0; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getSkillRanks(int, pcgen.core.facade.SkillFacade) */ @Override public float getSkillRanks(CharacterLevelFacade level, SkillFacade skill) { // TODO Ranks aren't stored by level - have compromised by returning the total. Further discussion needed if (skill instanceof Skill) { return SkillRankControl.getTotalRank(theCharacter, (Skill) skill); } return 0; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getSkillTotal(int, pcgen.core.facade.SkillFacade) */ @Override public int getSkillTotal(CharacterLevelFacade level, SkillFacade skill) { // TODO Ranks aren't stored by level - have compromised by returning the total. Further discussion needed if (skill instanceof Skill) { Float ranks = SkillRankControl.getTotalRank(theCharacter, (Skill) skill); Integer mods = SkillModifier.modifier((Skill) skill, theCharacter); return mods.intValue() + ranks.intValue(); } return 0; } @Override public SkillBreakdown getSkillBreakdown(CharacterLevelFacade level, SkillFacade skill) { SkillBreakdown sb = new SkillBreakdown(); // TODO Ranks aren't stored by level - have compromised by returning the total. Further discussion needed if (skill instanceof Skill) { sb.ranks = SkillRankControl.getTotalRank(theCharacter, (Skill) skill); sb.modifier = SkillModifier.modifier((Skill) skill, theCharacter); sb.total = sb.modifier + (int) sb.ranks; } return sb; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getSpentSkillPoints(int) */ @Override public int getSpentSkillPoints(CharacterLevelFacade level) { PCLevelInfo classLevel = getLevelInfo(level); if (classLevel == null) { return 0; } return classLevel.getSkillPointsGained(theCharacter) - classLevel.getSkillPointsRemaining(); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#getRemainingSkillPoints(int) */ @Override public int getRemainingSkillPoints(CharacterLevelFacade level) { PCLevelInfo classLevel = getLevelInfo(level); if (classLevel == null) { return 0; } return classLevel.getSkillPointsRemaining(); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#investSkillPoints(int, pcgen.core.facade.SkillFacade, int) */ @Override public boolean investSkillPoints(CharacterLevelFacade level, SkillFacade skill, int points) { if (points == 0 || level == null || !(level instanceof CharacterLevelFacadeImpl)) { Logging.errorPrint("Invalid request to investSkillPoints in " + skill + ". Points: " + points + " level: " + level); return false; } PCLevelInfo classLevel = getLevelInfo(level); int skillPool; if (Globals.getGameModeHasPointPool()) { skillPool = theCharacter.getSkillPoints(); } else { skillPool = classLevel.getSkillPointsRemaining(); if ((points < 0) && (((skillPool - points) > classLevel .getSkillPointsGained(theCharacter)) || !classHasRanksIn(skill, ((CharacterLevelFacadeImpl) level).getSelectedClass()))) { level = findLevelWithSpentSkillPoints(points, skill); if (level == null) { delegate.showInfoMessage(Constants.APPLICATION_NAME, LanguageBundle.getFormattedString( "in_iskErr_message_05", skill)); return false; } classLevel = getLevelInfo(level); skillPool = classLevel.getSkillPointsRemaining(); } } if ((points > 0) && (points > skillPool)) { delegate.showInfoMessage(Constants.APPLICATION_NAME, LanguageBundle .getFormattedString("in_iskErr_message_04a", String .valueOf(skillPool))); return false; } SkillCost sc = getSkillCost(level, skill); if (sc == null) { Logging.errorPrint("Failed to get skillcost for skill " + skill + ". Could not process request to invest " + points + " in the skill"); return false; } if (sc.equals(SkillCost.EXCLUSIVE)) { delegate.showInfoMessage(Constants.APPLICATION_NAME, LanguageBundle .getString("in_iskErr_message_06")); return false; } final double cost = sc.getCost(); double rank = points / cost; Skill aSkill = (Skill) skill; boolean hasSkill = charDisplay.hasSkill(aSkill); if (!hasSkill) { SkillDisplay.updateSkillsOutputOrder(theCharacter, aSkill); } final String classKeyName = charDisplay.getLevelInfoClassKeyName(getLevelIndex(level)); PCClass aClass = theCharacter.getClassKeyed(classKeyName); String errMessage = SkillRankControl.modRanks(rank, aClass, false, theCharacter, aSkill); if ("".equals(errMessage)) //$NON-NLS-1$ { classLevel.setSkillPointsRemaining(skillPool - points); } if (ChooseActivation.hasNewChooseToken(aSkill) && characterFacadeImpl != null) { characterFacadeImpl.postLevellingUpdates(); } if (!errMessage.isEmpty()) { delegate.showInfoMessage(Constants.APPLICATION_NAME, errMessage); return false; } updateSkillsTodo(); fireSkillPointEvent(this, getLevelIndex(level), false); fireSkillBonusEvent(this, getLevelIndex(level), false); return true; } @Override public CharacterLevelFacade findNextLevelForSkill(SkillFacade skill, CharacterLevelFacade baseLevel, float newRank) { Skill aSkill = (Skill) skill; SkillCost skillCost = getSkillCost(baseLevel, aSkill); float maxRanks = getMaxRanks(baseLevel, skillCost, isClassSkillForMaxRanks(baseLevel, aSkill)); float currRank = SkillRankControl.getTotalRank(theCharacter, aSkill); if (newRank < currRank) { // 1. Selected level (if class had purchased a rank and is not above max ranks) if (classHasRanksIn(skill, ((CharacterLevelFacadeImpl) baseLevel).getSelectedClass()) && !Float.isNaN(maxRanks) && maxRanks >= currRank && getSpentSkillPoints(baseLevel) > 0) { return baseLevel; } // 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 CharacterLevelFacade levelToRefundSkill = scanForLevelToRefundSkill(aSkill, currRank, (PCClass) getClassTaken(baseLevel)); if (levelToRefundSkill != null) { return levelToRefundSkill; } // 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 levelToRefundSkill = scanForLevelToRefundSkill(aSkill, currRank, null); return levelToRefundSkill; } // Check if current level ok if (!Float.isNaN(maxRanks) && maxRanks >= newRank && getRemainingSkillPoints(baseLevel) > 0) { return baseLevel; } // Check for class cost on this level or higher int baseLevelIndex = getLevelIndex(baseLevel); CharacterLevelFacade levelToBuySkill = scanForwardforLevelToBuySkill(aSkill, newRank, baseLevelIndex, SkillCost.CLASS); if (levelToBuySkill != null) { return levelToBuySkill; } // Check for class cost on any level levelToBuySkill = scanForwardforLevelToBuySkill(aSkill, newRank, 0, SkillCost.CLASS); if (levelToBuySkill != null) { return levelToBuySkill; } // Check for any cost on this level or higher levelToBuySkill = scanForwardforLevelToBuySkill(aSkill, newRank, baseLevelIndex, null); if (levelToBuySkill != null) { return levelToBuySkill; } // Check for any cost on any level levelToBuySkill = scanForwardforLevelToBuySkill(aSkill, newRank, 0, null); return levelToBuySkill; } private CharacterLevelFacade scanForwardforLevelToBuySkill(Skill aSkill, float testRank, int baseLevelIndex, SkillCost costToMatch) { for (int i = baseLevelIndex; i < charLevels.size(); i++) { CharacterLevelFacade testLevel = getElementAt(i); //Logging.errorPrint("Checking " + testLevel); if (getRemainingSkillPoints(testLevel) <= 0) { //Logging.errorPrint("Skipping level " + testLevel + " as it does not have points left."); continue; } SkillCost skillCost = getSkillCost(testLevel, aSkill); if (costToMatch != null && skillCost.getCost() != costToMatch.getCost()) { //Logging.errorPrint("Skipping level " + testLevel + " as it is not the same cost as " + costToMatch); continue; } float maxRanks = getMaxRanks(testLevel, skillCost, isClassSkillForMaxRanks(testLevel, aSkill)); if (!Float.isNaN(maxRanks) && maxRanks >= testRank) { //Logging.errorPrint("Selected level " + testLevel); return testLevel; } //Logging.errorPrint("Skipping level " + testLevel + " as skill is above max ranks"); } return null; } private CharacterLevelFacade scanForLevelToRefundSkill(Skill aSkill, float testRank, PCClass classToMatch) { for (int i = 0; i < charLevels.size(); i++) { CharacterLevelFacade testLevel = getElementAt(i); //Logging.errorPrint("Checking " + testLevel); String lvlClassName = getLevelInfo(testLevel).getClassKeyName(); if (classToMatch != null && !classToMatch.getKeyName().equals(lvlClassName)) { //Logging.errorPrint("Skipping level " + testLevel + " as it is not the same class as " + classToMatch); continue; } if (!classHasRanksIn(aSkill, ((CharacterLevelFacadeImpl) testLevel).getSelectedClass())) { //Logging.errorPrint("Skipping level " + testLevel + " as it does not have ranks in " + aSkill); continue; } if (getSpentSkillPoints(testLevel) <= 0) { //Logging.errorPrint("Skipping level " + testLevel + " as it does not have spent points."); continue; } SkillCost skillCost = getSkillCost(testLevel, aSkill); float maxRanks = getMaxRanks(testLevel, skillCost, isClassSkillForMaxRanks(testLevel, aSkill)); if (!Float.isNaN(maxRanks) && maxRanks >= testRank) { //Logging.errorPrint("Selected level " + testLevel); return testLevel; } //Logging.errorPrint("Skipping level " + testLevel + " as skill is above max ranks"); } return null; } /** * Find a level which has a certain number of points spent. * @param points The negative number of points spent required. * @param skill * @return The level with spent points, or null if none match */ private CharacterLevelFacade findLevelWithSpentSkillPoints(int points, SkillFacade skill) { for (int i = charLevels.size()-1; i>= 0; i--) { CharacterLevelFacadeImpl levelFacade = (CharacterLevelFacadeImpl) charLevels.get(i); PCLevelInfo levelInfo = getLevelInfo(levelFacade); if (levelInfo.getSkillPointsRemaining() - points <= levelInfo.getSkillPointsGained(theCharacter)) { if (classHasRanksIn(skill, levelFacade.getSelectedClass())) { return levelFacade; } } } return null; } /** * Identify if the class has ranks in the skill. * @param skill The skill to be checked for. * @param pcClass The class being checked. * @return true if the character took ranks of the skill in the class. */ private boolean classHasRanksIn(SkillFacade skill, ClassFacade pcClass) { Double rank = theCharacter.getSkillRankForClass((Skill) skill, (PCClass) pcClass); return (rank != null) && (rank > 0.0d); } protected void updateSkillsTodo() { int remainingPoints = calcRemainingSkillPoints(); if (remainingPoints < 0) { todoManager.addTodo(new TodoFacadeImpl(Tab.SKILLS, "Skills", "in_iskTodoTooMany", 1)); todoManager.removeTodo("in_iskTodoRemain"); } else if (remainingPoints > 0) { todoManager.addTodo(new TodoFacadeImpl(Tab.SKILLS, "Skills", "in_iskTodoRemain", 1)); todoManager.removeTodo("in_iskTodoTooMany"); } else { todoManager.removeTodo("in_iskTodoRemain"); todoManager.removeTodo("in_iskTodoTooMany"); } } /** * @return The total of each level's remaining skill points. */ private int calcRemainingSkillPoints() { int numRemaining = 0; for (CharacterLevelFacade clf : charLevels) { numRemaining += getRemainingSkillPoints(clf); } return numRemaining; } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#setGainedSkillPoints(int, int) */ @Override public void setGainedSkillPoints(CharacterLevelFacade level, int points) { int spentSkillPoints = getSpentSkillPoints(level); PCLevelInfo classLevel = getLevelInfo(level); classLevel.setSkillPointsGained(theCharacter, points); classLevel.setSkillPointsRemaining(points-spentSkillPoints); fireSkillPointEvent(this, getLevelIndex(level), false); } // ============== Level Management code ========================= /** * Register the addition of a new level to the character of the * specified class. It is expected that the backing PlayerCharacter object * will be updated by our caller. * @param theClassLevel The class the level is in. */ void addLevelOfClass(CharacterLevelFacadeImpl theClassLevel) { ClassFacade theClass = theClassLevel.getSelectedClass(); classLevels.add(theClass); addElement(theClassLevel); updateSkillsTodo(); } /** * Remove the last level gained. It is expected that the backing * PlayerCharacter object will be updated by our caller. */ void removeLastLevel() { classLevels.remove(classLevels.size()-1); removeElement(getSize()-1); updateSkillsTodo(); } void classListRefreshRequired() { refreshClassList(); fireClassChangedEvent(this, 0, true); fireSkillBonusEvent(this, 0, true); } // ============== Listener Management code ========================= /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#addClasListener(pcgen.core.facade.CharacterLevelsFacade.ClassListener) */ @Override public void addClassListener(ClassListener listener) { listenerList.add(ClassListener.class, listener); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#addHitPointListener(pcgen.core.facade.CharacterLevelsFacade.HitPointListener) */ @Override public void addHitPointListener(HitPointListener listener) { listenerList.add(HitPointListener.class, listener); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#addSkillBonusListener(pcgen.core.facade.CharacterLevelsFacade.SkillBonusListener) */ @Override public void addSkillBonusListener(SkillBonusListener listener) { listenerList.add(SkillBonusListener.class, listener); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#addSkillPointListener(pcgen.core.facade.CharacterLevelsFacade.SkillPointListener) */ @Override public void addSkillPointListener(SkillPointListener listener) { listenerList.add(SkillPointListener.class, listener); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#removeClassListener(pcgen.core.facade.CharacterLevelsFacade.ClassListener) */ @Override public void removeClassListener(ClassListener listener) { listenerList.remove(ClassListener.class, listener); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#removeHitPointListener(pcgen.core.facade.CharacterLevelsFacade.HitPointListener) */ @Override public void removeHitPointListener(HitPointListener listener) { listenerList.remove(HitPointListener.class, listener); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#removeSkillBonusListener(pcgen.core.facade.CharacterLevelsFacade.SkillBonusListener) */ @Override public void removeSkillBonusListener(SkillBonusListener listener) { listenerList.remove(SkillBonusListener.class, listener); } /* (non-Javadoc) * @see pcgen.core.facade.CharacterLevelsFacade#removeSkillPointListener(pcgen.core.facade.CharacterLevelsFacade.SkillPointListener) */ @Override public void removeSkillPointListener(SkillPointListener listener) { listenerList.remove(SkillPointListener.class, listener); } protected void fireClassChangedEvent(Object source, int baseLevelIndex, boolean stacks) { Object[] listeners = listenerList.getListenerList(); CharacterLevelEvent e = null; for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ClassListener.class) { if (e == null) { e = new CharacterLevelEvent(source, baseLevelIndex, stacks); } ((ClassListener) listeners[i + 1]).classChanged(e); } } } protected void fireHitPointEvent(Object source, int baseLevelIndex, boolean stacks) { Object[] listeners = listenerList.getListenerList(); CharacterLevelEvent e = null; for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == HitPointListener.class) { if (e == null) { e = new CharacterLevelEvent(source, baseLevelIndex, stacks); } ((HitPointListener) listeners[i + 1]).hitPointsChanged(e); } } } protected void fireSkillPointEvent(Object source, int baseLevelIndex, boolean stacks) { Object[] listeners = listenerList.getListenerList(); CharacterLevelEvent e = null; for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == SkillPointListener.class) { if (e == null) { e = new CharacterLevelEvent(source, baseLevelIndex, stacks); } ((SkillPointListener) listeners[i + 1]).skillPointsChanged(e); } } } protected void fireSkillBonusEvent(Object source, int baseLevelIndex, boolean stacks) { Object[] listeners = listenerList.getListenerList(); CharacterLevelEvent e = null; for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == SkillBonusListener.class) { if (e == null) { e = new CharacterLevelEvent(source, baseLevelIndex, stacks); } ((SkillBonusListener) listeners[i + 1]).skillBonusChanged(e); } } } @Override public void dataAdded(DataFacetChangeEvent<CharID, Skill> dfce) { if (dfce.getCharID() != charID) { return; } Skill skill = dfce.getCDOMObject(); if (theCharacter.getRank(skill) > 0) { fireSkillBonusEvent(this, 0, true); } } @Override public void dataRemoved(DataFacetChangeEvent<CharID, Skill> dfce) { if (dfce.getCharID() != charID) { return; } //Skill skill = dfce.getCDOMObject(); fireSkillBonusEvent(this, 0, true); } @Override public void bonusChange(BonusChangeEvent bce) { if (bce.getCharID() != charID || bce.getOldVal() == null) { return; } fireSkillBonusEvent(this, 0, true); } }