/** * Copyright (C) 2002-2012 The FreeCol Team * * This file is part of FreeCol. * * FreeCol is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * FreeCol 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with FreeCol. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.freecol.common.model; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Logger; public class FeatureContainer { private static final Logger logger = Logger.getLogger(FeatureContainer.class.getName()); private Map<String, Set<Ability>> abilities = new HashMap<String, Set<Ability>>(); private Map<String, Set<Modifier>> modifiers = new HashMap<String, Set<Modifier>>(); public Set<Ability> getAbilities() { Set<Ability> result = new HashSet<Ability>(); for (Set<Ability> abilitySet : abilities.values()) { result.addAll(abilitySet); } return result; } public Set<Modifier> getModifiers() { Set<Modifier> result = new HashSet<Modifier>(); for (Set<Modifier> modifierSet : modifiers.values()) { result.addAll(modifierSet); } return result; } /** * Returns a Set of Abilities with the given ID. * * @param id a <code>String</code> value * @return a <code>Set<Feature></code> value */ public Set<Ability> getAbilitySet(String id) { return getAbilitySet(id, null, null); } /** * Returns a Set of Abilities with the given ID which apply to the * given FreeColGameObjectType. * * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value * @return a <code>Set<Feature></code> value */ public Set<Ability> getAbilitySet(String id, FreeColGameObjectType objectType) { return getAbilitySet(id, objectType, null); } /** * Returns a Set of Abilities with the given ID which apply to the * given FreeColGameObjectType and Turn. * * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value * @param turn a <code>Turn</code> value * @return a <code>Set<Feature></code> value */ public Set<Ability> getAbilitySet(String id, FreeColGameObjectType objectType, Turn turn) { Set<Ability> abilitySet = abilities.get(id); if (abilitySet == null) { return new HashSet<Ability>(); } else { Set<Ability> result = new HashSet<Ability>(); for (Ability ability : abilitySet) { if (ability.appliesTo(objectType, turn)) { result.add(ability); } } return result; } } /** * Returns true if this Player has the ability with the given ID. * * @param id a <code>String</code> value * @return a <code>boolean</code> value */ public boolean hasAbility(String id) { return hasAbility(id, null, null); } /** * Returns true if this Player has the ability with the given ID. * * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value * @return a <code>boolean</code> value */ public boolean hasAbility(String id, FreeColGameObjectType objectType) { return hasAbility(id, objectType, null); } /** * Returns true if this Player has the ability with the given ID. * * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value * @param turn a <code>Turn</code> value * @return a <code>boolean</code> value */ public boolean hasAbility(String id, FreeColGameObjectType objectType, Turn turn) { Set<Ability> abilitySet = abilities.get(id); if (abilitySet == null) { return false; } else { boolean foundApplicableAbility = false; for (Ability ability : abilitySet) { if (ability.appliesTo(objectType, turn)) { if (ability.getValue()) { foundApplicableAbility = true; } else { return false; } } } return foundApplicableAbility; } } /** * Returns true if the given Set of Abilities is not empty and * does not contain any Abilities with the value false. * * @return a <code>boolean</code> value */ public static boolean hasAbility(Set<Ability> abilitySet) { if (abilitySet.isEmpty()) { return false; } else { for (Ability ability : abilitySet) { if (!ability.getValue()) { return false; } } return true; } } /** * Returns a Set of Abilities with the given ID. * * @param id a <code>String</code> value * @return a <code>Set<Feature></code> value */ public Set<Modifier> getModifierSet(String id) { return getModifierSet(id, null, null); } /** * Returns a Set of Abilities with the given ID which apply to the * given FreeColGameObjectType. * * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value * @return a <code>Set<Feature></code> value */ public Set<Modifier> getModifierSet(String id, FreeColGameObjectType objectType) { return getModifierSet(id, objectType, null); } /** * Returns a Set of Abilities with the given ID which apply to the * given FreeColGameObjectType and Turn. * * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value * @param turn a <code>Turn</code> value * @return a <code>Set<Feature></code> value */ public Set<Modifier> getModifierSet(String id, FreeColGameObjectType objectType, Turn turn) { HashSet<Modifier> result = new HashSet<Modifier>(); Set<Modifier> modifierSet = modifiers.get(id); if (modifierSet == null) { return result; } else if (objectType == null) { return modifierSet; } else { for (Modifier modifier : modifierSet) { if (modifier.appliesTo(objectType, turn)) { result.add(modifier); } } return result; } } /** * Applies a Set of Modifiers with the given ID to the given float * value. * * @param number a <code>float</code> value * @param id a <code>String</code> value */ public float applyModifier(float number, String id) { return applyModifier(number, id, null, null); } /** * Applies a Set of Modifiers with the given ID which match the * given FreeColGameObjectType to the given float value. * * @param number a <code>float</code> value * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value */ public float applyModifier(float number, String id, FreeColGameObjectType objectType) { return applyModifier(number, id, objectType, null); } /** * Applies a Set of Modifiers with the given ID which match the * given FreeColGameObjectType and Turn to the given float value. * * @param number a <code>float</code> value * @param id a <code>String</code> value * @param objectType a <code>FreeColGameObjectType</code> value * @param turn a <code>Turn</code> value */ public float applyModifier(float number, String id, FreeColGameObjectType objectType, Turn turn) { return applyModifierSet(number, turn, getModifierSet(id, objectType, turn)); } /** * Applies a given Set of Modifiers to the given float value. * * @param number a <code>float</code> value * @param turn a <code>Turn</code> value * @return a <code>float</code> value */ public static float applyModifiers(float number, Turn turn, List<Modifier> modifierSet) { if (modifierSet == null || modifierSet.isEmpty()) { return number; } float result = number; for (Modifier modifier : modifierSet) { float value = modifier.getValue(); if (value == Modifier.UNKNOWN) { return Modifier.UNKNOWN; } if (modifier.hasIncrement() && turn != null) { int diff = turn.getNumber() - modifier.getFirstTurn().getNumber(); switch(modifier.getIncrementType()) { case ADDITIVE: value += modifier.getIncrement() * diff; break; case MULTIPLICATIVE: value *= modifier.getIncrement() * diff; break; case PERCENTAGE: value += (value * modifier.getIncrement() * diff) / 100; break; } } switch(modifier.getType()) { case ADDITIVE: result += value; break; case MULTIPLICATIVE: result *= value; break; case PERCENTAGE: result += (result * value) / 100; break; } } return result; } /** * Applies a given Set of Modifiers to the given float value. * * @param number a <code>float</code> value * @param turn a <code>Turn</code> value * @return a <code>float</code> value */ public static float applyModifierSet(float number, Turn turn, Set<Modifier> modifierSet) { if (modifierSet == null) { return number; } float additive = 0, percentage = 0, multiplicative = 1; for (Modifier modifier : modifierSet) { float value = modifier.getValue(); if (value == Modifier.UNKNOWN) { return Modifier.UNKNOWN; } if (modifier.hasIncrement() && turn != null) { int diff = turn.getNumber() - modifier.getFirstTurn().getNumber(); switch(modifier.getIncrementType()) { case ADDITIVE: value += modifier.getIncrement() * diff; break; case MULTIPLICATIVE: value *= modifier.getIncrement() * diff; break; case PERCENTAGE: value += (value * modifier.getIncrement() * diff) / 100; break; } } switch(modifier.getType()) { case ADDITIVE: additive += value; break; case MULTIPLICATIVE: multiplicative *= value; break; case PERCENTAGE: percentage += value; break; } } float result = number; result += additive; result *= multiplicative; result += (result * percentage) / 100; return result; } /** * Add the given Ability to the set of Abilities present. If the * Ability given can not be combined with a Ability with the same * ID already present, the old Ability will be replaced. * * @param ability a <code>Ability</code> value * @return true if the Ability was added */ public boolean addAbility(Ability ability) { if (ability == null) { return false; } Set<Ability> abilitySet = abilities.get(ability.getId()); if (abilitySet == null) { abilitySet = new HashSet<Ability>(); abilities.put(ability.getId(), abilitySet); } return abilitySet.add(ability); } /** * Add the given Modifier to the set of Modifiers present. If the * Modifier given can not be combined with a Modifier with the same * ID already present, the old Modifier will be replaced. * * @param modifier a <code>Modifier</code> value * @return true if the Modifier was added */ public boolean addModifier(Modifier modifier) { if (modifier == null) { return false; } Set<Modifier> modifierSet = modifiers.get(modifier.getId()); if (modifierSet == null) { modifierSet = new HashSet<Modifier>(); modifiers.put(modifier.getId(), modifierSet); } return modifierSet.add(modifier); } /** * Removes and returns a Ability from this ability set. * * @param oldAbility a <code>Ability</code> value * @return a <code>Ability</code> value */ public Ability removeAbility(Ability oldAbility) { if (oldAbility == null) { return null; } else { Set<Ability> abilitySet = abilities.get(oldAbility.getId()); if (abilitySet == null) { return null; } else if (abilitySet.remove(oldAbility)) { return oldAbility; } else { return null; } } } /** * Describe <code>removeAbilities</code> method here. * * @param id a <code>String</code> value */ public void removeAbilities(String id) { abilities.remove(id); } /** * Removes and returns a Modifier from this modifier set. * * @param oldModifier a <code>Modifier</code> value * @return a <code>Modifier</code> value */ public Modifier removeModifier(Modifier oldModifier) { if (oldModifier == null) { return null; } else { Set<Modifier> modifierSet = modifiers.get(oldModifier.getId()); if (modifierSet == null) { return null; } else if (modifierSet.remove(oldModifier)) { return oldModifier; } else { return null; } } } /** * Describe <code>removeModifiers</code> method here. * * @param id a <code>String</code> value */ public void removeModifiers(String id) { modifiers.remove(id); } /** * Describe <code>add</code> method here. * * @param featureContainer a <code>FeatureContainer</code> value */ public void add(FeatureContainer featureContainer) { for (Entry<String, Set<Ability>> entry : featureContainer.abilities.entrySet()) { Set<Ability> abilitySet = abilities.get(entry.getKey()); if (abilitySet == null) { abilities.put(entry.getKey(), new HashSet<Ability>(entry.getValue())); } else { abilitySet.addAll(entry.getValue()); } } for (Entry<String, Set<Modifier>> entry : featureContainer.modifiers.entrySet()) { Set<Modifier> modifierSet = modifiers.get(entry.getKey()); if (modifierSet == null) { modifiers.put(entry.getKey(), new HashSet<Modifier>(entry.getValue())); } else { modifierSet.addAll(entry.getValue()); } } } /** * Describe <code>remove</code> method here. * * @param featureContainer a <code>FeatureContainer</code> value */ public void remove(FeatureContainer featureContainer) { for (Entry<String, Set<Ability>> entry : featureContainer.abilities.entrySet()) { Set<Ability> abilitySet = abilities.get(entry.getKey()); if (abilitySet != null) { abilitySet.removeAll(entry.getValue()); } } for (Entry<String, Set<Modifier>> entry : featureContainer.modifiers.entrySet()) { Set<Modifier> modifierSet = modifiers.get(entry.getKey()); if (modifierSet != null) { modifierSet.removeAll(entry.getValue()); } } } public boolean containsAbilityKey(String key) { return abilities.containsKey(key); } public boolean containsModifierKey(String key) { return modifiers.containsKey(key); } /** * Replaces the source field. This is necessary because objects * may inherit Features from other, abstract objects. * * @param oldSource a <code>FreeColGameObjectType</code> value * @param newSource a <code>FreeColGameObjectType</code> value */ public void replaceSource(FreeColGameObjectType oldSource, FreeColGameObjectType newSource) { for (Ability ability : getAbilities()) { if (ability.getSource() == oldSource) { removeAbility(ability); Ability newAbility = new Ability(ability); newAbility.setSource(newSource); addAbility(newAbility); } } for (Modifier modifier : getModifiers()) { if (modifier.getSource() == oldSource) { removeModifier(modifier); Modifier newModifier = new Modifier(modifier); newModifier.setSource(newSource); addModifier(newModifier); } } } /** * Debug helper. */ public String toString() { StringBuilder result = new StringBuilder(); result.append("[FeatureContainer [abilities"); for (Ability ability : getAbilities()) { result.append(" " + ability.toString()); } result.append("][modifiers"); for (Modifier modifier : getModifiers()) { result.append(" " + modifier.toString()); } result.append("]]"); return result.toString(); } }