/* * Copyright (c) Thomas Parker, 2014. * * This program 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 program 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package pcgen.cdom.facet; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import pcgen.cdom.enumeration.CharID; import pcgen.cdom.enumeration.Type; import pcgen.cdom.facet.base.AbstractStorageFacet; import pcgen.core.Globals; import pcgen.core.Skill; /** * SkillRankChangeFacet tracks changes to Bonus SKILLRANK values on a * PlayerCharacter and allows other classes to listen to such changes on a * Player Character. * * @author Thomas Parker (thpr [at] yahoo.com) */ public class BonusSkillRankChangeFacet extends AbstractStorageFacet<CharID> { /** * The SkillRankChangeSupport object that manages the listeners that receive * SkillRankChangeEvents from this SkillRankChangeFacet. */ private final SkillRankChangeSupport support = new SkillRankChangeSupport(); private BonusCheckingFacet bonusCheckingFacet; /** * Performs a check against the previously known values of the bonuses for * the Player Character identified by the given CharID. If any Bonus values * have changed, then this will throw a SkillRankChangeEvent to any * SkillRankChangeListener objects which have subscribed to receive updates * when values change. * * @param id * The CharID identifying the Player Character for which the * check for changes in bonus skillrank values should be * performed */ public void reset(CharID id) { Map<Skill, Double> map = getConstructingInfo(id); for (Skill s : Globals.getContext().getReferenceContext() .getConstructedCDOMObjects(Skill.class)) { double newValue = bonusCheckingFacet .getBonus(id, "SKILLRANK", s.getKeyName()); for (Type singleType : s.getTrueTypeList(false)) { newValue += bonusCheckingFacet.getBonus(id, "SKILLRANK", "TYPE." + singleType); } Double oldValue = map.get(s); if ((oldValue == null) || (newValue != oldValue.doubleValue())) { map.put(s, newValue); support.fireSkillRankChange(id, s, oldValue, newValue); } } } /** * Returns a HashMap of SkillRank Bonus values for this SkillRankChangeFacet * and the PlayerCharacter represented by the given CharID. Will create a * new empty HashMap for the PlayerCharacter represented by the given CharID * if no information has been set in this SkillRankChangeFacet for the given * CharID. Will not return null. * * Note that this method SHOULD NOT be public. The HashMap object is owned * by SkillRankChangeFacet, and since it can be modified, a reference to * that HashMap should not be exposed to any object other than * SkillRankChangeFacet. * * @param id * The CharID for which the HashMap of bonus values should be * returned * @return The HashMap of SkillRank Bonus values for the Player Character * represented by the given CharID */ private HashMap<Skill, Double> getConstructingInfo(CharID id) { HashMap<Skill, Double> map = getInfo(id); if (map == null) { map = new HashMap<>(); setCache(id, map); } return map; } /** * Returns a HashMap of Bonus values for this SkillRankChangeFacet and the * PlayerCharacter represented by the given CharID. May return null if no * information has been set in this SkillRankChangeFacet for the given * CharID. * * Note that this method SHOULD NOT be public. The HashMap object is owned * by SkillRankChangeFacet, and since it can be modified, a reference to * that HashMap should not be exposed to any object other than * SkillRankChangeFacet. * * @param id * The CharID for which the HashMap of bonus values should be * returned * @return The HashMap of SkillRank Bonus values for the Player Character * represented by the given CharID */ private HashMap<Skill, Double> getInfo(CharID id) { return (HashMap<Skill, Double>) getCache(id); } /** * SkillRankChangeListener is the interface that must be implemented by a * class for it to receive SkillRankChangeEvents from the * SkillRankChangeFacet when a SkillRank Bonus value has changed for a * Player Character. * * @author Thomas Parker (thpr [at] yahoo.com) */ @FunctionalInterface public interface SkillRankChangeListener { /** * Method called when a SkillRank Bonus value has changed on a Player * Character. The SkillRankChangeEvent contains the relevant details of * the SkillRank Bonus value change. * * @param srce * The SkillRankChangeEvent containing the details of the * SkillRank Bonus value change for a Player Character */ void bonusChange(SkillRankChangeEvent srce); } /** * SkillRankChangeEvent is an event sent to a SkillRankChangeListener when a * SkillRank Bonus value changes on a Player Character. * * @author Thomas Parker (thpr [at] yahoo.com) */ public static class SkillRankChangeEvent { /** * The CharID identifying the Player Character on which the Bonus value * change took place. */ private final CharID charID; /** * The Skill for which the SkillRank Bonus value changed on the Player * Character. */ private final Skill skill; /** * The previous value of the Bonus value */ private final Number oldVal; /** * The new value of the Bonus value */ private final Number newVal; /** * Constructs a new SkillRankChangeEvent indicating a Bonus value change * took place on the Player Character identified by the given CharId. * The Bonus name, type, old value, and new value are provided. * * @param id * The CharID indicating the Player Character on which the * Bonus value change took place * @param sk * The Skill for the SkillRank Bonus value that changed * @param oldValue * The previous value of the Bonus value * @param newValue * The new value of the Bonus value */ public SkillRankChangeEvent(CharID id, Skill sk, Number oldValue, Number newValue) { charID = id; skill = sk; oldVal = oldValue; newVal = newValue; } public CharID getCharID() { return charID; } public Skill getSkill() { return skill; } public Number getOldVal() { return oldVal; } public Number getNewVal() { return newVal; } } /** * SkillRankChangeSupport is a support class that provides the actual * structure for adding and removing listeners to a class that can provide * updates for changes to SkillRank Bonus values on a Player Character. * * @author Thomas Parker (thpr [at] yahoo.com) */ public static class SkillRankChangeSupport { private List<SkillRankChangeListener> listeners = new ArrayList<>(); /** * Adds a new SkillRankChangeListener to receive SkillRankChangeEventas * from the change source. The given SkillRankChangeListener is * subscribed to all SkillRank Bonus value changes. * * Note that the SkillRankChangeListeners are a list, meaning a given * SkillRankChangeListener can be added more than once, and if that * occurs, it must be removed an equivalent number of times in order to * no longer receive events from this SkillRankChangeSupport. * * @param listener * The SkillRankChangeListener to receive * SkillRankChangeEvents from this SkillRankChangeSupport */ public synchronized void addSkillRankChangeListener( SkillRankChangeListener listener) { listeners.add(listener); } /** * Removes a SkillRankChangeListener so that it will no longer receive * SkillRankChangeEvents from the source DataFacet. * * @param listener * The SkillRankChangeListener to be removed */ public synchronized void removeSkillRankChangeListener( SkillRankChangeListener listener) { listeners.remove(listener); } public synchronized SkillRankChangeListener[] getSkillRankChangeListeners() { return (listeners.toArray(new SkillRankChangeListener[listeners.size()])); } /** * Sends a SkillRankChangeEvent to the SkillRankChangeListeners that are * receiving SkillRankChangeEvents from the change source. * * @param id * The CharID identifying the Player Character to which the * SkillRankChangeEvent relates. * @param skill * The skill for which the SkillRank Bonus value changed * @param oldValue * The previous value of the Bonus value * @param newValue * The new value of the Bonus value */ public void fireSkillRankChange(CharID id, Skill skill, Number oldValue, Number newValue) { SkillRankChangeEvent bce = new SkillRankChangeEvent(id, skill, oldValue, newValue); for (SkillRankChangeListener target : listeners) { target.bonusChange(bce); } } } /** * Adds a new SkillRankChangeListener to receive SkillRankChangeEvents from * SkillRankChangeFacet. The given SkillRankChangeListener subscribed to all * SkillRank Bonus changes. * * Note that the SkillRankChangeListeners are a list, meaning a given * SkillRankChangeListener can be added more than once, and if that occurs, * it must be removed an equivalent number of times in order to no longer * receive events from this SkillRankChangeFacet. * * @param listener * The SkillRankChangeListener to receive SkillRankChangeEvents * from this SkillRankChangeFacet */ public void addSkillRankChangeListener(SkillRankChangeListener listener) { support.addSkillRankChangeListener(listener); } /** * Removes a SkillRankChangeListener so that it will no longer receive * SkillRankChangeEvents from SkillRankChangeFacet. * * @param listener * The SkillRankChangeListener to be removed */ public void removeSkillRankChangeListener(SkillRankChangeListener listener) { support.removeSkillRankChangeListener(listener); } public void setBonusCheckingFacet(BonusCheckingFacet bonusCheckingFacet) { this.bonusCheckingFacet = bonusCheckingFacet; } /** * Copies the contents of the SkillRankChangeFacet from one Player Character * to another Player Character, based on the given CharIDs representing * those Player Characters. * * This is a method in SkillRankChangeFacet in order to avoid exposing the * mutable HashMap object to other classes. This should not be inlined, as * the HashMap is internal information to SkillRankChangeFacet and should * not be exposed to other classes. * * Note also the copy is a one-time event and no references are maintained * between the Player Characters represented by the given CharIDs (meaning * once this copy takes place, any change to the SkillRankChangeFacet of one * Player Character will only impact the Player Character where the * SkillRankChangeFacet was changed). * * @param source * The CharID representing the Player Character from which the * information should be copied * @param copy * The CharID representing the Player Character to which the * information should be copied */ @Override public void copyContents(CharID source, CharID copy) { Map<Skill, Double> map = getInfo(source); if (map != null) { getConstructingInfo(copy).putAll(map); } } public double getRank(CharID id, Skill skill) { Map<Skill, Double> map = getInfo(id); if (map != null) { Double rank = map.get(skill); if (rank != null) { return rank.doubleValue(); } } return 0.0; } }