Fixed abilities not transfering from gems

This commit is contained in:
Jules 2024-04-06 15:50:19 -07:00
parent 09b34d2123
commit c1ecc198e3
11 changed files with 568 additions and 934 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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()));

View File

@ -145,7 +145,7 @@ public class MMOItemBuilder extends Buildable<MMOItem> {
*/
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);
}

View File

@ -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<ItemStat, StatData> 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<ItemStat, StatData> 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:
* <p></p>
* If the item does not have this stat yet, it will be set with <code>MMOItem.setData()</code>
* <p>If this data is not <code>Mergeable</code>, it will also be set</p>
* <p></p>
* 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:
* <p></p>
* If the item does not have this stat yet, it will be set with <code>MMOItem.setData()</code>
* <p>If this data is not <code>Mergeable</code>, it will also be set</p>
* <p></p>
* 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<ItemStat> 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<ItemStat> 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<ItemStat, StatHistory> 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
* <p></p>
* 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<ItemStat, StatHistory> mergeableStatHistory = new HashMap<>();
@NotNull
public ArrayList<StatHistory> getStatHistories() {
return new ArrayList<>(mergeableStatHistory.values());
}
/**
* Gets the history associated to this stat, if there is any
* <p></p>
* 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<StatHistory> getStatHistories() {
return new ArrayList<>(mergeableStatHistory.values());
}
/**
* Sets the history associated to this stat.
* <p></p>
* 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.
* <p></p>
* 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.
* <p></p>
* <b>Make sure to check {@link #hasUpgradeTemplate()} before calling.</b>
*/
public void upgrade() {
/**
* Upgrades this MMOItem one level.
* <p></p>
* <b>Make sure to check {@link #hasUpgradeTemplate()} before calling.</b>
*/
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;
}
/**
* <b>Make sure to check {@link #hasUpgradeTemplate()} before calling.</b>
* <p></p>
* 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");
/**
* <b>Make sure to check {@link #hasUpgradeTemplate()} before calling.</b>
* <p></p>
* 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<GemstoneData> getGemStones() {
@NotNull
public List<GemstoneData> 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<GemstoneData> 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).
* <p></p>
* 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<Pair<GemstoneData, MMOItem>> 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<Pair<GemstoneData, MMOItem>> 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).
* <p></p>
* 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<Pair<GemstoneData, MMOItem>> extractGemstones() {
// Identify actual stats
for (StatHistory hist : mergeableStatHistory.values())
for (Pair<GemstoneData, MMOItem> 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<Pair<GemstoneData, MMOItem>> 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 <code>null</code>
* 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<GemstoneData, MMOItem> 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 <code>null</code>
* 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
}

View File

@ -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);

View File

@ -38,7 +38,9 @@ public class AbilityListData implements StatData, Mergeable<AbilityListData> {
@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<AbilityListData> {
return true;
}
@Override
public String toString() {
return "AbilityListData{" +
"abilities=" + abilities +
'}';
}
@Override
public boolean isEmpty() {
return abilities.isEmpty();

View File

@ -23,7 +23,7 @@ import java.util.*;
*/
public class GemSocketsData implements StatData, Mergeable<GemSocketsData>, RandomStatData<GemSocketsData> {
@NotNull
private final Set<GemstoneData> gems = new LinkedHashSet<>();
private final List<GemstoneData> gems = new ArrayList<>();
@NotNull
private final List<String> emptySlots;
@ -55,7 +55,7 @@ public class GemSocketsData implements StatData, Mergeable<GemSocketsData>, 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<GemSocketsData>, 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<GemSocketsData>, Rand
return emptySlots;
}
@NotNull
/**
* @see #getGems()
* @deprecated Gems are stored inside a list
* rather than inside a linked hash set.
*/
@Deprecated
public Set<GemstoneData> getGemstones() {
return new HashSet<>(gems);
}
@NotNull
public List<GemstoneData> getGems() {
return gems;
}

View File

@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
* <b>Strongly encouraged to override the <code>equals</code> method
* to something fitting here as Mergeable stats should support comparisons.</b>
*/
public interface Mergeable<S extends StatData> extends StatData {
public interface Mergeable<S extends Mergeable<S>> extends StatData {
/**
* Merging two stat datas is required when an item benefits from

View File

@ -354,46 +354,14 @@ public class DoubleStat extends ItemStat<NumericStatFormula, DoubleData> 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;
}

View File

@ -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<StatData> perExternalData = new ArrayList<>();
private HashMap<UUID, StatData> 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 <b><code>+5.5</code></b>
*/
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 <code>StatHistory.getExternalData()</code>
*/
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<UUID> 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<UUID, StatData> 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.
* <p></p>
* This will not apply the changes, it will just give you the final
* <code>StatData</code> that shall be applied (used when upgrading).
*
* @param withPurge Check if the gemstones UUIDs are valid.
* Leave <code>true</code> 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.
* <p></p>
* 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.
* <p></p>
* 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 <code>true</code> 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 {
* </p>4: Sums Gem Stone Data (which should be scaled accordingly [Upgrades are entirely merged into their data])
* <p>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.
* <p></p>
* That is, it (in this order):
* <p>1: Starts out with a fresh (empty) data
* </p>2: Sums the original values
* </p>3: Sums Gem Stone Data (which should be scaled accordingly [Upgrades are entirely merged into their data])
* <p>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 <u>these</u> original bases.
* <p></p>
* 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
}