All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sucy.skill.manager.AttributeManager Maven / Gradle / Ivy

Go to download

A Minecraft Bukkit plugin aiming to provide an easy code API and skill editor for all server owners to create unique and fully custom classes and skills.

There is a newer version: 1.3.1-R1
Show newest version
/**
 * SkillAPI
 * com.sucy.skill.manager.AttributeManager
 * 

* The MIT License (MIT) *

* Copyright (c) 2014 Steven Sucy *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software") to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: *

* The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.sucy.skill.manager; import com.sucy.skill.SkillAPI; import com.sucy.skill.api.player.PlayerData; import com.sucy.skill.api.util.DamageLoreRemover; import com.sucy.skill.api.util.Data; import com.sucy.skill.data.formula.Formula; import com.sucy.skill.data.formula.value.CustomValue; import com.sucy.skill.dynamic.ComponentType; import com.sucy.skill.dynamic.EffectComponent; import com.sucy.skill.gui.tool.GUIData; import com.sucy.skill.gui.tool.GUIPage; import com.sucy.skill.gui.tool.GUITool; import com.sucy.skill.gui.tool.IconHolder; import com.sucy.skill.log.LogType; import com.sucy.skill.log.Logger; import mc.promcteam.engine.mccore.config.CommentedConfig; import mc.promcteam.engine.mccore.config.parse.DataSection; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; import java.util.*; import java.util.stream.Collectors; /** * Handles loading and accessing individual * attributes from the configuration. */ public class AttributeManager { // Keys for supported stat modifiers public static final String HEALTH = "health"; public static final String MANA = "mana"; public static final String MANA_REGEN = "mana-regen"; public static final String PHYSICAL_DAMAGE = "physical-damage"; public static final String MELEE_DAMAGE = "melee-damage"; public static final String PROJECTILE_DAMAGE = "projectile-damage"; public static final String PHYSICAL_DEFENSE = "physical-defense"; public static final String MELEE_DEFENSE = "melee-defense"; public static final String PROJECTILE_DEFENSE = "projectile-defense"; public static final String SKILL_DAMAGE = "skill-damage"; public static final String SKILL_DEFENSE = "skill-defense"; public static final String MOVE_SPEED = "move-speed"; public static final String ATTACK_SPEED = "attack-speed"; public static final String ARMOR = "armor"; public static final String LUCK = "luck"; public static final String ARMOR_TOUGHNESS = "armor-toughness"; public static final String EXPERIENCE = "exp"; public static final String HUNGER = "hunger"; public static final String HUNGER_HEAL = "hunger-heal"; public static final String COOLDOWN = "cooldown"; public static final String KNOCKBACK_RESIST = "knockback-resist"; private final HashMap attributes = new LinkedHashMap<>(); private final HashMap lookup = new HashMap<>(); private final HashMap> byStat = new HashMap<>(); private final HashMap> byComponent = new HashMap<>(); /** * Sets up the attribute manager, loading the attribute * data from the configuration. This is handled by SkillAPI * automatically so other plugins should not instantiate * this class. * * @param api SkillAPI reference */ public AttributeManager(SkillAPI api) { load(api); } /** * Retrieves an attribute template * * @param key attribute key * @return template for the attribute */ public Attribute getAttribute(String key) { return lookup.get(key.toLowerCase()); } /** * Unsafe getter for the attribute data. *

* Do not use this method or modify it's return value unless * you know exactly what you are doing. * * @return attributes map */ public HashMap getAttributes() { return attributes; } public List forStat(final String key) { return byStat.get(key.toLowerCase()); } public List forComponent(final EffectComponent component, final String key) { return byComponent.get(component.getKey() + "-" + key.toLowerCase()); } /** * Retrieves the available attribute keys. This * does not include display names for attributes. * * @return set of available attribute keys */ public Set getKeys() { return attributes.keySet(); } /** * Retrieves the available attribute keys including * both display names and config keys. * * @return display name and config keys for attributes */ public Set getLookupKeys() { return lookup.keySet(); } /** * Normalizes a config key or name into the config key * for a unified identifier to store stats under. * * @param key key to normalize * @return config key */ public String normalize(String key) { final Attribute attribute = lookup.get(key.toLowerCase()); if (attribute == null) { throw new IllegalArgumentException("Invalid attribute - " + key); } return attribute.getKey(); } /** * Loads attribute data from the config * * @param api SkillAPI reference */ private void load(SkillAPI api) { CommentedConfig config = new CommentedConfig(api, "attributes"); config.saveDefaultConfig(); DataSection data = config.getConfig(); Logger.log(LogType.ATTRIBUTE_LOAD, 1, "Loading attributes..."); for (String key : data.keys()) { Logger.log(LogType.ATTRIBUTE_LOAD, 2, " - " + key); Attribute attribute = new Attribute(data.getSection(key), key); attributes.put(attribute.getKey(), attribute); lookup.put(attribute.getKey(), attribute); lookup.put(attribute.getName().toLowerCase(), attribute); } GUIData attribs = GUITool.getAttributesMenu(); if (!attribs.isValid()) { int i = 0; GUIPage page = attribs.getPage(0); for (String key : attributes.keySet()) { if (i < 54) { page.set(i++, key); } } attribs.resize((attributes.size() + 8) / 9); } } /** * A single attribute template */ public class Attribute implements IconHolder { private static final String DISPLAY = "display"; private static final String GLOBAL = "global"; private static final String CONDITION = "condition"; private static final String MECHANIC = "mechanic"; private static final String TARGET = "target"; private static final String STATS = "stats"; private static final String MAX = "max"; // iomatix: cost and the modifier private static final String COSTBASE = "cost_base"; private static final String COSTMOD = "cost_modifier"; // Attribute description private String key; private String display; private ItemStack icon; private int max; // iomatix: cost and the modifier private int costBase; private double costModifier; // Dynamic global modifiers private Map> dynamicModifiers = new EnumMap<>(ComponentType.class); // General stat modifiers private HashMap statModifiers = new HashMap<>(); /** * Creates a new attribute, loading the settings from the given * config data. * * @param data config data to load from * @param key the key the attribute was labeled under */ public Attribute(DataSection data, String key) { this.key = key.toLowerCase(); this.display = data.getString(DISPLAY, key); this.icon = Data.parseIcon(data); this.max = data.getInt(MAX, 999); // iomatix: base_cost and the modifier // e.g. per 0.3 increase -> 0.3=>0, 0.6=>0, 0.9=>0, 1.2=>1 (the first additional cost point) etc. this.costBase = data.getInt(COSTBASE, 1); this.costModifier = data.getDouble(COSTMOD, 0.0); // Load dynamic global settings DataSection globals = data.getSection(GLOBAL); if (globals != null) { loadGroup(globals.getSection(CONDITION), ComponentType.CONDITION); loadGroup(globals.getSection(MECHANIC), ComponentType.MECHANIC); loadGroup(globals.getSection(TARGET), ComponentType.TARGET); } // Load stat settings DataSection stats = data.getSection(STATS); if (stats != null) { for (String stat : stats.keys()) { loadStatModifier(stats, stat); } } } /** * Retrieves the config key of the attribute * * @return config key of the attribute */ public String getKey() { return key; } /** * Retrieves the name for the attribute * * @return name of the attribute */ public String getName() { return display; } /** * Retrieves the icon for the attribute * * @return icon of the attribute */ @Override public ItemStack getIcon(PlayerData data) { ItemStack item = new ItemStack(icon.getType()); ItemMeta iconMeta = icon.getItemMeta(); ItemMeta meta = item.getItemMeta(); if (meta != null && iconMeta != null) { meta.setDisplayName(filter(data, iconMeta.getDisplayName())); List iconLore = iconMeta.getLore(); List lore = iconLore != null ? iconLore.stream().map(iconLine -> filter(data, iconLine)).collect(Collectors.toList()) : new ArrayList<>(); if (meta instanceof Damageable) { ((Damageable) meta).setDamage(((Damageable) iconMeta).getDamage()); } if (iconMeta.hasCustomModelData()) { meta.setCustomModelData(iconMeta.getCustomModelData()); } meta.setLore(lore); item.setItemMeta(meta); } return DamageLoreRemover.removeAttackDmg(item); } @Override public boolean isAllowed(final Player player) { return true; } /** * Filters a line of the icon according to the player data * * @param data player data to use * @param text line of text to filter * @return filtered line */ private String filter(PlayerData data, String text) { return text .replace("{amount}", "" + data.getInvestedAttribute(key)) .replace("{total}", "" + data.getAttribute(key)) ; } /** * @return icon for the attribute for use in the GUI editor */ public ItemStack getToolIcon() { ItemStack icon = new ItemStack(this.icon.getType()); ItemMeta meta = icon.getItemMeta(); ItemMeta iconMeta = this.icon.getItemMeta(); if (meta == null || iconMeta == null) { return icon; } meta.setDisplayName(key); List lore = iconMeta.hasLore() ? iconMeta.getLore() : null; if (lore == null) { lore = new ArrayList<>(); } if (iconMeta.hasDisplayName()) lore.add(0, iconMeta.getDisplayName()); meta.setLore(lore); icon.setItemMeta(meta); return icon; } /** * Retrieves the max amount the attribute can be raised to * * @return max attribute amount */ public int getMax() { return max; } // iomatix: base_cost and the modifier /** * Retrieves the starting cost of the attribute upgrade. * * @return costBase amount */ public int getCostBase(){ return costBase; } /** * Retrieves the raw additional cost of the attribute upgrade. * It should be converted to int e.g. using (int) Math.floor function. * * @return costModifier */ public double getCostMod(){ return costModifier; } /** * Modifies a dynamic condition's value * * @param component component to modify for * @param key key of the value to modify * @param value base value * @param amount amount of attribute points * @return modified value */ public double modify(EffectComponent component, String key, double value, int amount) { key = component.getKey() + "-" + key.toLowerCase(); final Map map = dynamicModifiers.get(component.getType()); if (map.containsKey(key)) { AttributeValue[] list = map.get(key); for (AttributeValue attribValue : list) { if (attribValue.passes(component)) { return attribValue.apply(value, amount); } } } return value; } /** * Modifies a stat value * * @param key key of the stat * @param base base value of the stat * @param amount amount of attribute points * @return modified stat value */ public double modifyStat(String key, double base, int amount) { if (statModifiers.containsKey(key)) { return statModifiers.get(key).compute(base, amount); } return base; } /** * Loads a dynamic group globals settings into the given map * * @param data config data to load from * @param type the component type to load for */ private void loadGroup(DataSection data, ComponentType type) { if (data == null) { return; } final Map target = dynamicModifiers.computeIfAbsent(type, t -> new HashMap<>()); for (String key : data.keys()) { final String lower = key.toLowerCase(); Logger.log(LogType.ATTRIBUTE_LOAD, 2, " SkillMod: " + key); final String value = data.getString(key); final String[] formulas = value.split("\\|"); final AttributeValue[] values = new AttributeValue[formulas.length]; int i = 0; for (final String formula : formulas) { values[i++] = new AttributeValue(formula); } target.put(lower, values); if (!byComponent.containsKey(lower)) { byComponent.put(lower, new ArrayList<>()); } byComponent.get(lower).add(this); } } /** * Loads a stat modifier from the config data * * @param data config data to load from * @param key key of the stat modifier */ private void loadStatModifier(DataSection data, String key) { if (data.has(key)) { Logger.log(LogType.ATTRIBUTE_LOAD, 2, " StatMod: " + key); statModifiers.put( key, new Formula(data.getString(key, "v"), new CustomValue("v"), new CustomValue("a"))); if (!byStat.containsKey(key)) { byStat.put(key, new ArrayList<>()); } byStat.get(key).add(this); } } } /** * Represents one formula modifier for an attribute * that can have conditions */ public class AttributeValue { private Formula formula; private HashMap conditions = new HashMap<>(); /** * Loads the attribute value that starts with the formula * and can have as many conditions as desired after * * @param data data string for the value */ public AttributeValue(String data) { String[] pieces = data.split(":"); formula = new Formula(pieces[0], new CustomValue("v"), new CustomValue("a")); for (int i = 1; i < pieces.length; i++) { String[] sides = pieces[i].split("="); conditions.put(sides[0], sides[1]); Logger.log(LogType.ATTRIBUTE_LOAD, 3, " Condition: " + sides[0] + " / " + sides[1]); } } /** * Checks whether the formula should be applied to the component * * @param component component to check for conditions against * @return true if passes the conditions */ public boolean passes(EffectComponent component) { for (String key : conditions.keySet()) { if (!component.getSettings().getString(key).equalsIgnoreCase(conditions.get(key))) { return false; } } return true; } /** * Checks the conditions for the given component * * @param value base value * @param amount amount of attribute points * @return the modified value if the conditions passed or the base value if they failed */ public double apply(double value, int amount) { return formula.compute(value, amount); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy