From c1ecc198e32d111ee97cea35dd0fdb533ec469f9 Mon Sep 17 00:00:00 2001 From: Jules Date: Sat, 6 Apr 2024 15:50:19 -0700 Subject: [PATCH] Fixed abilities not transfering from gems --- .../crafting/recipe/CustomSmithingRecipe.java | 3 +- .../mmoitems/api/interaction/GemStone.java | 119 +-- .../api/item/build/ItemStackBuilder.java | 2 +- .../api/item/build/MMOItemBuilder.java | 2 +- .../mmoitems/api/item/mmoitem/MMOItem.java | 753 +++++++++--------- .../mmoitems/stat/data/AbilityData.java | 8 + .../mmoitems/stat/data/AbilityListData.java | 11 +- .../mmoitems/stat/data/GemSocketsData.java | 23 +- .../mmoitems/stat/data/type/Mergeable.java | 2 +- .../mmoitems/stat/type/DoubleStat.java | 38 +- .../mmoitems/stat/type/StatHistory.java | 541 +++---------- 11 files changed, 568 insertions(+), 934 deletions(-) diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CustomSmithingRecipe.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CustomSmithingRecipe.java index 321167fb..c5fc2d23 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CustomSmithingRecipe.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/crafting/recipe/CustomSmithingRecipe.java @@ -18,6 +18,7 @@ import net.Indyuce.mmoitems.api.interaction.GemStone; import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem; import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; +import net.Indyuce.mmoitems.api.player.PlayerData; import net.Indyuce.mmoitems.api.util.message.FFPMMOItems; import net.Indyuce.mmoitems.stat.Enchants; import net.Indyuce.mmoitems.stat.data.EnchantListData; @@ -601,7 +602,7 @@ public class CustomSmithingRecipe extends MythicRecipeOutput { continue; } // Ok proceed - GemStone asGem = new GemStone(p, m.newBuilder().buildNBT()); + GemStone asGem = new GemStone(PlayerData.get(p), m.newBuilder().buildNBT()); // Put GemStone.ApplyResult res = asGem.applyOntoItem(gen, gen.getType(), "", false, true); diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/interaction/GemStone.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/interaction/GemStone.java index caead8d0..02f06757 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/interaction/GemStone.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/interaction/GemStone.java @@ -45,8 +45,7 @@ public class GemStone extends UseItem { * Entirely loads the MMOItem and checks if * it has the required empty socket for the gem */ - MMOItem targetMMO = new LiveMMOItem(target); - return applyOntoItem(targetMMO, targetType, MMOUtils.getDisplayName(target.getItem()), true, false); + return applyOntoItem(new LiveMMOItem(target), targetType, MMOUtils.getDisplayName(target.getItem()), true, false); } @NotNull @@ -64,7 +63,7 @@ public class GemStone extends UseItem { // Checks if the gem supports the item type, or the item set, or a weapon String appliableTypes = getNBTItem().getString(ItemStats.ITEM_TYPE_RESTRICTION.getNBTPath()); - if (!appliableTypes.equals("") && (!targetType.isWeapon() || !appliableTypes.contains("WEAPON")) + if (!appliableTypes.isEmpty() && (!targetType.isWeapon() || !appliableTypes.contains("WEAPON")) && !appliableTypes.contains(targetType.getId())) return new ApplyResult(ResultType.NONE); @@ -89,79 +88,29 @@ public class GemStone extends UseItem { return new ApplyResult(ResultType.FAILURE); } - /* - * To not clear enchantments put by players - */ + // To not clear enchantments put by players Enchants.separateEnchantments(targetMMO); /* - * Gem stone can be successfully applied. apply stats then abilities and - * permanent effects. also REGISTER gem stone in the item gem stone list. + * Gemstone can be successfully applied. Apply stats then abilities and + * permanent effects. also REGISTER gemstone in the item gemstone list. */ LiveMMOItem gemMMOItem = new LiveMMOItem(getNBTItem()); GemstoneData gemData = new GemstoneData(gemMMOItem, foundSocketColor); /* * Now must apply the gem sockets data to the Stat History and then recalculate. - * - * Gotta, however, find the correct StatData to which apply it to. Damn this can - * be pretty complicated! + * Gotta, however, find the correct StatData to which apply it to. */ StatHistory gemStory = StatHistory.from(targetMMO, ItemStats.GEM_SOCKETS); - - // Original? - if (((GemSocketsData) gemStory.getOriginalData()).getEmptySocket(gemType) != null) { - //UPGRD//MMOItems.log("\u00a77Applied Gemstone @\u00a76Original\u00a77: \u00a73" + foundSocketColor); - - // Charmer - ((GemSocketsData) gemStory.getOriginalData()).apply(gemType, gemData); - - } else { - - // Check Gem gems are not supported >:l. Check the modifiers ig - boolean success = false; - for (UUID uid : gemStory.getAllModifiers()) { - - // Get that gem - GemSocketsData registeredGemData = (GemSocketsData) gemStory.getModifiersBonus(uid); - if (registeredGemData != null && registeredGemData.getEmptySocket(gemType) != null) { - //UPGRD//MMOItems.log("\u00a77Applied Gemstone @\u00a76Gemstone\u00a77: \u00a73" + foundSocketColor); - - // Charmer - success = true; - registeredGemData.apply(gemType, gemData); - break; - } - } - - if (!success) { - - for (StatData extraneousGem : gemStory.getExternalData()) { - - // Get that gem - GemSocketsData registeredGemData = (GemSocketsData) extraneousGem; - if (registeredGemData != null && registeredGemData.getEmptySocket(gemType) != null) { - //UPGRD//MMOItems.log("\u00a77Applied Gemstone @\u00a76External\u00a77: \u00a73" + foundSocketColor); - - // Charmer - registeredGemData.apply(gemType, gemData); - break; - } - } - } - } - - // Recalculate - //HSY//MMOItems.log(" \u00a73-\u00a7a- \u00a77Gem Application Recalculation \u00a73-\u00a7a-\u00a73-\u00a7a-\u00a73-\u00a7a-\u00a73-\u00a7a-"); + findEmptySocket(gemStory, gemType, gemData); targetMMO.setData(ItemStats.GEM_SOCKETS, gemStory.recalculate(targetMMO.getUpgradeLevel())); - //UPGRD//MMOItems.log("Applied Gemstone: \u00a73" + foundSocketColor); /* * Get the item's level, important for the GemScalingStat */ Integer levelIdentified = null; final String scaling = gemMMOItem.hasData(ItemStats.GEM_UPGRADE_SCALING) ? gemMMOItem.getData(ItemStats.GEM_UPGRADE_SCALING).toString() : GemUpgradeScaling.defaultValue; - //UPGRD//MMOItems.log("Scaling Identified: \u00a73" + scaling); switch (scaling) { case "HISTORIC": levelIdentified = 0; @@ -169,32 +118,18 @@ public class GemStone extends UseItem { case "SUBSEQUENT": levelIdentified = targetMMO.getUpgradeLevel(); break; - case "NEVER": default: break; } - gemData.setLevel(levelIdentified); - //UPGRD//MMOItems.log("Set Level: \u00a7b" + gemData.getLevel()); - /* - * Only applies NON PROPER and MERGEABLE item stats - */ + + // Only applies NON PROPER and MERGEABLE item stats for (ItemStat stat : gemMMOItem.getStats()) { + if (stat instanceof GemStoneStat) continue; - // If it is not PROPER - if (!(stat instanceof GemStoneStat)) { - - // Get the stat data - StatData data = gemMMOItem.getData(stat); - - // If the data is MERGEABLE - if (data instanceof Mergeable) { - //UPGRD//MMOItems.log("\u00a79>>> \u00a77Gem-Merging \u00a7c" + stat.getNBTPath()); - - // Merge into it - targetMMO.mergeData(stat, data, gemData.getHistoricUUID()); - } - } + final StatData data = gemMMOItem.getData(stat); + if (data instanceof Mergeable) + targetMMO.mergeData(stat, data, gemData.getHistoricUUID()); } if (!silent) { @@ -204,11 +139,37 @@ public class GemStone extends UseItem { if (buildStack) return new ApplyResult(targetMMO.newBuilder().build()); - else return new ApplyResult(targetMMO, ResultType.SUCCESS); } + private void findEmptySocket(StatHistory socketHistory, String gemType, GemstoneData gemstone) { + + // Og data + GemSocketsData data = ((GemSocketsData) socketHistory.getOriginalData()); + if (data.apply(gemType, gemstone)) return; + + // Modifiers + for (UUID modifierId : socketHistory.getAllModifiers()) { + data = (GemSocketsData) socketHistory.getModifiersBonus(modifierId); + if (data.apply(gemType, gemstone)) return; + } + + // External + for (StatData untypedData : socketHistory.getExternalData()) { + data = (GemSocketsData) untypedData; + if (data.apply(gemType, gemstone)) return; + } + + // Gems + for (UUID gemId : socketHistory.getAllGemstones()) { + data = (GemSocketsData) socketHistory.getGemstoneData(gemId); + if (data.apply(gemType, gemstone)) return; + } + + throw new RuntimeException("MMOItem contains available socket but not its socket stat history"); + } + public static class ApplyResult { @NotNull private final ResultType type; diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java index 980d09ab..84e4afb2 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/ItemStackBuilder.java @@ -168,7 +168,7 @@ public class ItemStackBuilder { builtMMOItem.setData(stat, s.recalculate(l)); // Add to NBT, if the gemstones were not purged - if (!s.isClear()) { + if (!s.isEmpty()) { //GEM//MMOItems.log("\u00a7a -+- \u00a77Recording History"); addItemTag(new ItemTag(history_keyword + stat.getId(), s.toNBTString())); diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/MMOItemBuilder.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/MMOItemBuilder.java index 50a49310..65ea2bf9 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/MMOItemBuilder.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/build/MMOItemBuilder.java @@ -145,7 +145,7 @@ public class MMOItemBuilder extends Buildable { */ public void applyData(@NotNull ItemStat stat, @NotNull StatData data) { final StatData found = mmoitem.getData(stat); - if (found != null && found instanceof Mergeable) ((Mergeable) found).mergeWith(data); + if (found != null && found instanceof Mergeable) ((Mergeable) found).mergeWith((Mergeable) data); else mmoitem.setData(stat, data); } diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/mmoitem/MMOItem.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/mmoitem/MMOItem.java index bc78c78c..6a7c3f06 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/mmoitem/MMOItem.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/item/mmoitem/MMOItem.java @@ -27,204 +27,188 @@ import java.util.*; @SuppressWarnings("unused") public class MMOItem implements ItemReference { - private final Type type; - private final String id; + private final Type type; + private final String id; - /** - * Where data about all the item stats is stored. When the item is - * generated, this map is read and all the stats are applied. The order in - * which stats are added is not very important anymore - */ - @NotNull - private final Map stats = new HashMap<>(); + /** + * Where data about all the item stats is stored. When the item is + * generated, this map is read and all the stats are applied. The order in + * which stats are added is not very important anymore + */ + @NotNull + private final Map stats = new HashMap<>(); - /** - * Constructor used to generate an ItemStack based on some stat data - * - * @param type - * The type of the item you want to create - * @param id - * The id of the item, make sure it is different from other - * existing items not to interfere with MI features like the - * dynamic item updater - */ - public MMOItem(Type type, String id) { - this.type = type; - this.id = id; - } + /** + * Constructor used to generate an ItemStack based on some stat data + * + * @param type The type of the item you want to create + * @param id The id of the item, make sure it is different from other + * existing items not to interfere with MI features like the + * dynamic item updater + */ + public MMOItem(Type type, String id) { + this.type = type; + this.id = id; + } - @Override - public Type getType() { return type; } + @Override + public Type getType() { + return type; + } - @Override - public String getId() { return id; } + @Override + public String getId() { + return id; + } - /** - * Will merge that data into this item: - *

- * If the item does not have this stat yet, it will be set with MMOItem.setData() - *

If this data is not Mergeable, it will also be set

- *

- * Otherwise, the data will be merged with the previous. If an UUID is provided, it will also be - * stored as a GemStone in the history, allowing to be removed from the item with that same UUID. - */ - public void mergeData(@NotNull ItemStat stat, @NotNull StatData data, @Nullable UUID associatedGemStone) { - //GEM//MMOItems.log("Merging stone stat \u00a76" + stat.getNBTPath() + "\u00a77 into \u00a7c" + getType().getName() + " " + getId()); + /** + * Will merge that data into this item: + *

+ * If the item does not have this stat yet, it will be set with MMOItem.setData() + *

If this data is not Mergeable, it will also be set

+ *

+ * Otherwise, the data will be merged with the previous. If an UUID is provided, it will also be + * stored as a GemStone in the history, allowing to be removed from the item with that same UUID. + */ + public void mergeData(@NotNull ItemStat stat, @NotNull StatData data, @Nullable UUID associatedGemStone) { + boolean bc = stat.equals(ItemStats.ABILITIES); - // Do we already have the data? - if (data instanceof Mergeable) { - //GEM//MMOItems.log("\u00a7a + \u00a77Mergeable"); + // Merge if possible, otherwise write over + if (data instanceof Mergeable) { - // Prepare to merge: Gather History (Also initializes the ORIGINAL stats) - StatHistory sHistory = StatHistory.from(this, stat); + // Prepare to merge, gather history + StatHistory sHistory = StatHistory.from(this, stat); + if (associatedGemStone != null) sHistory.registerGemstoneData(associatedGemStone, data); + else sHistory.registerExternalData(data); + setData(stat, sHistory.recalculate(getUpgradeLevel())); + } else + setData(stat, data); + } - // As GemStone or as External? - if (associatedGemStone != null) { - //GEM//MMOItems.log(" \u00a79++\u00a77 As Gemstone \u00a7b" + associatedGemStone.toString()); + public void setData(@NotNull ItemStat stat, @NotNull StatData data) { + stats.put(stat, data); + } - // As GemStone - sHistory.registerGemstoneData(associatedGemStone, data); + public void replaceData(@NotNull ItemStat stat, @NotNull StatData data) { + stats.replace(stat, data); + } - // As External - } else { - //GEM//MMOItems.log(" \u00a7c++\u00a77 As External"); + public void removeData(@NotNull ItemStat stat) { + stats.remove(stat); + } - // As External, UUIDless modifier - sHistory.registerExternalData(data); - } + public StatData getData(@NotNull ItemStat stat) { + return stats.get(stat); + } - // Recalculate - //GEM//MMOItems.log(" \u00a76+++\u00a77 Recalculating..."); - //HSY//MMOItems.log(" \u00a73-\u00a7a- \u00a77Gem Application Recalculation \u00a73-\u00a7a-\u00a73-\u00a7a-\u00a73-\u00a7a-\u00a73-\u00a7a-"); - setData(stat, sHistory.recalculate(getUpgradeLevel())); + public boolean hasData(@NotNull ItemStat stat) { + return (stats.get(stat) != null); + } - // Merging means replacing if it cannot be merged - } else { + /** + * @return Collection of all item stats which have some data on this mmoitem + */ + @NotNull + public Set getStats() { + return stats.keySet(); + } + //endregion - // Override Completely - setData(stat, data); - } - } + //region Building - public void setData(@NotNull ItemStat stat, @NotNull StatData data) { - stats.put(stat, data); - } + /** + * @return A class which lets you build this mmoitem into an ItemStack + */ + @NotNull + public ItemStackBuilder newBuilder() { + return new ItemStackBuilder(this); + } - public void replaceData(@NotNull ItemStat stat, @NotNull StatData data) { - stats.replace(stat, data); - } + /*** + * @return A cloned instance of this mmoitem. This does NOT clone the + * StatData instances! If you edit these statDatas, the previous + * mmoitem will be edited as well. + */ + @SuppressWarnings("MethodDoesntCallSuperMethod") + @Override + public MMOItem clone() { + MMOItem clone = new MMOItem(type, id); - public void removeData(@NotNull ItemStat stat) { - stats.remove(stat); - } + // Clone them stats and histories + for (ItemStat stat : stats.keySet()) { - public StatData getData(@NotNull ItemStat stat) { - return stats.get(stat); - } + // Copy Stat Datas themselves + clone.stats.put(stat, stats.get(stat)); - public boolean hasData(@NotNull ItemStat stat) { return (stats.get(stat) != null); } + // Copy Histories + StatHistory hist = getStatHistory(stat); + if (hist != null) { + final StatHistory histClone = hist.clone(); + histClone.setParent(clone); + clone.setStatHistory(stat, histClone); + } + } - /** - * @return Collection of all item stats which have some data on this mmoitem - */ - @NotNull public Set getStats() { - return stats.keySet(); - } - //endregion + // Thats it + return clone; + } + //endregion - //region Building and such - /** - * @return A class which lets you build this mmoitem into an ItemStack - */ - @NotNull public ItemStackBuilder newBuilder() { - return new ItemStackBuilder(this); - } + //region Stat History Stuff + /* + * When merging stats (like applying a gemstone), the item must remember which were + * its original stats, and from which gem stone came each stat, in order to allow + * removal of gem stones in the future. This is where that is remembered. + */ + @NotNull + private final Map mergeableStatHistory = new HashMap<>(); - /*** - * @return A cloned instance of this mmoitem. This does NOT clone the - * StatData instances! If you edit these statDatas, the previous - * mmoitem will be edited as well. - */ - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public MMOItem clone() { - MMOItem clone = new MMOItem(type, id); + /** + * Gets the history associated to this stat, if there is any + *

+ * A stat history is basically the memory of its original stats, + * from when it was created, its gemstone stats, + * those added by which gem, and its upgrade bonuses. + */ + @Nullable + public StatHistory getStatHistory(@NotNull ItemStat stat) { - // Clone them stats and histories - for (ItemStat stat : stats.keySet()) { + /* + * As an assumption for several enchantment recognition operations, + * Enchantment data must never be clear and lack history. This is + * the basis for when an item is 'old' + */ + if (stat instanceof Enchants) + return mergeableStatHistory.getOrDefault(stat, StatHistory.from(this, stat, true)); - // Copy Stat Datas themselves - clone.stats.put(stat, stats.get(stat)); + /* + * Normal stat, just fetch. + */ + try { - // Copy Histories - StatHistory hist = getStatHistory(stat); - if (hist != null) { - final StatHistory histClone = hist.clone(); - histClone.setParent(clone); - clone.setStatHistory(stat, histClone); - } - } + // Well that REALLY should work + return mergeableStatHistory.get(stat); - // Thats it - return clone; - } - //endregion + } catch (ClassCastException ignored) { + return null; + } + } - //region Stat History Stuff - /* - * When merging stats (like applying a gemstone), the item must remember which were - * its original stats, and from which gem stone came each stat, in order to allow - * removal of gem stones in the future. This is where that is remembered. - */ - @NotNull - private final Map mergeableStatHistory = new HashMap<>(); + @NotNull + public ArrayList getStatHistories() { + return new ArrayList<>(mergeableStatHistory.values()); + } - /** - * Gets the history associated to this stat, if there is any - *

- * A stat history is basically the memmory of its original stats, - * from when it was created, its gem stone stats, - * those added by which gem, and its upgrade bonuses. - */ - @Nullable - public StatHistory getStatHistory(@NotNull ItemStat stat) { - - /* - * As an assumption for several enchantment recognition operations, - * Enchantment data must never be clear and lack history. This is - * the basis for when an item is 'old' - */ - if (stat instanceof Enchants) - return mergeableStatHistory.getOrDefault(stat, StatHistory.from(this, stat, true)); - - /* - * Normal stat, just fetch. - */ - try { - - // Well that REALLY should work - return mergeableStatHistory.get(stat); - - } catch (ClassCastException ignored) { - return null; - } - } - - @NotNull - public ArrayList getStatHistories() { - return new ArrayList<>(mergeableStatHistory.values()); - } - - /** - * Sets the history associated to this stat. - *

- * A stat history is basically the memory of its original stats, - * from when it was created, its gemstone stats, those - * added by which gem, and its upgrade bonuses. - */ - public void setStatHistory(@NotNull ItemStat stat, @NotNull StatHistory hist) { - mergeableStatHistory.put(stat, hist); - } + /** + * Sets the history associated to this stat. + *

+ * A stat history is basically the memory of its original stats, + * from when it was created, its gemstone stats, those + * added by which gem, and its upgrade bonuses. + */ + public void setStatHistory(@NotNull ItemStat stat, @NotNull StatHistory hist) { + mergeableStatHistory.put(stat, hist); + } //region Other API @@ -246,272 +230,265 @@ public class MMOItem implements ItemReference { return found != null ? MMOItems.plugin.getLore().getTooltip(found.toString()) : null; } - /** - * A MMOItem from the template only has damage - * from the ITEM_DAMAGE stat - * - * @return The damage suffered by this item - */ - @Deprecated - public int getDamage() { + /** + * A MMOItem from the template only has damage + * from the ITEM_DAMAGE stat + * + * @return The damage suffered by this item + */ + @Deprecated + public int getDamage() { - // Does it use MMO Durability? - int maxDurability = hasData(ItemStats.MAX_DURABILITY) ? SilentNumbers.round(((DoubleData) getData(ItemStats.MAX_DURABILITY)).getValue()) : -1; + // Does it use MMO Durability? + int maxDurability = hasData(ItemStats.MAX_DURABILITY) ? SilentNumbers.round(((DoubleData) getData(ItemStats.MAX_DURABILITY)).getValue()) : -1; - if (maxDurability > 0) { + if (maxDurability > 0) { - // Apparently we must do this - NBTItem nbtItem = newBuilder().buildNBT(); + // Apparently we must do this + NBTItem nbtItem = newBuilder().buildNBT(); - // Durability - int durability = hasData(ItemStats.CUSTOM_DURABILITY) ? SilentNumbers.round(((DoubleData) getData(ItemStats.CUSTOM_DURABILITY)).getValue()) : maxDurability; + // Durability + int durability = hasData(ItemStats.CUSTOM_DURABILITY) ? SilentNumbers.round(((DoubleData) getData(ItemStats.CUSTOM_DURABILITY)).getValue()) : maxDurability; - // Damage is the difference between max and current durability - return maxDurability - durability; + // Damage is the difference between max and current durability + return maxDurability - durability; - } else { + } else { - // Use vanilla durability - return hasData(ItemStats.ITEM_DAMAGE) ? SilentNumbers.round(((DoubleData) getData(ItemStats.ITEM_DAMAGE)).getValue()) : 0; - } - } + // Use vanilla durability + return hasData(ItemStats.ITEM_DAMAGE) ? SilentNumbers.round(((DoubleData) getData(ItemStats.ITEM_DAMAGE)).getValue()) : 0; + } + } - /** - * A MMOItem from the template only has damage - * from the ITEM_DAMAGE stat - * - * @param damage The damage suffered by this item - */ - @Deprecated - public void setDamage(int damage) { + /** + * A MMOItem from the template only has damage + * from the ITEM_DAMAGE stat + * + * @param damage The damage suffered by this item + */ + @Deprecated + public void setDamage(int damage) { - // Too powerful - if (hasData(ItemStats.UNBREAKABLE)) { return; } + // Too powerful + if (hasData(ItemStats.UNBREAKABLE)) return; - // Does it use MMO Durability? - int maxDurability = hasData(ItemStats.MAX_DURABILITY) ? SilentNumbers.round(((DoubleData) getData(ItemStats.MAX_DURABILITY)).getValue()) : -1; + // Does it use MMO Durability? + int maxDurability = hasData(ItemStats.MAX_DURABILITY) ? SilentNumbers.round(((DoubleData) getData(ItemStats.MAX_DURABILITY)).getValue()) : -1; - if (maxDurability > 0) { + if (maxDurability > 0) { - // Apparently we must do this - NBTItem nbtItem = newBuilder().buildNBT(); + // Apparently we must do this + NBTItem nbtItem = newBuilder().buildNBT(); - // Durability - setData(ItemStats.CUSTOM_DURABILITY, new DoubleData(maxDurability - damage)); + // Durability + setData(ItemStats.CUSTOM_DURABILITY, new DoubleData(maxDurability - damage)); - // Scale damage - Material mat = hasData(ItemStats.MATERIAL) ? ((MaterialData) getData(ItemStats.MATERIAL)).getMaterial() : Material.GOLD_INGOT; - double multiplier = ((double) damage) * ((double) mat.getMaxDurability()) / ((double) maxDurability); - if (multiplier == 0) { return; } + // Scale damage + Material mat = hasData(ItemStats.MATERIAL) ? ((MaterialData) getData(ItemStats.MATERIAL)).getMaterial() : Material.GOLD_INGOT; + double multiplier = ((double) damage) * ((double) mat.getMaxDurability()) / ((double) maxDurability); + if (multiplier == 0) return; - // Set to some decent amount of damage - setData(ItemStats.ITEM_DAMAGE, new DoubleData(multiplier)); + // Set to some decent amount of damage + setData(ItemStats.ITEM_DAMAGE, new DoubleData(multiplier)); - } else { + } else { - // Use vanilla durability - setData(ItemStats.ITEM_DAMAGE, new DoubleData(damage)); - } - } - //endregion + // Use vanilla durability + setData(ItemStats.ITEM_DAMAGE, new DoubleData(damage)); + } + } + //endregion - //region Upgrading API + //region Upgrading API - /** - * Upgrades this MMOItem one level. - *

- * Make sure to check {@link #hasUpgradeTemplate()} before calling. - */ - public void upgrade() { + /** + * Upgrades this MMOItem one level. + *

+ * Make sure to check {@link #hasUpgradeTemplate()} before calling. + */ + public void upgrade() { - // Upgrade through the template's API - getUpgradeTemplate().upgrade(this); - } + // Upgrade through the template's API + getUpgradeTemplate().upgrade(this); + } - /** - * Whether or not this item has all the information - * required to call - */ - public boolean hasUpgradeTemplate() { - return hasData(ItemStats.UPGRADE) && ((UpgradeData) getData(ItemStats.UPGRADE)).getTemplate() != null; - } + /** + * Whether or not this item has all the information + * required to call + */ + public boolean hasUpgradeTemplate() { + return hasData(ItemStats.UPGRADE) && ((UpgradeData) getData(ItemStats.UPGRADE)).getTemplate() != null; + } - /** - * @return The upgrade level, or 0 if there is none. - */ - public int getUpgradeLevel() { - return hasData(ItemStats.UPGRADE) ? ((UpgradeData) getData(ItemStats.UPGRADE)).getLevel() : 0; - } + /** + * @return The upgrade level, or 0 if there is none. + */ + public int getUpgradeLevel() { + return hasData(ItemStats.UPGRADE) ? ((UpgradeData) getData(ItemStats.UPGRADE)).getLevel() : 0; + } - /** - * @return The upgrade level, or 0 if there is none. - */ - public int getMaxUpgradeLevel() { + /** + * @return The upgrade level, or 0 if there is none. + */ + public int getMaxUpgradeLevel() { - // Does it have Upgrade Data? - if (hasData(ItemStats.UPGRADE)) { + // Does it have Upgrade Data? + if (hasData(ItemStats.UPGRADE)) { - // Return the registered level. - return ((UpgradeData) getData(ItemStats.UPGRADE)).getMax(); - } + // Return the registered level. + return ((UpgradeData) getData(ItemStats.UPGRADE)).getMax(); + } - // Nope? Well its level 0 I guess. - return 0; - } + // Nope? Well its level 0 I guess. + return 0; + } - /** - * Make sure to check {@link #hasUpgradeTemplate()} before calling. - *

- * This will fail and throw an exception if the MMOItem has no upgrade template. - * @return The upgrade template by which the MMOItem would upgrade normally. - */ - @SuppressWarnings("ConstantConditions") - @NotNull public UpgradeTemplate getUpgradeTemplate() { - Validate.isTrue(hasUpgradeTemplate(), "Item has no upgrade information"); + /** + * Make sure to check {@link #hasUpgradeTemplate()} before calling. + *

+ * This will fail and throw an exception if the MMOItem has no upgrade template. + * + * @return The upgrade template by which the MMOItem would upgrade normally. + */ + @SuppressWarnings("ConstantConditions") + @NotNull + public UpgradeTemplate getUpgradeTemplate() { + Validate.isTrue(hasUpgradeTemplate(), "Item has no upgrade information"); - // All Right - UpgradeData data = (UpgradeData) getData(ItemStats.UPGRADE); + // All Right + UpgradeData data = (UpgradeData) getData(ItemStats.UPGRADE); - // That's the template - return data.getTemplate(); - } + // That's the template + return data.getTemplate(); + } - /** - * Get the list of GemStones inserted into this item - */ - @NotNull - public Set getGemStones() { + @NotNull + public List getGemstones() { + final StatData data = getData(ItemStats.GEM_SOCKETS); + return data == null ? new ArrayList<>() : ((GemSocketsData) data).getGems(); + } - // Got gem sockets? - if (hasData(ItemStats.GEM_SOCKETS)) { + /** + * @see #getGemstones() + */ + @Deprecated + public Set getGemStones() { + return new HashSet<>(getGemstones()); + } + //endregion - // Get Data - GemSocketsData data = (GemSocketsData) getData(ItemStats.GEM_SOCKETS); + //region Gem Sockets API - // Thats it - return data.getGemstones(); + /** + * It is not 100% fool-proof, since some GemStones just don't have + * enough information to be extracted (legacy gemstones). + *

+ * Note that this is somewhat an expensive operation, restrain + * from calling it much because it completely loads all the stats + * of every Gem Stone. + * + * @return The list of GemStones contained here. + */ + @NotNull + public List> extractGemstones() { - // Has no gem sockets - } else { + // Found? + final @Nullable GemSocketsData thisSocketsData = (GemSocketsData) getData(ItemStats.GEM_SOCKETS); + if (thisSocketsData == null) + return new ArrayList<>(); - // Empty Set - return new HashSet<>(); - } - } - //endregion + // Find restored items + final List> pairs = new ArrayList<>(); + for (GemstoneData gem : thisSocketsData.getGemstones()) { + final MMOItem restored = MMOItems.plugin.getMMOItem(MMOItems.plugin.getTypes().get(gem.getMMOItemType()), gem.getMMOItemID()); + if (restored != null) + pairs.add(Pair.of(gem, restored)); + } - //region Gem Sockets API + // If RevID updating, no need to identify stats + if (MMOItemReforger.gemstonesRevIDWhenUnsocket) + return pairs; - /** - * It is not 100% fool proof, since some GemStones just don't have - * enough information to be extracted (legacy gemstones). - *

- * Note that this is somewhat an expensive operation, restrain - * from calling it much because it completely loads all the stats - * of every Gem Stone. - * - * @return The list of GemStones contained here. - */ - @NotNull - public List> extractGemstones() { + // Identify actual stats + for (StatHistory hist : mergeableStatHistory.values()) + for (Pair gem : pairs) { + final StatData historicGemData = hist.getGemstoneData(gem.getKey().getHistoricUUID()); + if (historicGemData != null) + gem.getValue().setData(hist.getItemStat(), historicGemData); + } - // Found? - final @Nullable GemSocketsData thisSocketsData = (GemSocketsData) getData(ItemStats.GEM_SOCKETS); - if (thisSocketsData == null) - return new ArrayList<>(); + return pairs; + } - // Find restored items - final List> pairs = new ArrayList<>(); - for (GemstoneData gem : thisSocketsData.getGemstones()) { - final MMOItem restored = MMOItems.plugin.getMMOItem(MMOItems.plugin.getTypes().get(gem.getMMOItemType()), gem.getMMOItemID()); - if (restored != null) - pairs.add(Pair.of(gem, restored)); - } + /** + * Extracts a single gemstone. Note that this only builds the original Gemstone MMOItem, and if you + * wish to actually remove the GemStone, you must do so through {@link #removeGemStone(UUID, String)} + * + * @param gem Gemstone that you believe is in here + * @return The gemstone as it was when inserted, or null + * if such gemstone is not in here. + * @see #extractGemstones() More optimized method for extracting all gemstones at the same time. + */ + @Nullable + public MMOItem extractGemstone(@NotNull GemstoneData gem) { - // If RevID updating, no need to identify stats - if (MMOItemReforger.gemstonesRevIDWhenUnsocket) - return pairs; + final MMOItem restored = MMOItems.plugin.getMMOItem(MMOItems.plugin.getTypes().get(gem.getMMOItemType()), gem.getMMOItemID()); + if (restored == null) + return null; - // Identify actual stats - for (StatHistory hist : mergeableStatHistory.values()) - for (Pair gem : pairs) { - final StatData historicGemData = hist.getGemstoneData(gem.getKey().getHistoricUUID()); - if (historicGemData != null) - gem.getValue().setData(hist.getItemStat(), historicGemData); - } + // If RevID updating, no need to identify stats + if (MMOItemReforger.gemstonesRevIDWhenUnsocket) + return restored; - return pairs; - } + // Identify actual stats + for (StatHistory hist : mergeableStatHistory.values()) { + final StatData historicGemData = hist.getGemstoneData(gem.getHistoricUUID()); + if (historicGemData != null) + restored.setData(hist.getItemStat(), historicGemData); + } - /** - * Extracts a single gemstone. Note that this only builds the original Gemstone MMOItem, and if you - * wish to actually remove the GemStone, you must do so through {@link #removeGemStone(UUID, String)} - * - * @param gem Gemstone that you believe is in here - * @return The gemstone as it was when inserted, or null - * if such gemstone is not in here. - * @see #extractGemstones() More optimized method for extracting all gemstones at the same time. - */ - @Nullable - public MMOItem extractGemstone(@NotNull GemstoneData gem) { + return restored; + } - final MMOItem restored = MMOItems.plugin.getMMOItem(MMOItems.plugin.getTypes().get(gem.getMMOItemType()), gem.getMMOItemID()); - if (restored == null) - return null; + /** + * Deletes this UUID from all stat histories. + * + * @param gemUUID UUID of gem to remove + * @param color Color of the gem socket to restore. {@code null} to not restore socket. + */ + @SuppressWarnings("ConstantConditions") + public void removeGemStone(@NotNull UUID gemUUID, @Nullable String color) { - // If RevID updating, no need to identify stats - if (MMOItemReforger.gemstonesRevIDWhenUnsocket) - return restored; + // Get gemstone data + if (!hasData(ItemStats.GEM_SOCKETS)) + return; - // Identify actual stats - for (StatHistory hist : mergeableStatHistory.values()) { - final StatData historicGemData = hist.getGemstoneData(gem.getHistoricUUID()); - if (historicGemData != null) - restored.setData(hist.getItemStat(), historicGemData); - } + //GEM//MMOItems.log("\u00a7b-\u00a78-\u00a79-\u00a77 Extracting \u00a7e" + gemUUID.toString()); + StatHistory gemStory = StatHistory.from(this, ItemStats.GEM_SOCKETS); - return restored; - } + /* + * We must only find the StatData where this gem resides, + * and eventually the StatHistories of the affected stats + * will purge themselves from extraneous gems (that are + * no longer registered onto the GEM_SOCKETS history). + */ + if (((GemSocketsData) gemStory.getOriginalData()).removeGem(gemUUID, color)) + return; - /** - * Deletes this UUID from all stat histories. - * - * @param gemUUID UUID of gem to remove - * @param color Color of the gem socket to restore. {@code null} to not restore socket. - */ - @SuppressWarnings("ConstantConditions") - public void removeGemStone(@NotNull UUID gemUUID, @Nullable String color) { + // Attempt gems + for (UUID gemDataUUID : gemStory.getAllGemstones()) + if (((GemSocketsData) gemStory.getGemstoneData(gemDataUUID)).removeGem(gemUUID, color)) + return; - // Get gemstone data - if (!hasData(ItemStats.GEM_SOCKETS)) - return; + // Attempt externals + for (StatData externalData : gemStory.getExternalData()) + if (((GemSocketsData) externalData).removeGem(gemUUID, color)) + return; - //GEM//MMOItems.log("\u00a7b-\u00a78-\u00a79-\u00a77 Extracting \u00a7e" + gemUUID.toString()); - StatHistory gemStory = StatHistory.from(this, ItemStats.GEM_SOCKETS); - - /* - * We must only find the StatData where this gem resides, - * and eventually the StatHistories of the affected stats - * will purge themselves from extraneous gems (that are - * no longer registered onto the GEM_SOCKETS history). - */ - if (((GemSocketsData) gemStory.getOriginalData()).removeGem(gemUUID, color)) - return; - - // Attempt gems - for (UUID gemDataUUID : gemStory.getAllGemstones()) - if (((GemSocketsData) gemStory.getGemstoneData(gemDataUUID)).removeGem(gemUUID, color)) - return; - - // Attempt externals - for (StatData externalData : gemStory.getExternalData()) - if (((GemSocketsData) externalData).removeGem(gemUUID, color)) - return; - - // Attempt gems - for (UUID gemDataUUID : gemStory.getAllModifiers()) - if (((GemSocketsData) gemStory.getModifiersBonus(gemDataUUID)).removeGem(gemUUID, color)) - return; - } - //endregion + // Attempt gems + for (UUID gemDataUUID : gemStory.getAllModifiers()) + if (((GemSocketsData) gemStory.getModifiersBonus(gemDataUUID)).removeGem(gemUUID, color)) + return; + } + //endregion } diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java index 01610d54..77b9b994 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityData.java @@ -168,6 +168,14 @@ public class AbilityData extends Skill { return ability.equals(that.ability) && getTrigger().equals(that.getTrigger()) && modifiers.equals(that.modifiers); } + @Override + public String toString() { + return "AbilityData{" + + "ability=" + ability + + ", modifiers=" + modifiers + + '}'; + } + @Override public int hashCode() { return Objects.hash(ability, modifiers); diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityListData.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityListData.java index 36143462..32fb4fae 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityListData.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/AbilityListData.java @@ -38,7 +38,9 @@ public class AbilityListData implements StatData, Mergeable { @NotNull @Override public AbilityListData clone() { - return new AbilityListData(); + final AbilityListData clone = new AbilityListData(); + clone.abilities.addAll(this.abilities); + return clone; } @Override @@ -76,6 +78,13 @@ public class AbilityListData implements StatData, Mergeable { return true; } + @Override + public String toString() { + return "AbilityListData{" + + "abilities=" + abilities + + '}'; + } + @Override public boolean isEmpty() { return abilities.isEmpty(); diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/GemSocketsData.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/GemSocketsData.java index 52baf3e3..c11148e6 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/GemSocketsData.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/GemSocketsData.java @@ -23,7 +23,7 @@ import java.util.*; */ public class GemSocketsData implements StatData, Mergeable, RandomStatData { @NotNull - private final Set gems = new LinkedHashSet<>(); + private final List gems = new ArrayList<>(); @NotNull private final List emptySlots; @@ -55,7 +55,7 @@ public class GemSocketsData implements StatData, Mergeable, Rand @Nullable public String getEmptySocket(@NotNull String gem) { for (String slot : emptySlots) - if (gem.equals("") || slot.equals(getUncoloredGemSlot()) || gem.equals(slot)) + if (gem.isEmpty() || slot.equals(getUncoloredGemSlot()) || gem.equals(slot)) return slot; return null; } @@ -70,9 +70,12 @@ public class GemSocketsData implements StatData, Mergeable, Rand gems.add(gem); } - public void apply(String gem, GemstoneData gemstone) { - emptySlots.remove(getEmptySocket(gem)); + public boolean apply(String gem, GemstoneData gemstone) { + final String matchingSocket = getEmptySocket(gem); + if (matchingSocket == null) return false; + emptySlots.remove(matchingSocket); gems.add(gemstone); + return true; } public void addEmptySlot(@NotNull String slot) { @@ -84,8 +87,18 @@ public class GemSocketsData implements StatData, Mergeable, Rand return emptySlots; } - @NotNull + /** + * @see #getGems() + * @deprecated Gems are stored inside a list + * rather than inside a linked hash set. + */ + @Deprecated public Set getGemstones() { + return new HashSet<>(gems); + } + + @NotNull + public List getGems() { return gems; } diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/type/Mergeable.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/type/Mergeable.java index 0574075d..ac1c6ce4 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/type/Mergeable.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/data/type/Mergeable.java @@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull; * Strongly encouraged to override the equals method * to something fitting here as Mergeable stats should support comparisons. */ -public interface Mergeable extends StatData { +public interface Mergeable> extends StatData { /** * Merging two stat datas is required when an item benefits from diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/DoubleStat.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/DoubleStat.java index 38092e41..763fd380 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/DoubleStat.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/DoubleStat.java @@ -354,46 +354,14 @@ public class DoubleStat extends ItemStat impleme @NotNull @Override public StatData apply(@NotNull StatData original, @NotNull UpgradeInfo info, int level) { - - // Must be DoubleData - int i = level; if (original instanceof DoubleData && info instanceof DoubleUpgradeInfo) { - - // Get value + int i = level; double value = ((DoubleData) original).getValue(); - - // If leveling up - if (i > 0) { - - // While still positive - while (i > 0) { - - // Apply PMP Operation Positively - value = ((DoubleUpgradeInfo) info).getPMP().apply(value); - - // Decrease - i--; - } - - // Degrading the item - } else if (i < 0) { - - // While still negative - while (i < 0) { - - // Apply PMP Operation Reversibly - value = ((DoubleUpgradeInfo) info).getPMP().reverse(value); - - // Decrease - i++; - } - } - - // Update + while (i-- > 0) value = ((DoubleUpgradeInfo) info).getPMP().apply(value); + while (i++ < 0) value = ((DoubleUpgradeInfo) info).getPMP().reverse(value); ((DoubleData) original).setValue(value); } - // Upgraded return original; } diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/StatHistory.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/StatHistory.java index 4ed40d51..dc0e382f 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/StatHistory.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/stat/type/StatHistory.java @@ -14,7 +14,6 @@ import net.Indyuce.mmoitems.api.util.message.FFPMMOItems; import net.Indyuce.mmoitems.stat.data.EnchantListData; import net.Indyuce.mmoitems.stat.data.GemSocketsData; import net.Indyuce.mmoitems.stat.data.GemstoneData; -import net.Indyuce.mmoitems.stat.data.StringListData; import net.Indyuce.mmoitems.stat.data.type.Mergeable; import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo; @@ -43,10 +42,10 @@ public class StatHistory { private ArrayList perExternalData = new ArrayList<>(); private HashMap perGemstoneData = new HashMap<>(); - private StatHistory(@NotNull MMOItem ofItem, @NotNull ItemStat ofStat, @NotNull StatData ogData) { - itemStat = ofStat; - originalData = ogData; - parent = ofItem; + private StatHistory(@NotNull MMOItem parentItem, @NotNull ItemStat parentStat, @NotNull StatData parentData) { + itemStat = parentStat; + originalData = parentData; + parent = parentItem; } /** @@ -78,36 +77,20 @@ public class StatHistory { * @return Sure there is a Stat History and all but, does it * actually have any information apart from the OG Data? */ - public boolean isClear() { + public boolean isEmpty() { /* * Enchant list data is not clear even if redundant. - * * Its an important assumption in several methods - * like Enchants.separateEnchantments() + * like Enchants#separateEnchantments() */ - if (getOriginalData() instanceof EnchantListData) { - if (((EnchantListData) getOriginalData()).getEnchants().size() != 0) { - //CLR//MMOItems.log("\u00a7a -+- \u00a77Found Enchantments, \u00a7cnot clear. \u00a78{\u00a77" + getItemStat().getId() + "\u00a78}"); - return false; - } - } - - // Any gemstones or external SH? Then its NOT CLEAR - if (getAllGemstones().size() > 0 || getExternalData().size() > 0 || getAllModifiers().size() > 0) { - //CLR//MMOItems.log("\u00a7a -+- \u00a77Found Gemstones / ESH, \u00a7cnot clear. \u00a78{\u00a77" + getItemStat().getId() + "\u00a78}"); + if (getOriginalData() instanceof EnchantListData && ((EnchantListData) getOriginalData()).getEnchants().size() != 0) return false; - } - // Is it clear? - if (getOriginalData().isEmpty() && (!isUpgradeable() || getMMOItem().getUpgradeLevel() == 0)) { - //CLR//MMOItems.log("\u00a7a -+- \u00a77Original data is clear & unupgraded, \u00a7aclear. \u00A73(\u00a78Upgradeable? \u00a7b" + isUpgradeable() + "\u00a78, Upgrade Level:\u00a7b " + getMMOItem().getUpgradeLevel() + "\u00a73) \u00a78{\u00a77" + getItemStat().getId() + "\u00a78}"); - return true; - } + // Any gemstones, external or modifier data? + if (getAllGemstones().size() > 0 || getExternalData().size() > 0 || getAllModifiers().size() > 0) return false; - // Exactly the same as the MMOItem? [This check should basically always be true though] - //CLR//if (getOriginalData().equals(getMMOItem().getData(getItemStat()))) { MMOItems.log("\u00a7a -+- \u00a77Original data has never been merged, \u00a7aclear. \u00a78{\u00a77" + getItemStat().getId() + "\u00a78}"); } - return getOriginalData().equals(getMMOItem().getData(getItemStat())); + return true; } public void setParent(@NotNull MMOItem parent) { @@ -119,7 +102,8 @@ public class StatHistory { * Presumably from when it was first generated. */ public void setOriginalData(@NotNull StatData s) { - originalData = Objects.requireNonNull(s, "Original data cannot be null"); + Validate.notNull(s, "Original data cannot be null"); + originalData = s; } /** @@ -137,6 +121,8 @@ public class StatHistory { * were rolled when the item was first created. */ public void registerModifierBonus(@NotNull UUID of, @NotNull StatData data) { + Validate.notNull(of, "Modifier UUID cannot be null"); + Validate.notNull(data, "Stat data cannot be null"); perModifierBonus.put(of, data); } @@ -199,6 +185,8 @@ public class StatHistory { * The value of this stat data will be +5.5 */ public void registerGemstoneData(@NotNull UUID of, @NotNull StatData data) { + Validate.notNull(of, "Gemstone ID cannot be null"); + Validate.notNull(data, "Stat data cannot be null"); perGemstoneData.put(of, data); } @@ -224,20 +212,15 @@ public class StatHistory { return perExternalData; } - @Deprecated - public void consolidateEXSH() { - collapseExternalData(); - } - /** - * Collapses all external stat datas - * of unknown source into only one. + * Merges all external stat datas of unknown + * origin into one stat data instance. */ - public void collapseExternalData() { + public void fuseExternalData() { // Create Clear StatData theEXSH = getItemStat().getClearStatData(); - for (StatData ex : getExternalData()) if (ex != null) ((Mergeable) theEXSH).mergeWith(ex); + for (StatData ex : getExternalData()) ((Mergeable) theEXSH).mergeWith((Mergeable) ex); // Clear and Register getExternalData().clear(); @@ -253,6 +236,7 @@ public class StatHistory { * Well, I guess whatever plugin is putting them here may remove them by editing the list directly with StatHistory.getExternalData() */ public void registerExternalData(@NotNull StatData data) { + Validate.notNull(data, "Stat data cannot be null"); perExternalData.add(data); } @@ -323,86 +307,33 @@ public class StatHistory { original = ((Mergeable) original).clone(); //UPGRD//MMOItems.log("\u00a7a +\u00a77 Found original data\u00a7f " + original); } - //LVL//MMOItems.log(" \u00a7d*\u00a77-\u00a7a-\u00a763? \u00a77Lvl: \u00a7b" + parentItem.getUpgradeLevel() + "\u00a7d-\u00a77-\u00a7a-\u00a7d-\u00a77-\u00a7a-"); - // Create new + // Create and register new hist = new StatHistory(parentItem, stat, original); - - //LVL//MMOItems.log(" \u00a7d*\u00a77-\u00a7a-\u00a764? \u00a77Lvl: \u00a7b" + parentItem.getUpgradeLevel() + "\u00a7d-\u00a77-\u00a7a-\u00a7d-\u00a77-\u00a7a-"); - // Append to the item parentItem.setStatHistory(stat, hist); - - //LVL//MMOItems.log(" \u00a7d*\u00a77-\u00a7a-\u00a765? \u00a77Lvl: \u00a7b" + parentItem.getUpgradeLevel() + "\u00a7d-\u00a77-\u00a7a-\u00a7d-\u00a77-\u00a7a-"); - - //LVL//MMOItems.log(" \u00a7d*\u00a77*\u00a7a*\u00a766? \u00a77Lvl: \u00a7b" + hist.getMMOItem().getUpgradeLevel() + "\u00a7d-\u00a77-\u00a7a-\u00a7d-\u00a77-\u00a7a-"); - // Thats it return hist; } /** * Checks the item and makes sure that the UUIDs attributed to gemstones - * link to existing gem stones. Removes them if no such gemstone exists. + * link to existing gemstones. Removes them if no such gemstone exists. */ public void purgeGemstones() { - // Which will get purged... - ArrayList extraneous = new ArrayList<>(); + // No socket history can be found => clear all gems GemSocketsData data = (GemSocketsData) getMMOItem().getData(ItemStats.GEM_SOCKETS); if (data == null) { - data = new GemSocketsData(new ArrayList<>()); + perGemstoneData.clear(); + return; } - // For each UUID - for (UUID gem : perGemstoneData.keySet()) { - - // Check Gemstones - boolean success = false; - for (GemstoneData indiv : data.getGemstones()) { - - // Not null - if (indiv != null) { - - // Equal in UUID - if (gem.equals(indiv.getHistoricUUID())) { - - success = true; - break; - } - } - } - - // No success? - if (!success) { - - // No gemstone matched - extraneous.add(gem); - } + // Pb: intersecting between List and HashTable + HashMap newPerGemstoneData = new HashMap<>(); + for (GemstoneData gemData : data.getGemstones()) { + final StatData found = perGemstoneData.get(gemData.getHistoricUUID()); + if (found != null) newPerGemstoneData.put(gemData.getHistoricUUID(), found); } - - // Unregister - for (UUID ext : extraneous) { - //RECALCULATE//MMOItems.log("\u00a76 ||\u00a77 Purged Stone: \u00a7e" + ext.toString() + "\u00a78 (\u00a73" + getItemStat().getId() + "\u00a78)"); - - // Remove - removeGemData(ext); - } - } - - /** - * @return If this stat changes when the MMOItem is upgraded. - */ - public boolean isUpgradeable() { - - // No upgrades no possible - if (!getMMOItem().hasUpgradeTemplate()) { - return false; - } - - // Get Upgrade Info? - UpgradeInfo inf = getMMOItem().getUpgradeTemplate().getUpgradeInfo(getItemStat()); - - // No Upgrade Information? Looks like you're calculating as a normal merge stat - return inf != null; + perGemstoneData = newPerGemstoneData; } /** @@ -416,70 +347,21 @@ public class StatHistory { return recalculate(true, level); } - /** - * This recalculates final value of the stats of the item. - *

- * This will not apply the changes, it will just give you the final - * StatData that shall be applied (used when upgrading). - * - * @param withPurge Check if the gemstones UUIDs are valid. - * Leave true unless you know - * what you're doing. - */ - @NotNull - public StatData recalculate(boolean withPurge, int level) { - //RECALCULATE//MMOItems.log("\u00a7d|||\u00a77 Recalculating \u00a7f" + getItemStat().getNBTPath() + "\u00a77, Purge? \u00a7e" + withPurge); - - if (withPurge) { - purgeGemstones(); - } - - // If its upgradeable and not level ZERO, it must apply upgrades - //UPGRD//MMOItems.log("\u00a7d|\u00a79|\u00a76|\u00a77 Upgradeable Requirements: "); - //UPGRD//MMOItems.log(" \u00a76|\u00a77 Upgrade Level: \u00a7e" + level); - //UPGRD//MMOItems.log(" \u00a76|\u00a77 Upgradeable Stat: \u00a7e" + (getItemStat() instanceof Upgradable)); - //UPGRD//MMOItems.log(" \u00a76|\u00a77 Template Exists: \u00a7e" + (getMMOItem().hasUpgradeTemplate())); - if ((level != 0) && - (getItemStat() instanceof Upgradable) && - (getMMOItem().hasUpgradeTemplate())) { - - // Recalculate upgrading - return recalculateUpgradeable(level); - } - - // Merge Normally - return recalculateMergeable(); - } - /** * This recalculates values accounting only for gemstones and external data. - *

* In case someone was wondered the contribution of upgrading the item, just - * substract it from {@link #recalculate(int)} + * subtract it from {@link #recalculate(int)}. */ @NotNull public StatData recalculateUnupgraded() { - return recalculateUnupgraded(true); + return recalculate(true, null); } - /** - * This recalculates values accounting only for gemstones and external data. - *

- * In case someone was wondered the contribution of upgrading the item, just - * substract it from {@link #recalculate(int)} - * - * @param withPurge Check if the gemstones UUIDs are valid. - * Leave true unless you know - * what you're doing. - */ - @NotNull - public StatData recalculateUnupgraded(boolean withPurge) { - if (withPurge) { - purgeGemstones(); - } - - // Merge Normally - return recalculateMergeable(); + private int findLevel(int upgradeLevel, UUID gemstoneId) { + for (GemstoneData gemstone : getMMOItem().getGemstones()) + if (gemstone.getHistoricUUID().equals(gemstoneId)) + return gemstone.isScaling() ? gemstone.getLevel() : upgradeLevel; + throw new IllegalArgumentException("Could not find level of gem " + gemstoneId); } /** @@ -492,131 +374,39 @@ public class StatHistory { *

4: Sums Gem Stone Data (which should be scaled accordingly [Upgrades are entirely merged into their data]) *

5: Sums external data (modifiers that are not linked to an ID, I suppose by external plugins). */ - private StatData recalculateUpgradeable(int lvl) { - //RECALCULATE//MMOItems.log("\u00a76|||\u00a77 Calculating \u00a7f" + getItemStat().getNBTPath() + "\u00a77 as Upgradeable"); + @NotNull + public StatData recalculate(boolean purgeFirst, @Nullable Integer upgradeLevel) { + if (purgeFirst) purgeGemstones(); - // Get Upgrade Info? - UpgradeInfo inf = getMMOItem().getUpgradeTemplate().getUpgradeInfo(getItemStat()); - - // No Upgrade Information? Looks like you're calculating as a normal merge stat - if (inf == null) { - return recalculateMergeable(); - } + final UpgradeInfo upgradeInfo = upgradeLevel != null && + upgradeLevel != 0 && + getItemStat() instanceof Upgradable ? + getMMOItem().getUpgradeTemplate().getUpgradeInfo(getItemStat()) : null; // Clone original - StatData ogCloned = ((Mergeable) originalData).clone(); - //DBL//if (ogCloned instanceof DoubleData) MMOItems.log("\u00a76 >\u00a77 Original Base: \u00a7e" + ((DoubleData) ogCloned).getValue() + "\u00a78 {Original:\u00a77 " + ((DoubleData) getOriginalData()).getValue() + "\u00a78}"); + Mergeable finalData = ((Mergeable) originalData).clone(); - // Add Modifiers (who are affected by upgrades as if they was the base item data - for (UUID d : perModifierBonus.keySet()) { - - //DBL//if (getModifiersBonus() instanceof DoubleData) MMOItems.log("\u00a76 >\u00a7c> \u00a77 Modifier Base: \u00a7e" + ((DoubleData) getModifiersBonus()).getValue()); - // Just merge ig - ((Mergeable) ogCloned).mergeWith(((Mergeable) getModifiersBonus(d)).clone()); - } + // Add modifiers (affected by upgrades as if they were base item data) + for (UUID modifierId : perModifierBonus.keySet()) finalData.mergeWith((Mergeable) getModifiersBonus(modifierId)); // Level up - //RECALCULATE//MMOItems.log("\u00a76 ||\u00a77 Item Level: \u00a7e" + lvl); - StatData ret = ((Upgradable) getItemStat()).apply(ogCloned, inf, lvl); - //DBL//if (ret instanceof DoubleData) MMOItems.log("\u00a76 >\u00a77 Leveled Base: \u00a7e" + ((DoubleData) ret).getValue() + "\u00a78 {Original:\u00a77 " + ((DoubleData) getOriginalData()).getValue() + "\u00a78}"); + if (upgradeInfo != null) + finalData = (Mergeable) ((Upgradable) getItemStat()).apply(finalData, upgradeInfo, upgradeLevel); // Add up gemstones - for (UUID d : perGemstoneData.keySet()) { - - // Identify insertion level (When was the gemstone put into the item? - int level = 0; - - // Whats this gemstone's upgrade level? - for (GemstoneData gData : getMMOItem().getGemStones()) { - if (gData == null) { - continue; - } - //RECALCULATE//MMOItems.log("\u00a76 -\u00a7b-\u00a76-\u00a77 Gemstone " + gData.getName() + "\u00a77 " + gData.getHistoricUUID().toString()); - - // Find that one of matching UUID - if (gData.getHistoricUUID().equals(d)) { - - if (gData.isScaling()) { - - // Ok - level = gData.getLevel(); - //RECALCULATE//MMOItems.log("\u00a76 -\u00a7b-\u00a76-\u00a7a- Found:\u00a77" + level); - - } else { - - // No scaling - level = lvl; - //RECALCULATE//MMOItems.log("\u00a76 -\u00a7b-\u00a76-\u00a7a- Found,\u00a77 Unscaling"); - } - } + for (UUID gemstoneId : perGemstoneData.keySet()) { + Mergeable gsData = (Mergeable) getGemstoneData(gemstoneId); + if (upgradeInfo != null) { + int levelDifference = upgradeLevel - findLevel(upgradeLevel, gemstoneId); + gsData = (Mergeable) ((Upgradable) getItemStat()).apply(gsData.clone(), upgradeInfo, levelDifference); } - - // Calculate level difference - int gLevel = lvl - level; - //RECALCULATE//MMOItems.log("\u00a76 |\u00a7b|\u00a76>\u00a77 Gemstone Level: \u00a7e" + gLevel + "\u00a77 (Put at \u00a7b" + level + "\u00a77)"); - - //DBL//if (getGemstoneData(d) instanceof DoubleData) MMOItems.log("\u00a76 \u00a7b|>\u00a77 Gemstone Base: \u00a7e" + ((DoubleData) getGemstoneData(d)).getValue()); - // Apply upgrades - //noinspection ConstantConditions - StatData gRet = ((Upgradable) getItemStat()).apply(((Mergeable) getGemstoneData(d)).clone(), inf, gLevel); - //DBL//if (gRet instanceof DoubleData) MMOItems.log("\u00a76 \u00a7b|>\u00a77 Leveled Base: \u00a7e" + ((DoubleData) gRet).getValue()); - - // Merge - ((Mergeable) ret).mergeWith(((Mergeable) gRet).clone()); + finalData.mergeWith(gsData); } - // Add up externals (who dont suffer upgrades - for (StatData d : getExternalData()) { + // Add up externals (who don't suffer upgrades) + for (StatData externalData : getExternalData()) finalData.mergeWith((Mergeable) externalData); - //DBL//if (d instanceof DoubleData) MMOItems.log("\u00a76 >\u00a7c> \u00a77 Extraneous Base: \u00a7e" + ((DoubleData) d).getValue()); - // Just merge ig - ((Mergeable) ret).mergeWith(((Mergeable) d).clone()); - } - - // Return result - //DBL//if (ret instanceof DoubleData) MMOItems.log("\u00a76:::\u00a77 Result: \u00a7e" + ((DoubleData) ret).getValue() + "\u00a78 {Original:\u00a77 " + ((DoubleData) getOriginalData()).getValue() + "\u00a78}"); - return ret; - } - - /** - * This recalculates final value of the stats of the item. - *

- * That is, it (in this order): - *

1: Starts out with a fresh (empty) data - *

2: Sums the original values - *

3: Sums Gem Stone Data (which should be scaled accordingly [Upgrades are entirely merged into their data]) - *

4: Sums external data (modifiers that are not linked to an ID, I suppose by external plugins). - */ - private StatData recalculateMergeable() { - //RECALCULATE//MMOItems.log("\u00a73|||\u00a77 Calculating \u00a7f" + getItemStat().getNBTPath() + "\u00a77 as Mergeable"); - - // Just clone bro - StatData ret = ((Mergeable) getOriginalData()).clone(); - - //DBL//if (ret instanceof DoubleData) MMOItems.log("\u00a73 > \u00a77 Original Base: \u00a7e" + ((DoubleData) ret).getValue()); - - // Add Modifiers - for (StatData d : perModifierBonus.values()) { - //DBL//if (getModifiersBonus() instanceof DoubleData) MMOItems.log("\u00a73 >\u00a7c> \u00a77 Modifier Base: \u00a7e" + ((DoubleData) getModifiersBonus()).getValue()); - // Just merge ig - ((Mergeable) ret).mergeWith(((Mergeable) d).clone()); - } - - // Add up gemstones - for (StatData d : perGemstoneData.values()) { - //DBL//if (d instanceof DoubleData) MMOItems.log("\u00a73 >\u00a7b> \u00a77 Gemstone Base: \u00a7e" + ((DoubleData) d).getValue()); - ((Mergeable) ret).mergeWith(((Mergeable) d).clone()); - } - - // Add up externals - for (StatData d : getExternalData()) { - //DBL//if (d instanceof DoubleData) MMOItems.log("\u00a73 >\u00a7c> \u00a77 Extraneous Base: \u00a7e" + ((DoubleData) d).getValue()); - ((Mergeable) ret).mergeWith(((Mergeable) d).clone()); - } - - // Return result - //DBL//if (ret instanceof DoubleData) MMOItems.log("\u00a73:::\u00a77 Result: \u00a7b" + ((DoubleData) ret).getValue()); - return ret; + return finalData; } /** @@ -632,7 +422,7 @@ public class StatHistory { JsonObject object = new JsonObject(); // To know the stat it was - object.addProperty(enc_Stat, getItemStat().getId()); + object.addProperty(ENC_STAT, getItemStat().getId()); /* * Save the original data. It is redundant to save if it is clear though. @@ -646,7 +436,7 @@ public class StatHistory { * StatHistory of these items. */ if (!getOriginalData().isEmpty() || getItemStat() == ItemStats.ENCHANTS) { - object.add(enc_OGS, ItemTag.compressTags(getItemStat().getAppliedNBT(getOriginalData()))); + object.add(ENC_OGS, ItemTag.compressTags(getItemStat().getAppliedNBT(getOriginalData()))); } // Kompress Arrays @@ -671,7 +461,7 @@ public class StatHistory { // Include if (gemz.size() > 0) { - object.add(enc_GSS, gemz); + object.add(ENC_GSS, gemz); } @@ -692,7 +482,7 @@ public class StatHistory { // Include if (externals.size() > 0) { - object.add(enc_EXS, externals); + object.add(ENC_EXS, externals); } // Kompress Arrays @@ -717,7 +507,7 @@ public class StatHistory { // Include if (modz.size() > 0) { - object.add(enc_MOD, modz); + object.add(ENC_MOD, modz); } @@ -756,22 +546,22 @@ public class StatHistory { JsonElement modEncode = null; // It has stat information right? - if (json.has(enc_Stat)) { - statEncode = json.get(enc_Stat); + if (json.has(ENC_STAT)) { + statEncode = json.get(ENC_STAT); } else { return null; } - if (json.has(enc_OGS)) { - ogStatsEncode = json.get(enc_OGS); + if (json.has(ENC_OGS)) { + ogStatsEncode = json.get(ENC_OGS); } - if (json.has(enc_GSS)) { - gemsEncode = json.get(enc_GSS); + if (json.has(ENC_GSS)) { + gemsEncode = json.get(ENC_GSS); } - if (json.has(enc_EXS)) { - extEncode = json.get(enc_EXS); + if (json.has(ENC_EXS)) { + extEncode = json.get(ENC_EXS); } - if (json.has(enc_MOD)) { - modEncode = json.get(enc_MOD); + if (json.has(ENC_MOD)) { + modEncode = json.get(ENC_MOD); } // It is a primitive right @@ -960,21 +750,12 @@ public class StatHistory { @Nullable public static StatHistory fromNBTString(@NotNull MMOItem iSource, @NotNull String codedJson) { - // Attempt try { - - // Make JSON Parser JsonParser pJSON = new JsonParser(); - - // Parse as array JsonObject oJSON = pJSON.parse(codedJson).getAsJsonObject(); - - // Bake return fromJson(iSource, oJSON); } catch (Throwable e) { - - // Feedbacc FriendlyFeedbackProvider ffp = new FriendlyFeedbackProvider(FFPMMOItems.get()); ffp.activatePrefix(true, "Stat History"); ffp.log(FriendlyFeedbackCategory.ERROR, "Could not get stat history: $f{0}$b at $f{1}", e.getMessage(), e.getStackTrace()[0].toString()); @@ -983,53 +764,6 @@ public class StatHistory { } } - /** - * Get all gemstone and extraneous data from this other, while - * keeping the current ones as well as these original bases. - *

- * Fails if the stats are not the same one. - */ - @Deprecated - public void assimilate(@NotNull StatHistory other) { - - // Stat must be the same - if (other.getItemStat().getNBTPath().equals(getItemStat().getNBTPath())) { - //UPDT//MMOItems.log(" \u00a72>\u00a76> \u00a77History Stat Matches"); - - //UPDT//MMOItems.log(" \u00a76:\u00a72: \u00a77Original Gemstones \u00a7f" + perGemstoneData.size()); - //UPDT//MMOItems.log(" \u00a76:\u00a72: \u00a77Original Externals \u00a7f" + perExternalData.size()); - - // Register gemstones - for (UUID exUID : other.getAllGemstones()) { - //noinspection ConstantConditions - registerGemstoneData(exUID, other.getGemstoneData(exUID)); - } - - // Register externals - for (StatData ex : other.getExternalData()) { - registerExternalData((ex)); - } - - // Register modifiers - for (UUID exUID : other.getAllModifiers()) { - //noinspection ConstantConditions - registerModifierBonus(exUID, other.getModifiersBonus(exUID)); - } - - //UPDT//MMOItems.log(" \u00a76:\u00a72: \u00a77Final Gemstones \u00a7f" + perGemstoneData.size()); - //UPDT//MMOItems.log(" \u00a76:\u00a72: \u00a77Final Externals \u00a7f" + perExternalData.size()); - //ASS//MMOItems.log(" \u00a76:\u00a72: \u00a77Assimiliaton Result \u00a7f"); - //ASS//log(); - } - } - - @Deprecated - public StatHistory clone(@NotNull MMOItem newParent) { - final StatHistory his = clone(); - his.setParent(newParent); - return his; - } - @SuppressWarnings("MethodDoesntCallSuperMethod") @NotNull public StatHistory clone() { @@ -1042,88 +776,51 @@ public class StatHistory { return res; } - static final String enc_Stat = "Stat"; - static final String enc_OGS = "OGStory"; - static final String enc_GSS = "Gemstory"; - static final String enc_EXS = "Exstory"; - static final String enc_MOD = "Mod"; + private static final String ENC_STAT = "Stat", ENC_OGS = "OGStory", ENC_GSS = "Gemstory", ENC_EXS = "Exstory", ENC_MOD = "Mod"; - /** - * Logs into the console. Dev Method - */ - @Deprecated - public void log() { - - MMOItems.print(null, "\u00a76SH of \u00a7e" + getItemStat().getId() + "\u00a77, \u00a7b" + getMMOItem().getType() + " " + getMMOItem().getId(), null); - - if (getOriginalData() instanceof StringListData) { - - MMOItems.print(null, "\u00a7a++ Original", null); - for (String str : ((StringListData) getOriginalData()).getList()) { - MMOItems.print(null, "\u00a7a ++\u00a77 " + str, null); - } - - MMOItems.print(null, "\u00a7e++ Gemstones", null); - for (UUID ui : getAllGemstones()) { - StatData sd = getGemstoneData(ui); - if (!(sd instanceof StringListData)) { - continue; - } - for (String str : ((StringListData) sd).getList()) { - MMOItems.print(null, "\u00a7e ++\u00a77 " + str, null); - } - } - - MMOItems.print(null, "\u00a7c++ ExSH", null); - for (StatData sd : getExternalData()) { - if (!(sd instanceof StringListData)) { - continue; - } - for (String str : ((StringListData) sd).getList()) { - MMOItems.print(null, "\u00a7e ++\u00a77 " + str, null); - } - } - - MMOItems.print(null, "\u00a7d++ Modifiers", null); - for (UUID ui : getAllModifiers()) { - StatData sd = getModifiersBonus(ui); - if (!(sd instanceof StringListData)) { - continue; - } - for (String str : ((StringListData) sd).getList()) { - MMOItems.print(null, "\u00a7d ++\u00a77 " + str, null); - } - } - } else { - - MMOItems.print(null, "\u00a7a-- Original", null); - MMOItems.print(null, "\u00a7a ++\u00a77 " + getOriginalData(), null); - - MMOItems.print(null, "\u00a7e-- Gemstones", null); - for (UUID ui : getAllGemstones()) { - StatData sd = getGemstoneData(ui); - if (sd == null) { - continue; - } - MMOItems.print(null, "\u00a7e ++\u00a77 " + sd, null); - } - - MMOItems.print(null, "\u00a7c-- ExSH", null); - for (StatData sd : getExternalData()) { - if (sd == null) { - continue; - } - MMOItems.print(null, "\u00a7e ++\u00a77 " + sd, null); - } - - MMOItems.print(null, "\u00a7d-- Modifiers", null); - for (UUID ui : getAllModifiers()) { - StatData sd = getModifiersBonus(ui); - if (sd == null) { - continue; - } - MMOItems.print(null, "\u00a7d ++\u00a77 " + sd, null); - } + //region Methods not used + public void assimilate(@NotNull StatHistory other) { + if (other.getItemStat().getNBTPath().equals(getItemStat().getNBTPath())) { + for (UUID exUID : other.getAllGemstones()) registerGemstoneData(exUID, other.getGemstoneData(exUID)); + for (StatData ex : other.getExternalData()) registerExternalData((ex)); + for (UUID exUID : other.getAllModifiers()) registerModifierBonus(exUID, other.getModifiersBonus(exUID)); } } + + /** + * @deprecated use {@link #clone()} followed by {@link #setParent(MMOItem)} + */ + @Deprecated + public StatHistory clone(@NotNull MMOItem newParent) { + final StatHistory his = clone(); + his.setParent(newParent); + return his; + } + + @NotNull + @Deprecated + public StatData recalculateUnupgraded(boolean withPurge) { + return recalculate(withPurge, null); + } + + /** + * @see #isEmpty() + */ + @Deprecated + public boolean isClear() { + return isEmpty(); + } + + /** + * @see #fuseExternalData() + */ + @Deprecated + public void consolidateEXSH() { + fuseExternalData(); + } + + public boolean isUpgradeable() { + return getMMOItem().hasUpgradeTemplate() && getMMOItem().getUpgradeTemplate().getUpgradeInfo(getItemStat()) != null; + } + //endregion }