diff --git a/src/com/projectkorra/projectkorra/ability/CoreAbility.java b/src/com/projectkorra/projectkorra/ability/CoreAbility.java index abdc2972..e74dc5c0 100644 --- a/src/com/projectkorra/projectkorra/ability/CoreAbility.java +++ b/src/com/projectkorra/projectkorra/ability/CoreAbility.java @@ -3,6 +3,7 @@ package com.projectkorra.projectkorra.ability; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; @@ -17,6 +18,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.jar.JarFile; +import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.tuple.Pair; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -39,6 +42,9 @@ import com.projectkorra.projectkorra.ability.util.ComboManager; import com.projectkorra.projectkorra.ability.util.MultiAbilityManager; import com.projectkorra.projectkorra.ability.util.MultiAbilityManager.MultiAbilityInfo; import com.projectkorra.projectkorra.ability.util.PassiveManager; +import com.projectkorra.projectkorra.attribute.Attribute; +import com.projectkorra.projectkorra.attribute.AttributeModifier; +import com.projectkorra.projectkorra.attribute.AttributePriority; import com.projectkorra.projectkorra.configuration.ConfigManager; import com.projectkorra.projectkorra.event.AbilityEndEvent; import com.projectkorra.projectkorra.event.AbilityProgressEvent; @@ -73,6 +79,7 @@ public abstract class CoreAbility implements Ability { private static final Map, CoreAbility> ABILITIES_BY_CLASS = new ConcurrentHashMap<>(); private static final double DEFAULT_COLLISION_RADIUS = 0.3; private static final List ADDON_PLUGINS = new ArrayList<>(); + private static final Map, Map> ATTRIBUTE_FIELDS = new HashMap<>(); private static int idCounter; @@ -80,12 +87,15 @@ public abstract class CoreAbility implements Ability { protected BendingPlayer bPlayer; protected FlightHandler flightHandler; + private final Map>>> attributeModifiers = new HashMap<>(); + private final Map attributeValues = new HashMap<>(); private boolean started; private boolean removed; private boolean hidden; private int id; private long startTime; private long startTick; + private boolean attributesModified; static { idCounter = Integer.MIN_VALUE; @@ -103,6 +113,15 @@ public abstract class CoreAbility implements Ability { * @see #getAbility(String) */ public CoreAbility() { + for (Field field : getClass().getDeclaredFields()) { + if (field.isAnnotationPresent(Attribute.class)) { + Attribute attribute = field.getAnnotation(Attribute.class); + if (!ATTRIBUTE_FIELDS.containsKey(getClass())) { + ATTRIBUTE_FIELDS.put(getClass(), new HashMap<>()); + } + ATTRIBUTE_FIELDS.get(getClass()).put(attribute.value(), field); + } + } } /** @@ -151,6 +170,7 @@ public abstract class CoreAbility implements Ability { this.remove(); return; } + this.started = true; this.startTime = System.currentTimeMillis(); final Class clazz = this.getClass(); @@ -237,6 +257,10 @@ public abstract class CoreAbility implements Ability { } try { + if (!abil.attributesModified) { + abil.modifyAttributes(); + abil.attributesModified = true; + } abil.progress(); Bukkit.getServer().getPluginManager().callEvent(new AbilityProgressEvent(abil)); } @@ -930,6 +954,76 @@ public abstract class CoreAbility implements Ability { return locations; } + public CoreAbility addAttributeModifier(String attribute, Number value, AttributeModifier modification) { + return addAttributeModifier(attribute, value, modification, AttributePriority.MEDIUM); + } + + public CoreAbility addAttributeModifier(String attribute, Number value, AttributeModifier modificationType, AttributePriority priority) { + Validate.notNull(attribute, "attribute cannot be null"); + Validate.notNull(value, "value cannot be null"); + Validate.notNull(modificationType, "modifierMethod cannot be null"); + Validate.notNull(priority, "priority cannot be null"); + Validate.isTrue(ATTRIBUTE_FIELDS.containsKey(getClass()) && ATTRIBUTE_FIELDS.get(getClass()).containsKey(attribute), "Attribute " + attribute + " is not a defined Attribute for " + getName()); + if (!attributeModifiers.containsKey(attribute)) { + attributeModifiers.put(attribute, new HashMap<>()); + } + if (!attributeModifiers.get(attribute).containsKey(priority)) { + attributeModifiers.get(attribute).put(priority, new HashSet<>()); + } + attributeModifiers.get(attribute).get(priority).add(Pair.of(value, modificationType)); + return this; + } + + public CoreAbility setAttribute(String attribute, Object value) { + Validate.notNull(attribute, "attribute cannot be null"); + Validate.notNull(value, "value cannot be null"); + Validate.isTrue(ATTRIBUTE_FIELDS.containsKey(getClass()) && ATTRIBUTE_FIELDS.get(getClass()).containsKey(attribute), "Attribute " + attribute + " is not a defined Attribute for " + getName()); + attributeValues.put(attribute, value); + return this; + } + + private void modifyAttributes() { + System.out.println(attributeModifiers); + for (String attribute : attributeModifiers.keySet()) { + Field field = ATTRIBUTE_FIELDS.get(getClass()).get(attribute); + boolean accessibility = field.isAccessible(); + field.setAccessible(true); + try { + for (AttributePriority priority : AttributePriority.values()) { + if (attributeModifiers.get(attribute).containsKey(priority)) { + for (Pair pair : attributeModifiers.get(attribute).get(priority)) { + Object get = field.get(this); + Validate.isTrue(get instanceof Number, "The field " + field.getName() + " cannot algebraically be modified."); + Number oldValue = (Number) field.get(this); + Number newValue = pair.getRight().performModification(oldValue, pair.getLeft()); + field.set(this, newValue); + } + } + } + } + catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + finally { + field.setAccessible(accessibility); + } + } + attributeValues.forEach((attribute, value) -> { + Field field = ATTRIBUTE_FIELDS.get(getClass()).get(attribute); + boolean accessibility = field.isAccessible(); + field.setAccessible(true); + try { + field.set(this, value); + } + catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + finally { + field.setAccessible(accessibility); + } + }); + } + /** * @return the current FileConfiguration for the plugin */ diff --git a/src/com/projectkorra/projectkorra/airbending/Suffocate.java b/src/com/projectkorra/projectkorra/airbending/Suffocate.java index 91eb2044..3b4aa845 100644 --- a/src/com/projectkorra/projectkorra/airbending/Suffocate.java +++ b/src/com/projectkorra/projectkorra/airbending/Suffocate.java @@ -30,7 +30,12 @@ import com.projectkorra.projectkorra.util.DamageHandler; public class Suffocate extends AirAbility { public static enum SpiralType { - HORIZONTAL1, HORIZONTAL2, VERTICAL1, VERTICAL2, DIAGONAL1, DIAGONAL2 + HORIZONTAL1, + HORIZONTAL2, + VERTICAL1, + VERTICAL2, + DIAGONAL1, + DIAGONAL2 }; private boolean started; diff --git a/src/com/projectkorra/projectkorra/attribute/Attribute.java b/src/com/projectkorra/projectkorra/attribute/Attribute.java index 80c69ed0..11b04836 100644 --- a/src/com/projectkorra/projectkorra/attribute/Attribute.java +++ b/src/com/projectkorra/projectkorra/attribute/Attribute.java @@ -9,19 +9,7 @@ import java.lang.annotation.Target; @Target(value = { ElementType.FIELD }) public @interface Attribute { - /** - * This is the attribute name that is added to the CoreAbility name. E.g. - * Returning "Damage" on a FireBlast ability would make the attribute - * "FireBlastDamage" - */ - String value() default ""; - - /** - * This is for overriding the attribute name if the name you want should not - * come from the CoreAbility name. E.g. Returning "FastSwimSpeed" would make - * the Attribute name "FastSwimSpeed", instead of ability + "FastSwimSpeed" - */ - String attribute() default ""; + public String value(); public static final String SPEED = "Speed"; public static final String RANGE = "Range"; @@ -34,4 +22,5 @@ public @interface Attribute { public static final String POWER = "Power"; public static final String WIDTH = "Width"; public static final String HEIGHT = "Height"; + } diff --git a/src/com/projectkorra/projectkorra/attribute/AttributeModifier.java b/src/com/projectkorra/projectkorra/attribute/AttributeModifier.java index f9fcb484..5c2d88ff 100644 --- a/src/com/projectkorra/projectkorra/attribute/AttributeModifier.java +++ b/src/com/projectkorra/projectkorra/attribute/AttributeModifier.java @@ -1,64 +1,82 @@ package com.projectkorra.projectkorra.attribute; -import org.bukkit.plugin.Plugin; +import org.apache.commons.lang.Validate; -import com.projectkorra.projectkorra.ProjectKorra; -import com.projectkorra.projectkorra.ability.CoreAbility; -import com.projectkorra.projectkorra.ability.FireAbility; -import com.projectkorra.projectkorra.ability.WaterAbility; +public enum AttributeModifier { -public class AttributeModifier { + ADDITION((oldValue, modifier) -> { + if (oldValue instanceof Double || modifier instanceof Double) { + return oldValue.doubleValue() + modifier.doubleValue(); + } else if (oldValue instanceof Float || modifier instanceof Float) { + return oldValue.floatValue() + modifier.floatValue(); + } else if (oldValue instanceof Long || modifier instanceof Long) { + return oldValue.longValue() + modifier.longValue(); + } else if (oldValue instanceof Integer || modifier instanceof Integer) { + return oldValue.intValue() + modifier.intValue(); + } + return 0; + }), + SUBTRACTION((oldValue, modifier) -> { + if (oldValue instanceof Double || modifier instanceof Double) { + return oldValue.doubleValue() - modifier.doubleValue(); + } else if (oldValue instanceof Float || modifier instanceof Float) { + return oldValue.floatValue() - modifier.floatValue(); + } else if (oldValue instanceof Long || modifier instanceof Long) { + return oldValue.longValue() - modifier.longValue(); + } else if (oldValue instanceof Integer || modifier instanceof Integer) { + return oldValue.intValue() - modifier.intValue(); + } + return 0; + }), + MULTIPLICATION((oldValue, modifier) -> { + if (oldValue instanceof Double || modifier instanceof Double) { + return oldValue.doubleValue() * modifier.doubleValue(); + } else if (oldValue instanceof Float || modifier instanceof Float) { + return oldValue.floatValue() * modifier.floatValue(); + } else if (oldValue instanceof Long || modifier instanceof Long) { + return oldValue.longValue() * modifier.longValue(); + } else if (oldValue instanceof Integer || modifier instanceof Integer) { + return oldValue.intValue() * modifier.intValue(); + } + return 0; + }), + DIVISION((oldValue, modifier) -> { + if (oldValue instanceof Double || modifier instanceof Double) { + return oldValue.doubleValue() / modifier.doubleValue(); + } else if (oldValue instanceof Float || modifier instanceof Float) { + return oldValue.floatValue() / modifier.floatValue(); + } else if (oldValue instanceof Long || modifier instanceof Long) { + return oldValue.longValue() / modifier.longValue(); + } else if (oldValue instanceof Integer || modifier instanceof Integer) { + return oldValue.intValue() / modifier.intValue(); + } + return 0; + }); - private double modifier = 1.0D; - private final AttributeModifierType type; + private AttributeModifierMethod modifier; - public enum AttributeModifierType { - MULTIPLY, ADDITION - }; - - public AttributeModifier(final String name, final double modifier, final AttributeModifierType type, final Plugin plugin) { + private AttributeModifier(AttributeModifierMethod modifier) { this.modifier = modifier; - this.type = type; } - public AttributeModifier(final String name, final AttributeModifierType type, final Plugin plugin) { - this(name, 1.0D, type, plugin); + public AttributeModifierMethod getModifier() { + return modifier; } - protected AttributeModifier(final String name, final AttributeModifierType type, final double modifier) { - this(name, modifier, type, ProjectKorra.plugin); + public Number performModification(Number oldValue, Number modifier) { + Validate.isTrue(!(this == DIVISION && modifier.doubleValue() == 0), "modifier cannot be 0"); + return this.modifier.performModification(oldValue, modifier); } /** - * Should return the modifier that should be applied to the Attribute. Is - * called every time it is applied, so the value doesn't have to be final. - * - * @return The modifier + * Functional interface for modifying fields with the {@link Attribute} + * annotation */ - public double getModifier(final CoreAbility ability) { - return this.modifier; + @FunctionalInterface + public interface AttributeModifierMethod { + + public Number performModification(Number oldValue, Number modifier); + } - /** - * Returns what type of math should be done with the modifier. - * - * @return The modifier type - */ - public AttributeModifierType getType() { - return this.type; - } - - public static AttributeModifier WATERBENDING_NIGHT = new AttributeModifier("WaterbendingNightModifier", AttributeModifierType.MULTIPLY, 1.0) { - @Override - public double getModifier(final CoreAbility ability) { - return WaterAbility.getNightFactor(ability.getPlayer().getWorld()); - } - }; - - public static AttributeModifier FIREBENDING_DAY = new AttributeModifier("FirebendingDayModifier", AttributeModifierType.MULTIPLY, 1.0) { - @Override - public double getModifier(final CoreAbility ability) { - return FireAbility.getDayFactor(1.0, ability.getPlayer().getWorld()); - } - }; } diff --git a/src/com/projectkorra/projectkorra/attribute/AttributePriority.java b/src/com/projectkorra/projectkorra/attribute/AttributePriority.java new file mode 100644 index 00000000..8be56313 --- /dev/null +++ b/src/com/projectkorra/projectkorra/attribute/AttributePriority.java @@ -0,0 +1,9 @@ +package com.projectkorra.projectkorra.attribute; + +public enum AttributePriority { + + LOW, + MEDIUM, + HIGH; + +} diff --git a/src/com/projectkorra/projectkorra/attribute/Attributes.java b/src/com/projectkorra/projectkorra/attribute/Attributes.java deleted file mode 100644 index aac52eb4..00000000 --- a/src/com/projectkorra/projectkorra/attribute/Attributes.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.projectkorra.projectkorra.attribute; - -import java.lang.reflect.Field; - -import org.bukkit.Bukkit; - -import com.projectkorra.projectkorra.Element; -import com.projectkorra.projectkorra.ability.CoreAbility; -import com.projectkorra.projectkorra.attribute.AttributeModifier.AttributeModifierType; -import com.projectkorra.projectkorra.event.AttributeModifyEvent; - -public class Attributes { - /** - * Modifies all abilities of the provided element for the given attribute - * and modifier - * - * @param element The type of abilities being changed. Can be a subelement. - * @param attribute What attribute to modify - * @param modifier The modifier - */ - public static void modify(final Element element, final String attribute, final AttributeModifier modifier) { - for (final CoreAbility ability : CoreAbility.getAbilitiesByElement(element)) { - modify(ability, attribute, modifier); - } - }; - - /** - * Modifies all the attribute of the provided ability from the given - * attribute and modifier - * - * @param ability The ability to change - * @param attribute What attribute to modify - * @param modifier The modifier - */ - public static void modify(final CoreAbility ability, final String attribute, final AttributeModifier modifier) { - if (ability.getPlayer() == null) { - for (final CoreAbility ability2 : CoreAbility.getAbilities(ability.getClass())) { - modify(ability2, attribute, modifier); - } - return; - } - - for (final Field field : ability.getClass().getDeclaredFields()) { - if (field.isAnnotationPresent(Attribute.class)) { - final Attribute annotation = field.getAnnotation(Attribute.class); - String attrToTest = ability.getName() + annotation.value(); - if (!annotation.attribute().equals("")) { - attrToTest = annotation.attribute(); - } - - if (attrToTest.equalsIgnoreCase(attribute)) { - final boolean flag = field.isAccessible(); - - if (!flag) { - field.setAccessible(true); - } - - try { - if (field.getDeclaringClass().equals(Double.TYPE.getClass())) { - final double oldValue = field.getDouble(ability); - double newValue = modifier.getType() == AttributeModifierType.MULTIPLY ? oldValue * modifier.getModifier(ability) : oldValue + modifier.getModifier(ability); - - final AttributeModifyEvent event = new AttributeModifyEvent(ability, attribute, oldValue, newValue, modifier); - Bukkit.getPluginManager().callEvent(event); - newValue = event.getNewValue(); - - field.setDouble(ability, newValue); - } else if (field.getDeclaringClass().equals(Long.TYPE.getClass())) { - final long oldValue = field.getLong(ability); - long newValue = (long) (modifier.getType() == AttributeModifierType.MULTIPLY ? oldValue * modifier.getModifier(ability) : oldValue + modifier.getModifier(ability)); - - final AttributeModifyEvent event = new AttributeModifyEvent(ability, attribute, oldValue, newValue, modifier); - Bukkit.getPluginManager().callEvent(event); - newValue = (long) event.getNewValue(); - - field.setLong(ability, newValue); - } else if (field.getDeclaringClass().equals(Integer.TYPE.getClass())) { - final int oldValue = field.getInt(ability); - int newValue = (int) (modifier.getType() == AttributeModifierType.MULTIPLY ? oldValue * modifier.getModifier(ability) : oldValue + modifier.getModifier(ability)); - - final AttributeModifyEvent event = new AttributeModifyEvent(ability, attribute, oldValue, newValue, modifier); - Bukkit.getPluginManager().callEvent(event); - newValue = (int) event.getNewValue(); - - field.setInt(ability, newValue); - } - - } - catch (final IllegalArgumentException e) { - e.printStackTrace(); - } - catch (final IllegalAccessException e) { - e.printStackTrace(); - } - - if (!flag) { - field.setAccessible(false); - } - } - } - } - } - -} diff --git a/src/com/projectkorra/projectkorra/event/AttributeModifyEvent.java b/src/com/projectkorra/projectkorra/event/AttributeModifyEvent.java deleted file mode 100644 index 774079e5..00000000 --- a/src/com/projectkorra/projectkorra/event/AttributeModifyEvent.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.projectkorra.projectkorra.event; - -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -import com.projectkorra.projectkorra.ability.CoreAbility; -import com.projectkorra.projectkorra.attribute.AttributeModifier; - -public class AttributeModifyEvent extends Event { - - private final CoreAbility ability; - private final String attribute; - private final double oldValue; - private double newValue; - private final AttributeModifier modifier; - - public AttributeModifyEvent(final CoreAbility ability, final String attribute, final double oldValue, final double newValue, final AttributeModifier modifier) { - this.ability = ability; - this.attribute = attribute; - this.oldValue = oldValue; - this.newValue = newValue; - this.modifier = modifier; - } - - public CoreAbility getAbility() { - return this.ability; - } - - public AttributeModifier getModifier() { - return this.modifier; - } - - public boolean hasModifier() { - return this.modifier != null; - } - - public String getAttribute() { - return this.attribute; - } - - public double getOldValue() { - return this.oldValue; - } - - public double getNewValue() { - return this.newValue; - } - - public void setNewValue(final double newValue) { - this.newValue = newValue; - } - - @Override - public HandlerList getHandlers() { - return null; - } - -} diff --git a/src/com/projectkorra/projectkorra/firebending/FireBlast.java b/src/com/projectkorra/projectkorra/firebending/FireBlast.java index e73c1f71..9ef01d08 100644 --- a/src/com/projectkorra/projectkorra/firebending/FireBlast.java +++ b/src/com/projectkorra/projectkorra/firebending/FireBlast.java @@ -19,6 +19,7 @@ import com.projectkorra.projectkorra.ProjectKorra; import com.projectkorra.projectkorra.ability.AirAbility; import com.projectkorra.projectkorra.ability.FireAbility; import com.projectkorra.projectkorra.ability.util.Collision; +import com.projectkorra.projectkorra.attribute.Attribute; import com.projectkorra.projectkorra.avatar.AvatarState; import com.projectkorra.projectkorra.firebending.util.FireDamageTimer; import com.projectkorra.projectkorra.util.DamageHandler; @@ -35,10 +36,15 @@ public class FireBlast extends FireAbility { private boolean isFireBurst = false; private boolean fireBurstIgnite; private int ticks; + @Attribute(Attribute.COOLDOWN) private long cooldown; + @Attribute(Attribute.SPEED) private double speedFactor; + @Attribute(Attribute.RANGE) private double range; + @Attribute(Attribute.DAMAGE) private double damage; + @Attribute(Attribute.SPEED) private double speed; private double collisionRadius; private double fireTicks; diff --git a/src/com/projectkorra/projectkorra/util/Attribute.java b/src/com/projectkorra/projectkorra/util/Attribute.java deleted file mode 100644 index 052da5b9..00000000 --- a/src/com/projectkorra/projectkorra/util/Attribute.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.projectkorra.projectkorra.util; - -import java.lang.reflect.Field; - -import com.projectkorra.projectkorra.ProjectKorra; -import com.projectkorra.projectkorra.ability.CoreAbility; - -public class Attribute { - - public static boolean setField(final CoreAbility ability, final String field, final Object value) { - try { - final Field _field = ability.getClass().getDeclaredField(field); - final boolean oldVisibility = _field.isAccessible(); - _field.setAccessible(true); - try { - _field.set(ability, value); - } - catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - ProjectKorra.log.warning(e.getClass().getName() + ": Failed to set field '" + _field.getName() + "' (" + _field.getType().getSimpleName() + ") in " + ability.getClass().getSimpleName() + " to '" + value.toString() + "' (" + value.getClass().getSimpleName() + ") at"); - - return false; - } - _field.setAccessible(oldVisibility); - } - catch (NoSuchFieldException | SecurityException e) { - e.printStackTrace(); - ProjectKorra.log.warning(e.getClass().getName() + ": Failed to set field '" + field + "' in " + ability.getClass().getSimpleName() + " to '" + value.toString() + "' (" + value.getClass().getSimpleName() + ")"); - return false; - } - return true; - } - - public static Object getField(final CoreAbility ability, final String field) { - try { - final Field _field = ability.getClass().getDeclaredField(field); - final boolean oldVisibility = _field.isAccessible(); - _field.setAccessible(true); - try { - final Object object = _field.get(ability); - _field.setAccessible(oldVisibility); - return object; - } - catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - ProjectKorra.log.warning(e.getClass().getName() + ": Failed to get field '" + _field.getName() + "' in " + ability.getClass().getSimpleName()); - return null; - } - } - catch (NoSuchFieldException | SecurityException e) { - e.printStackTrace(); - ProjectKorra.log.warning(e.getClass().getName() + ": Failed to get field '" + field + "' in " + ability.getClass().getSimpleName()); - return null; - } - } - -}