mirror of
https://gitlab.com/phoenix-dvpmt/mmoitems.git
synced 2024-12-22 04:37:42 +01:00
Made Item Updating work **much** better, and much more transparent on what it is actually doing.
+ Upgrade level of items may be kept, with its bonuses + Gem Stones are now perfectly kept, with all their stats + Enchantments may be kept (more details in-game) + Soulbound (was already working) may be kept. + Item Name may be kept (already kinda working) + Lore may be kept (more detail in-game) Add to your config: ```yml item-revision: keep-data: display-name: true lore: true enchantments: true upgrades: true gemstones: true soulbound: true ``` *Requires MMOLib update*
This commit is contained in:
parent
12f9e30ea7
commit
ae5d45b04a
2
pom.xml
2
pom.xml
@ -96,7 +96,7 @@
|
||||
<dependency>
|
||||
<groupId>io.lumine</groupId>
|
||||
<artifactId>MythicLib</artifactId>
|
||||
<version>1.0.10</version>
|
||||
<version>1.0.12-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package net.Indyuce.mmoitems;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import io.lumine.mythic.lib.version.SpigotPlugin;
|
||||
import io.lumine.mythic.utils.plugin.LuminePlugin;
|
||||
import net.Indyuce.mmoitems.api.*;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
|
||||
import net.Indyuce.mmoitems.command.MMOItemsCommandTreeRoot;
|
||||
import net.Indyuce.mmoitems.command.UpdateItemCommand;
|
||||
import net.Indyuce.mmoitems.command.completion.UpdateItemCompletion;
|
||||
@ -525,7 +527,7 @@ public class MMOItems extends LuminePlugin {
|
||||
setRPG(plugin.load());
|
||||
|
||||
// Mention it
|
||||
Log("Using \u00a76" + plugin.getName() + "\u00a77 as RPG Player provider.");
|
||||
Print(Level.INFO, "Using $s{0}$b as RPGPlayer provider", plugin.getName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -601,6 +603,13 @@ public class MMOItems extends LuminePlugin {
|
||||
plugin.getServer().getConsoleSender().sendMessage("\u00a78[" + ChatColor.YELLOW + "MMOItems\u00a78] \u00a77" + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Easily log something using the FriendlyFeedbackProvider, nice!
|
||||
*/
|
||||
public static void Print(@NotNull Level level, @NotNull String message, String... replaces) {
|
||||
MMOItems.plugin.getLogger().log(level, FriendlyFeedbackProvider.QuickForConsole(FriendlyFeedbackPalette_MMOItems.get(), message, replaces));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The server's console sender.
|
||||
*/
|
||||
|
@ -3,32 +3,66 @@ package net.Indyuce.mmoitems.api;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
public class ReforgeOptions {
|
||||
private final boolean keepName, keepLore, keepEnchantments, keepModifications, keepSoulbind;
|
||||
private final boolean
|
||||
keepName,
|
||||
keepLore,
|
||||
keepEnchantments,
|
||||
keepUpgrades,
|
||||
keepGemStones,
|
||||
keepSoulbind;
|
||||
|
||||
public ReforgeOptions(ConfigurationSection config) {
|
||||
this.keepName = config.getBoolean("display-name");
|
||||
this.keepLore = config.getBoolean("lore");
|
||||
this.keepEnchantments = config.getBoolean("enchantments");
|
||||
this.keepModifications = config.getBoolean("modifications");
|
||||
this.keepUpgrades = config.getBoolean("upgrades");
|
||||
this.keepGemStones = config.getBoolean("gemstones");
|
||||
this.keepSoulbind = config.getBoolean("soulbound");
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the display name of the item.
|
||||
*/
|
||||
public boolean shouldKeepName() {
|
||||
return keepName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps all lore lines that begin with {@link org.bukkit.ChatColor#GRAY}
|
||||
*/
|
||||
public boolean shouldKeepLore() {
|
||||
return keepLore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this keep the enchantments the player
|
||||
* manually cast onto this item? (Not from gem
|
||||
* stones nor upgrades).
|
||||
*/
|
||||
public boolean shouldKeepEnchantments() {
|
||||
return keepEnchantments;
|
||||
}
|
||||
|
||||
public boolean shouldKeepModifications() {
|
||||
return keepModifications;
|
||||
/**
|
||||
* Retains the upgrade level of the item.
|
||||
*/
|
||||
public boolean shouldKeepUpgrades() {
|
||||
return keepUpgrades;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retains all gem stones if there are any, removing
|
||||
* one gem socket for every gemstone kept.
|
||||
* <p></p>
|
||||
* Gemstones remember at what upgrade level they were inserted.
|
||||
*/
|
||||
public boolean shouldKeepGemStones() {
|
||||
return keepGemStones;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retains the soulbind if it has any.
|
||||
*/
|
||||
public boolean shouldKeepSoulbind() {
|
||||
return keepSoulbind;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
|
||||
import net.Indyuce.mmoitems.stat.Enchants;
|
||||
import net.Indyuce.mmoitems.stat.data.UpgradeData;
|
||||
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
|
||||
import net.Indyuce.mmoitems.stat.data.type.StatData;
|
||||
@ -112,6 +113,9 @@ public class UpgradeTemplate {
|
||||
*/
|
||||
public void upgradeTo(@NotNull MMOItem mmoitem, int level) {
|
||||
|
||||
// Make sure to not overwrite player's enchantments when upgrading.
|
||||
Enchants.separateEnchantments(mmoitem);
|
||||
|
||||
// Set the items level
|
||||
UpgradeData dat;
|
||||
if (mmoitem.hasData(ItemStats.UPGRADE)) { dat = (UpgradeData) mmoitem.getData(ItemStats.UPGRADE); } else { dat = new UpgradeData(null, null, false, false, 0, 100); }
|
||||
|
@ -20,6 +20,8 @@ import net.Indyuce.mmoitems.api.util.message.Message;
|
||||
import net.Indyuce.mmoitems.comp.flags.FlagPlugin.CustomFlag;
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Weapon extends UseItem {
|
||||
public Weapon(Player player, NBTItem item) {
|
||||
this(PlayerData.get(player), item);
|
||||
@ -38,7 +40,7 @@ public class Weapon extends UseItem {
|
||||
|
||||
boolean asCanUse = playerData.getRPG().canUse(getNBTItem(), true);
|
||||
boolean asFlagAllowed = true; FlagPlugin fg = MMOItems.plugin.getFlags(); if (fg != null) { asFlagAllowed = fg.isFlagAllowed(getPlayer(), CustomFlag.MI_WEAPONS); }
|
||||
else { MMOItems.Log("Flag Plugin Not Found");}
|
||||
else { MMOItems.Print(Level.WARNING, "$fFlag plugin not found"); }
|
||||
return asCanUse || asFlagAllowed;
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ public class ItemStackBuilder {
|
||||
public NBTItem buildNBT() {
|
||||
// Clone as to not conflict in any way
|
||||
MMOItem builtMMOItem = mmoitem.clone();
|
||||
//GEM//MMOItems. Log("\u00a7e+ \u00a77Building \u00a7c" + mmoitem.getType().getName() + " " + mmoitem.getId() + "\u00a77 (Size \u00a7e" + mmoitem.mergeableStatHistory.size() + "\u00a77 Historic)");
|
||||
//GEM//MMOItems.Log("\u00a7e+ \u00a77Building \u00a7c" + mmoitem.getType().getName() + " " + mmoitem.getId() + "\u00a77 (Size \u00a7e" + mmoitem.getStatHistories().size() + "\u00a77 Historic)");
|
||||
|
||||
// For every stat within this item
|
||||
for (ItemStat stat : builtMMOItem.getStats())
|
||||
@ -113,7 +113,7 @@ public class ItemStackBuilder {
|
||||
// Attempt to add
|
||||
try {
|
||||
|
||||
//GEM//MMOItems. Log("\u00a7e -+- \u00a77Applying \u00a76" + stat.getNBTPath());
|
||||
//GEM//MMOItems.Log("\u00a7e -+- \u00a77Applying \u00a76" + stat.getNBTPath());
|
||||
|
||||
// Make necessary lore changes
|
||||
stat.whenApplied(this, builtMMOItem.getData(stat));
|
||||
@ -124,7 +124,7 @@ public class ItemStackBuilder {
|
||||
// Found it?
|
||||
if (s != null) {
|
||||
|
||||
//GEM//MMOItems. Log("\u00a7a -+- \u00a77Found History");
|
||||
//GEM//MMOItems.Log("\u00a7a -+- \u00a77Found History");
|
||||
|
||||
// Add to NBT
|
||||
addItemTag(new ItemTag(histroy_keyword + stat.getId(), s.toNBTString()));
|
||||
|
@ -60,25 +60,25 @@ public class MMOItem implements ItemReference {
|
||||
* 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());
|
||||
//GEM//MMOItems. Log("Merging stone stat \u00a76" + stat.getNBTPath() + "\u00a77 into \u00a7c" + getType().getName() + " " + getId());
|
||||
|
||||
// Do we already have the data?
|
||||
if (data instanceof Mergeable) {
|
||||
//GEM//MMOItems.Log("\u00a7a + \u00a77Mergeable");
|
||||
//GEM//MMOItems. Log("\u00a7a + \u00a77Mergeable");
|
||||
|
||||
// Prepare to merge: Gather History (Also initializes the ORIGINAL stats)
|
||||
StatHistory<StatData> sHistory = StatHistory.From(this, stat);
|
||||
|
||||
// As GemStone or as External?
|
||||
if (associatedGemStone != null) {
|
||||
//GEM//MMOItems.Log(" \u00a79++\u00a77 As Gemstone \u00a7b" + associatedGemStone.toString());
|
||||
//GEM//MMOItems. Log(" \u00a79++\u00a77 As Gemstone \u00a7b" + associatedGemStone.toString());
|
||||
|
||||
// As GemStone
|
||||
sHistory.registerGemstoneData(associatedGemStone, data);
|
||||
|
||||
// As External
|
||||
} else {
|
||||
//GEM//MMOItems.Log(" \u00a7c++\u00a77 As External");
|
||||
//GEM//MMOItems. Log(" \u00a7c++\u00a77 As External");
|
||||
|
||||
// As External, UUIDless modifier
|
||||
sHistory.registerExternalData(data);
|
||||
@ -111,9 +111,7 @@ public class MMOItem implements ItemReference {
|
||||
return stats.get(stat);
|
||||
}
|
||||
|
||||
public boolean hasData(@NotNull ItemStat stat) {
|
||||
return (stats.get(stat) != null);
|
||||
}
|
||||
public boolean hasData(@NotNull ItemStat stat) { return (stats.get(stat) != null); }
|
||||
|
||||
/**
|
||||
* @return Collection of all item stats which have some data on this mmoitem
|
||||
@ -163,15 +161,14 @@ public class MMOItem implements ItemReference {
|
||||
* 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 public final Map<String, StatHistory<StatData>> mergeableStatHistory = new HashMap<>();
|
||||
@NotNull final Map<String, StatHistory<StatData>> mergeableStatHistory = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 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<StatData> getStatHistory(@NotNull ItemStat stat) {
|
||||
@Nullable public StatHistory<StatData> getStatHistory(@NotNull ItemStat stat) {
|
||||
try {
|
||||
|
||||
// Well that REALLY should work
|
||||
@ -181,6 +178,11 @@ public class MMOItem implements ItemReference {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@NotNull public ArrayList<StatHistory<StatData>> getStatHistories() {
|
||||
|
||||
// Those
|
||||
return new ArrayList<>(mergeableStatHistory.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the history associated to this stat.
|
||||
|
@ -1,8 +1,13 @@
|
||||
package net.Indyuce.mmoitems.api.item.mmoitem;
|
||||
|
||||
import io.lumine.mythic.lib.api.item.ItemTag;
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import io.lumine.mythic.lib.api.item.SupportedNBTTagValues;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
|
||||
import net.Indyuce.mmoitems.stat.data.type.StatData;
|
||||
import net.Indyuce.mmoitems.stat.type.ItemStat;
|
||||
import net.Indyuce.mmoitems.stat.type.StatHistory;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -42,6 +47,15 @@ public class VolatileMMOItem extends ReadMMOItem {
|
||||
try {
|
||||
stat.whenLoaded(this);
|
||||
|
||||
// Also laod history :think ing:
|
||||
ItemTag hisTag = ItemTag.getTagAtPath(ItemStackBuilder.histroy_keyword + stat.getId(), getNBT(), SupportedNBTTagValues.STRING);
|
||||
if (hisTag != null) {
|
||||
|
||||
// Aye
|
||||
StatHistory<StatData> hist = StatHistory.fromNBTString(this, (String) hisTag.getValue());
|
||||
if (hist != null) { this.setStatHistory(stat, hist); }
|
||||
}
|
||||
|
||||
// Nope
|
||||
} catch (IllegalArgumentException exception) {
|
||||
|
||||
|
@ -144,7 +144,8 @@ public class MMOItemTemplate extends PostLoadObject implements ItemReference {
|
||||
* template has the 'tiered' recipe option, a random tier will be picked. If
|
||||
* the template has the 'level-item' option, a random level will be picked
|
||||
*
|
||||
* @param player The player for whom you are generating the itme
|
||||
* @param player The player for whom you are generating the item. Seems to only
|
||||
* matter when rolling for the 'item level'
|
||||
* @return Item builder with random level and tier?
|
||||
*/
|
||||
public MMOItemBuilder newBuilder(RPGPlayer player) {
|
||||
|
@ -1,158 +0,0 @@
|
||||
package net.Indyuce.mmoitems.api.util;
|
||||
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import io.lumine.mythic.utils.adventure.text.Component;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.ItemTier;
|
||||
import net.Indyuce.mmoitems.api.ReforgeOptions;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.api.player.RPGPlayer;
|
||||
import net.Indyuce.mmoitems.stat.data.DoubleData;
|
||||
import net.Indyuce.mmoitems.stat.data.SoulboundData;
|
||||
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
|
||||
import net.Indyuce.mmoitems.stat.data.type.StatData;
|
||||
import net.Indyuce.mmoitems.stat.type.GemStoneStat;
|
||||
import net.Indyuce.mmoitems.stat.type.ItemStat;
|
||||
import net.Indyuce.mmoitems.stat.type.Upgradable;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ItemModInstance {
|
||||
private final NBTItem nbtItem;
|
||||
private final int amount;
|
||||
|
||||
private final Map<ItemStat, StatData> itemData = new HashMap<>();
|
||||
|
||||
// Not initialized at first for performance reasons
|
||||
private MMOItem mmoItem;
|
||||
|
||||
private Component cachedName;
|
||||
private List<Component> cachedLore;
|
||||
private Map<Enchantment, Integer> cachedEnchants;
|
||||
private StatData cachedSoulbound;
|
||||
|
||||
public ItemModInstance(NBTItem nbt) {
|
||||
this.nbtItem = nbt;
|
||||
this.amount = nbt.getItem().getAmount();
|
||||
}
|
||||
|
||||
public void applySoulbound(Player p) {
|
||||
applySoulbound(p, MMOItems.plugin.getConfig().getInt("soulbound.auto-bind.level", 1));
|
||||
}
|
||||
|
||||
public void applySoulbound(Player p, int level) {
|
||||
loadLiveMMOItem();
|
||||
mmoItem.setData(ItemStats.SOULBOUND, new SoulboundData(p.getUniqueId(), p.getName(), level));
|
||||
}
|
||||
|
||||
public void reforge(Player p, ReforgeOptions options) {
|
||||
reforge(p == null ? null : PlayerData.get(p).getRPG(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param player The player to generate the new item from. If empty, it will
|
||||
* use the old items level and tier, or default values if
|
||||
* needed.
|
||||
*/
|
||||
public void reforge(RPGPlayer player, ReforgeOptions options) {
|
||||
loadVolatileMMOItem();
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(mmoItem.getType(), mmoItem.getId());
|
||||
|
||||
ItemMeta meta = nbtItem.getItem().getItemMeta();
|
||||
if (options.shouldKeepName() && meta.hasDisplayName())
|
||||
cachedName = nbtItem.getDisplayNameComponent();
|
||||
if (options.shouldKeepLore() && meta.hasLore())
|
||||
cachedLore = nbtItem.getLoreComponents();
|
||||
if (options.shouldKeepEnchantments())
|
||||
cachedEnchants = nbtItem.getItem().getEnchantments();
|
||||
|
||||
if (options.shouldKeepModifications())
|
||||
for (ItemStat stat : mmoItem.getStats()) {
|
||||
if (stat instanceof Upgradable)
|
||||
itemData.put(stat, mmoItem.getData(stat));
|
||||
|
||||
if (!(stat instanceof GemStoneStat)) {
|
||||
StatData data = mmoItem.getData(stat);
|
||||
if (data instanceof Mergeable) {
|
||||
if (itemData.containsKey(stat))
|
||||
((Mergeable) itemData.get(stat)).merge(data);
|
||||
else
|
||||
itemData.put(stat, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.shouldKeepSoulbind() && mmoItem.hasData(ItemStats.SOULBOUND))
|
||||
cachedSoulbound = mmoItem.getData(ItemStats.SOULBOUND);
|
||||
|
||||
if (player == null) {
|
||||
final int iLevel = MMOItems.plugin.getConfig().getInt("item-revision.default-item-level", -1);
|
||||
int level = iLevel == -1
|
||||
? (mmoItem.hasData(ItemStats.ITEM_LEVEL) ? (int) ((DoubleData) mmoItem.getData(ItemStats.ITEM_LEVEL)).getValue() : 0)
|
||||
: iLevel;
|
||||
ItemTier tier = (mmoItem.hasData(ItemStats.TIER) && MMOItems.plugin.getConfig().getBoolean("item-revision.keep-tiers"))
|
||||
? MMOItems.plugin.getTiers().get(mmoItem.getData(ItemStats.TIER).toString())
|
||||
: null;
|
||||
mmoItem = template.newBuilder(level, tier).build();
|
||||
} else
|
||||
mmoItem = template.newBuilder(player).build();
|
||||
}
|
||||
|
||||
public boolean hasChanges() {
|
||||
return mmoItem != null;
|
||||
}
|
||||
|
||||
public ItemStack toStack() {
|
||||
for (Map.Entry<ItemStat, StatData> data : itemData.entrySet())
|
||||
if (mmoItem.hasData(data.getKey()))
|
||||
((Mergeable) mmoItem.getData(data.getKey())).merge(data.getValue());
|
||||
else
|
||||
mmoItem.setData(data.getKey(), data.getValue());
|
||||
if (cachedSoulbound != null)
|
||||
mmoItem.setData(ItemStats.SOULBOUND, cachedSoulbound);
|
||||
ItemStack stack = mmoItem.newBuilder().build();
|
||||
stack.setAmount(amount);
|
||||
ItemMeta meta = stack.getItemMeta();
|
||||
if (cachedEnchants != null)
|
||||
stack.addUnsafeEnchantments(cachedEnchants);
|
||||
stack.setItemMeta(meta);
|
||||
|
||||
NBTItem nbtItem = NBTItem.get(stack);
|
||||
|
||||
if (cachedName != null)
|
||||
nbtItem.setDisplayNameComponent(cachedName);
|
||||
if (cachedLore != null) {
|
||||
nbtItem.setLoreComponents(cachedLore);
|
||||
}
|
||||
|
||||
return nbtItem.toItem();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the MMOItem as a LiveMMOItem if it's null or not already a
|
||||
* LiveMMOItem
|
||||
*/
|
||||
private void loadLiveMMOItem() {
|
||||
if (mmoItem != null && mmoItem instanceof LiveMMOItem)
|
||||
return;
|
||||
mmoItem = new LiveMMOItem(nbtItem);
|
||||
}
|
||||
|
||||
/* Initialize the MMOItem as a VolatileMMOItem if it's null */
|
||||
private void loadVolatileMMOItem() {
|
||||
if (mmoItem != null)
|
||||
return;
|
||||
mmoItem = new VolatileMMOItem(nbtItem);
|
||||
}
|
||||
}
|
576
src/main/java/net/Indyuce/mmoitems/api/util/MMOItemReforger.java
Normal file
576
src/main/java/net/Indyuce/mmoitems/api/util/MMOItemReforger.java
Normal file
@ -0,0 +1,576 @@
|
||||
package net.Indyuce.mmoitems.api.util;
|
||||
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import io.lumine.mythic.utils.adventure.text.Component;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.ItemTier;
|
||||
import net.Indyuce.mmoitems.api.ReforgeOptions;
|
||||
import net.Indyuce.mmoitems.api.UpgradeTemplate;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.player.PlayerData;
|
||||
import net.Indyuce.mmoitems.api.player.RPGPlayer;
|
||||
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
|
||||
import net.Indyuce.mmoitems.stat.Enchants;
|
||||
import net.Indyuce.mmoitems.stat.GemSockets;
|
||||
import net.Indyuce.mmoitems.stat.RevisionID;
|
||||
import net.Indyuce.mmoitems.stat.data.*;
|
||||
import net.Indyuce.mmoitems.stat.data.type.Mergeable;
|
||||
import net.Indyuce.mmoitems.stat.data.type.StatData;
|
||||
import net.Indyuce.mmoitems.stat.type.GemStoneStat;
|
||||
import net.Indyuce.mmoitems.stat.type.ItemStat;
|
||||
import net.Indyuce.mmoitems.stat.type.StatHistory;
|
||||
import net.Indyuce.mmoitems.stat.type.Upgradable;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class to manage modification of items with reference to what they used to be
|
||||
* (and apparently also used to automatically apply SoulBounds):
|
||||
*
|
||||
* <p><code><b>updating</b></code> refers to changing the base stats
|
||||
* of a MMOItem instance to what the template currently has, usually
|
||||
* keeping gem stones and upgrade level. This wont reroll RNG stats.</p>
|
||||
*
|
||||
* <p><code><b>reforging</b></code> same thing as updating, but rerolling
|
||||
* the RNG stats - basically transferring the data specified by the
|
||||
* {@link ReforgeOptions} into a new item of the same Type-ID</p>
|
||||
*
|
||||
* @author Gunging, ? (Indyuce is my guess)
|
||||
*/
|
||||
public class MMOItemReforger {
|
||||
|
||||
// region Item Data
|
||||
|
||||
// Raw NBT Item
|
||||
@NotNull private final NBTItem nbtItem;
|
||||
|
||||
// ItemStack size
|
||||
private final int amount;
|
||||
|
||||
// Not initialized at first for performance reasons
|
||||
private MMOItem mmoItem;
|
||||
|
||||
// Data
|
||||
private final Map<ItemStat, StatData> itemData = new HashMap<>();
|
||||
private final Map<ItemStat, StatHistory<StatData>> itemDataHistory = new HashMap<>();
|
||||
|
||||
//endregion
|
||||
|
||||
//region Cached stuff
|
||||
|
||||
// Stripped Name
|
||||
@Nullable String cachedName;
|
||||
|
||||
// Grayish Lore
|
||||
@NotNull ArrayList<String> cachedLore = new ArrayList<>();
|
||||
|
||||
// Extraneous Enchantments
|
||||
@Nullable EnchantListData cachedEnchantments;
|
||||
|
||||
// Gem Stones
|
||||
@Nullable GemSocketsData cachedGemStones;
|
||||
|
||||
// Upgrade Level
|
||||
@Nullable UpgradeData cachedUpgradeLevel;
|
||||
|
||||
// Soulbound
|
||||
private StatData cachedSoulbound;
|
||||
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* Prepare to reforge this MMOItem (starts out as NBTItem due to backend reasons).
|
||||
* @param nbt <b>Make sure {@link NBTItem#hasType()} returns true for this</b>.
|
||||
*/
|
||||
public MMOItemReforger(@NotNull NBTItem nbt) {
|
||||
this.nbtItem = nbt;
|
||||
this.amount = nbt.getItem().getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a quick soulbound based on the config value <code>soulbound.auto-bind.level</code> (default = 1)
|
||||
*/
|
||||
public void applySoulbound(@NotNull Player p) {
|
||||
applySoulbound(p, MMOItems.plugin.getConfig().getInt("soulbound.auto-bind.level", 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a quick soulbound of this level
|
||||
*/
|
||||
public void applySoulbound(@NotNull Player p, int level) {
|
||||
|
||||
// Initialize as Live MMOItem
|
||||
loadLiveMMOItem();
|
||||
|
||||
// Override Soulbound Data
|
||||
mmoItem.setData(ItemStats.SOULBOUND, new SoulboundData(p.getUniqueId(), p.getName(), level));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method updates the base stats of this item to what
|
||||
* the template currently has, only rerolling RNGs where the
|
||||
* probability of getting the same roll is less than 5%
|
||||
*
|
||||
* @param options Which data to 'keep'
|
||||
* @param p There is an option where an item's base stats will be better
|
||||
* if the player who first generates the item is a higher level.
|
||||
* This player will be used to fulfill that operation.
|
||||
* <p></p>
|
||||
* If <code>null</code>, the default modifiers
|
||||
* specified in the config is used.
|
||||
*/
|
||||
public void update(@Nullable Player p, @NotNull ReforgeOptions options) {
|
||||
update(p == null ? null : PlayerData.get(p).getRPG(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method updates the base stats of this item to what
|
||||
* the template currently has, only rerolling RNGs where the
|
||||
* probability of getting the same roll is less than 5%
|
||||
* <p></p>
|
||||
* Used when updating items with the updater.
|
||||
*
|
||||
* @param player There is an option where an item's base stats will be better
|
||||
* if the player who first generates the item is a higher level.
|
||||
* This player will be used to fulfill that operation.
|
||||
* <p></p>
|
||||
* If empty, it will use the old items level and tier,
|
||||
* or default values if needed.
|
||||
*
|
||||
* @see RevisionID
|
||||
*/
|
||||
public void update(@Nullable RPGPlayer player, @NotNull ReforgeOptions options) {
|
||||
|
||||
/*
|
||||
* todo Has to store every stat into itemData, then check each stat of
|
||||
* the new item to see if they are RNG rolls in order to:
|
||||
*
|
||||
* 1: If they arent, the probability of getting the old number
|
||||
* is straight up ZERO and must be replaced by the updated value.
|
||||
*
|
||||
* 2: If they are RNG rolls, if the probability of getting the
|
||||
* current roll is at least 5%, the old roll is kept.
|
||||
*
|
||||
* 3: If the stat is gone completely, its again a ZERO chance
|
||||
* so it is removed (the updated value of 0 prevailing).
|
||||
*/
|
||||
|
||||
reforge(player, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new item of the same Type-ID and transfers the data
|
||||
* from the old one following the options.
|
||||
*
|
||||
* @param options Which data to 'keep'
|
||||
* @param p There is an option where an item's base stats will be better
|
||||
* if the player who first generates the item is a higher level.
|
||||
* This player will be used to fulfill that operation.
|
||||
* <p></p>
|
||||
* If <code>null</code>, the default modifiers
|
||||
* specified in the config is used.
|
||||
*/
|
||||
public void reforge(@Nullable Player p, @NotNull ReforgeOptions options) {
|
||||
reforge(p == null ? null : PlayerData.get(p).getRPG(), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new item of the same Type-ID and transfers the data
|
||||
* from the old one following the options.
|
||||
*
|
||||
* @param player There is an option where an item's base stats will be better
|
||||
* if the player who first generates the item is a higher level.
|
||||
* This player will be used to fulfill that operation.
|
||||
* <p></p>
|
||||
* If empty, it will use the old items level and tier,
|
||||
* or default values if needed.
|
||||
*/
|
||||
public void reforge(@Nullable RPGPlayer player, @NotNull ReforgeOptions options) {
|
||||
|
||||
// Initialize as Volatile, find source template. GemStones require a Live MMOItem though (to correctly load all Stat Histories and sh)
|
||||
if (!options.shouldKeepGemStones()) { loadVolatileMMOItem(); } else { loadLiveMMOItem(); }
|
||||
MMOItemTemplate template = MMOItems.plugin.getTemplates().getTemplate(mmoItem.getType(), mmoItem.getId());
|
||||
ItemMeta meta = nbtItem.getItem().getItemMeta();
|
||||
Validate.isTrue(meta != null, FriendlyFeedbackProvider.QuickForConsole(FriendlyFeedbackPalette_MMOItems.get(), "Invalid item meta prevented $f{0}$b from updating.", template.getType().toString() + " " + template.getId()));
|
||||
|
||||
// Keep name
|
||||
if (options.shouldKeepName()) {
|
||||
//UPDT//MMOItems.Log(" \u00a73> \u00a77Keeping Name");
|
||||
|
||||
// Does it have a name?
|
||||
if (mmoItem.hasData(ItemStats.NAME)) {
|
||||
|
||||
// Cache it
|
||||
cachedName = mmoItem.getData(ItemStats.NAME).toString();
|
||||
|
||||
// No name defined, use display name I guess (pretty unusual btw)
|
||||
} else if (meta.hasDisplayName()) {
|
||||
|
||||
cachedName = meta.getDisplayName();
|
||||
}
|
||||
|
||||
//UPDT//MMOItems.Log(" \u00a73 + \u00a77" + cachedName);
|
||||
}
|
||||
|
||||
// Keep specific lore components
|
||||
if (options.shouldKeepLore() && mmoItem.hasData(ItemStats.LORE)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7d> \u00a77Keeping Lore");
|
||||
|
||||
// Examine every element
|
||||
for (String str : ((StringListData) mmoItem.getData(ItemStats.LORE)).getList()) {
|
||||
|
||||
// Does it start with the promised...?
|
||||
if (str.startsWith("\u00a77")) { cachedLore.add(str); }
|
||||
}
|
||||
|
||||
//UPDT//for (String lr : cachedLore) { //UPDT//MMOItems.Log(" \u00a7d + \u00a77" + lr); }
|
||||
}
|
||||
|
||||
// Choose enchantments to keep
|
||||
if (options.shouldKeepEnchantments()) {
|
||||
//UPDT//MMOItems.Log(" \u00a7b> \u00a77Keeping Enchantments");
|
||||
|
||||
// Enchant list data
|
||||
cachedEnchantments = new EnchantListData();
|
||||
|
||||
// Does it have MMOItems enchantment data?
|
||||
if (mmoItem.hasData(ItemStats.ENCHANTS)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7b* \u00a77Found Data");
|
||||
|
||||
// Nope
|
||||
} else {
|
||||
//UPDT//MMOItems.Log(" \u00a7b* \u00a77No Data");
|
||||
mmoItem.setData(ItemStats.ENCHANTS, new EnchantListData());
|
||||
}
|
||||
|
||||
// Make sure they are consolidated
|
||||
Enchants.separateEnchantments(mmoItem);
|
||||
|
||||
// Gather
|
||||
StatHistory<StatData> hist = StatHistory.From(mmoItem, ItemStats.ENCHANTS);
|
||||
|
||||
// Reap
|
||||
for (StatData pEnchants : hist.getExternalData()) {
|
||||
|
||||
// It really should be but whatever
|
||||
if (pEnchants instanceof EnchantListData) {
|
||||
|
||||
// For every stat
|
||||
for (Enchantment e : ((EnchantListData) pEnchants).getEnchants()) {
|
||||
|
||||
// Get Base/Current
|
||||
int established = cachedEnchantments.getLevel(e);
|
||||
|
||||
// Add
|
||||
int calculated = established + ((EnchantListData) pEnchants).getLevel(e);
|
||||
|
||||
// Put
|
||||
cachedEnchantments.addEnchant(e, calculated);
|
||||
//UPDT//MMOItems.Log(" \u00a7b + \u00a77" + e.getName() + " " + calculated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The cache now stores the full extent of extraneous data. Separate from thy history. (As to not include it in this in the cached data later)
|
||||
hist.getExternalData().clear();
|
||||
}
|
||||
|
||||
// Acquire old upgrade level
|
||||
if (options.shouldKeepUpgrades() && mmoItem.hasData(ItemStats.UPGRADE)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7e> \u00a77Keeping Upgrade Data");
|
||||
|
||||
// Get Level
|
||||
cachedUpgradeLevel = ((UpgradeData) mmoItem.getData(ItemStats.UPGRADE));
|
||||
}
|
||||
|
||||
// Gather Gemstones
|
||||
if (options.shouldKeepGemStones() && mmoItem.hasData(ItemStats.GEM_SOCKETS)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7a> \u00a77Keeping Gem Sockets");
|
||||
|
||||
// Cache that gemstone data
|
||||
cachedGemStones = (GemSocketsData) mmoItem.getData(ItemStats.GEM_SOCKETS);
|
||||
|
||||
// Store all the history of stat proceedings.
|
||||
for (StatHistory<StatData> hist : mmoItem.getStatHistories()) {
|
||||
//UPDT//MMOItems.Log(" \u00a7a + \u00a77History of \u00a7f" + hist.getItemStat().getNBTPath());
|
||||
|
||||
// Get and set
|
||||
itemDataHistory.put(hist.getItemStat(), hist);
|
||||
}
|
||||
}
|
||||
|
||||
// Soulbound transfer
|
||||
if (options.shouldKeepSoulbind() && mmoItem.hasData(ItemStats.SOULBOUND)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7c> \u00a77Keeping Soulbind");
|
||||
|
||||
// Find data
|
||||
cachedSoulbound = mmoItem.getData(ItemStats.SOULBOUND);
|
||||
}
|
||||
|
||||
if (player == null) {
|
||||
|
||||
// Get default Item Level
|
||||
final int iLevel = MMOItems.plugin.getConfig().getInt("item-revision.default-item-level", -32767);
|
||||
|
||||
// What level with the regenerated item will be hmmmm.....
|
||||
int level =
|
||||
|
||||
// No default level specified?
|
||||
(iLevel == -32767) ?
|
||||
|
||||
// Does the item have level?
|
||||
(mmoItem.hasData(ItemStats.ITEM_LEVEL) ? (int) ((DoubleData) mmoItem.getData(ItemStats.ITEM_LEVEL)).getValue() : 0 )
|
||||
|
||||
// Default level was specified, use that.
|
||||
: iLevel;
|
||||
|
||||
|
||||
// Identify tier.
|
||||
ItemTier tier =
|
||||
|
||||
// Does the item have a tier, and it should keep it?
|
||||
(mmoItem.hasData(ItemStats.TIER) && MMOItems.plugin.getConfig().getBoolean("item-revision.keep-tiers")) ?
|
||||
|
||||
// The tier will be the current tier
|
||||
MMOItems.plugin.getTiers().get(mmoItem.getData(ItemStats.TIER).toString())
|
||||
|
||||
// The item either has no tier, or shouldn't keep it. Null
|
||||
: null;
|
||||
|
||||
// Build it again (Reroll RNG)
|
||||
mmoItem = template.newBuilder(level, tier).build();
|
||||
|
||||
// No player provided, use defaults.
|
||||
} else {
|
||||
|
||||
// Build it again (Reroll RNG)
|
||||
mmoItem = template.newBuilder(player).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Was any reforge actually performed on this item?
|
||||
*/
|
||||
public boolean hasChanges() { return mmoItem != null; }
|
||||
|
||||
public ItemStack toStack() {
|
||||
|
||||
// For every cached stat (presumably due to its importance)
|
||||
for (ItemStat stat : itemData.keySet()) {
|
||||
//UPDT//MMOItems.Log(" \u00a72@\u00a78@ \u00a77Cached Stat");
|
||||
|
||||
// Replace into incoming MMOItem
|
||||
StatData data = itemData.get(stat);
|
||||
|
||||
// Include if nonull
|
||||
if (data != null) {
|
||||
//UPDT//MMOItems.Log(" \u00a72@\u00a78@ \u00a77Old stat \u00a7f" + stat.getNBTPath());
|
||||
|
||||
// Set, replace, begone the previous!
|
||||
mmoItem.setData(stat, data);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply histories
|
||||
for (ItemStat stat : itemDataHistory.keySet()) {
|
||||
//UPDT//MMOItems.Log(" \u00a72@\u00a76@ \u00a77Cached Stat History");
|
||||
|
||||
// Does it have history too?
|
||||
StatHistory<StatData> histOld = itemDataHistory.get(stat);
|
||||
if (histOld != null) {
|
||||
//UPDT//MMOItems.Log(" \u00a72 *\u00a76* \u00a77Of old stat \u00a7f" + histOld.getItemStat().getNBTPath());
|
||||
|
||||
// Regenerate the original data
|
||||
StatHistory<StatData> hist = StatHistory.From(mmoItem, stat);
|
||||
|
||||
// Remember...
|
||||
hist.Assimilate(histOld);
|
||||
|
||||
// Recalculate
|
||||
mmoItem.setData(hist.getItemStat(), hist.Recalculate(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Apply soulbound
|
||||
if (cachedSoulbound != null) {
|
||||
//UPDT//MMOItems.Log(" \u00a7c@ \u00a77Applying Soulbind");
|
||||
|
||||
// Apply
|
||||
mmoItem.setData(ItemStats.SOULBOUND, cachedSoulbound);
|
||||
}
|
||||
|
||||
// Contained enchantments huh
|
||||
if (cachedEnchantments != null) {
|
||||
//UPDT//MMOItems.Log(" \u00a7b@ \u00a77Applying Enchantments");
|
||||
|
||||
// Register as extraneous obviously
|
||||
StatHistory<StatData> hist = StatHistory.From(mmoItem, ItemStats.ENCHANTS);
|
||||
hist.registerExternalData(cachedEnchantments);
|
||||
//UPDT//for (Enchantment lr : cachedEnchantments.getEnchants()) { //UPDT//MMOItems. Log(" \u00a7b + \u00a77" + lr.getName() + " \u00a7f" + cachedEnchantments.getLevel(lr)); }
|
||||
}
|
||||
|
||||
// Upgrade Information
|
||||
if (cachedUpgradeLevel != null) {
|
||||
//UPDT//MMOItems.Log(" \u00a7e@ \u00a77Applying Upgrade");
|
||||
|
||||
// If has a upgrade template defined, just remember the level
|
||||
if (mmoItem.hasData(ItemStats.UPGRADE)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7e* \u00a77Existing Upgrade Detected");
|
||||
|
||||
// Get current ig
|
||||
UpgradeData current = ((UpgradeData) mmoItem.getData(ItemStats.UPGRADE));
|
||||
|
||||
// Edit level
|
||||
current.setLevel(Math.min(cachedUpgradeLevel.getLevel(), current.getMaxUpgrades()));
|
||||
//UPDT//MMOItems.Log(" \u00a7e + \u00a77Set to level \u00a7f" + current.getLevel());
|
||||
|
||||
// Re-set cuz why not
|
||||
mmoItem.setData(ItemStats.UPGRADE, current);
|
||||
|
||||
// Otherwise, the level AND template shall prevail
|
||||
} else {
|
||||
//UPDT//MMOItems.Log(" \u00a7e* \u00a77Using Cached");
|
||||
|
||||
// Set from the cached
|
||||
mmoItem.setData(ItemStats.UPGRADE, cachedUpgradeLevel);
|
||||
//UPDT//MMOItems.Log(" \u00a7e + \u00a77Set to level \u00a7f" + cachedUpgradeLevel.getLevel());
|
||||
}
|
||||
}
|
||||
|
||||
// Gem Stones
|
||||
if (cachedGemStones != null) {
|
||||
//UPDT//MMOItems.Log(" \u00a7a@ \u00a77Applying Gemstones");
|
||||
|
||||
// If has a upgrade template defined, just remember the level
|
||||
if (mmoItem.hasData(ItemStats.GEM_SOCKETS)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7a* \u00a77Existing Data Detected");
|
||||
|
||||
// Get current ig
|
||||
GemSocketsData current = ((GemSocketsData) mmoItem.getData(ItemStats.GEM_SOCKETS));
|
||||
|
||||
// Get those damn empty sockets
|
||||
ArrayList<String> availableSockets = new ArrayList<>(current.getEmptySlots());
|
||||
ArrayList<GemstoneData> oldSockets = new ArrayList<>(cachedGemStones.getGemstones());
|
||||
|
||||
// Remaining
|
||||
for (GemstoneData data : oldSockets) {
|
||||
//UPDT//MMOItems.Log(" \u00a7a*\u00a7e* \u00a77Fitting \u00a7f" + data.getHistoricUUID().toString());
|
||||
|
||||
// No more if no more sockets left
|
||||
if (availableSockets.size() <= 0) {
|
||||
//UPDT//MMOItems.Log(" \u00a7a +\u00a7c+ \u00a77No More Sockets");
|
||||
|
||||
// They all will fit anyway
|
||||
break;
|
||||
|
||||
// Still some sockets to fill hMMM
|
||||
} else {
|
||||
|
||||
// Get colour
|
||||
String colour = data.getColour();
|
||||
String remembrance;
|
||||
|
||||
// Not null?
|
||||
if (colour != null) {
|
||||
|
||||
// Contained? Remove
|
||||
remembrance = colour;
|
||||
|
||||
// No colour data, just remove a random slot ig
|
||||
} else {
|
||||
|
||||
// Get and remove
|
||||
remembrance = availableSockets.get(0);
|
||||
}
|
||||
|
||||
// Remove
|
||||
availableSockets.remove(remembrance);
|
||||
|
||||
// And guess what... THAT is the colour of this gem! Fabulous huh?
|
||||
data.setColour(remembrance);
|
||||
//UPDT//MMOItems.Log(" \u00a7a + \u00a77Fit into color \u00a7f" + remembrance);
|
||||
}
|
||||
}
|
||||
|
||||
// Update list of empty sockets
|
||||
cachedGemStones.getEmptySlots().clear();
|
||||
cachedGemStones.getEmptySlots().addAll(availableSockets);
|
||||
}
|
||||
|
||||
// Set the data, as changed as it may be
|
||||
mmoItem.setData(ItemStats.GEM_SOCKETS, cachedGemStones);
|
||||
}
|
||||
|
||||
// Lore
|
||||
if (!cachedLore.isEmpty()) {
|
||||
//UPDT//MMOItems.Log(" \u00a7d@ \u00a77Applying Lore");
|
||||
|
||||
// If it has lore, add I guess
|
||||
if (mmoItem.hasData(ItemStats.LORE)) {
|
||||
//UPDT//MMOItems.Log(" \u00a7d* \u00a77Inserting first");
|
||||
|
||||
// Get current ig
|
||||
StringListData current = ((StringListData) mmoItem.getData(ItemStats.LORE));
|
||||
|
||||
// Get those damn empty sockets
|
||||
ArrayList<String> listYes = new ArrayList<>(current.getList());
|
||||
|
||||
// Append to the end of the cached >:]
|
||||
cachedLore.addAll(listYes);
|
||||
}
|
||||
|
||||
// Create stat
|
||||
StringListData sData = new StringListData(cachedLore);
|
||||
//UPDT//for (String lr : cachedLore) { //UPDT//MMOItems.Log(" \u00a7d + \u00a77" + lr); }
|
||||
|
||||
// Set that as the lore
|
||||
mmoItem.setData(ItemStats.LORE, sData);
|
||||
}
|
||||
|
||||
// Name
|
||||
if (cachedName != null) {
|
||||
//UPDT//MMOItems.Log(" \u00a73@ \u00a77Applying Name \u00a7f" + cachedName);
|
||||
|
||||
// Replace name completely
|
||||
mmoItem.setData(ItemStats.NAME, new StringData(cachedName));
|
||||
}
|
||||
|
||||
// Apply upgrades
|
||||
if (mmoItem.hasUpgradeTemplate()) { mmoItem.getUpgradeTemplate().upgradeTo(mmoItem, mmoItem.getUpgradeLevel()); }
|
||||
|
||||
// Build and set amount
|
||||
ItemStack stack = mmoItem.newBuilder().build();
|
||||
stack.setAmount(amount);
|
||||
return stack;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the MMOItem as a LiveMMOItem if it's null or not already a
|
||||
* LiveMMOItem
|
||||
*/
|
||||
private void loadLiveMMOItem() {
|
||||
if (mmoItem != null && mmoItem instanceof LiveMMOItem) { return; }
|
||||
mmoItem = new LiveMMOItem(nbtItem);
|
||||
}
|
||||
|
||||
/* Initialize the MMOItem as a VolatileMMOItem if it's null */
|
||||
private void loadVolatileMMOItem() {
|
||||
if (mmoItem != null) { return;}
|
||||
mmoItem = new VolatileMMOItem(nbtItem);
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
package net.Indyuce.mmoitems.gui.edition;
|
||||
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import io.lumine.mythic.lib.version.VersionMaterial;
|
||||
import io.lumine.mythic.utils.items.ItemFactory;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.MMOUtils;
|
||||
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
|
||||
import net.Indyuce.mmoitems.api.util.MMOItemReforger;
|
||||
import net.Indyuce.mmoitems.stat.RevisionID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Inventory displayed when enabling the item updater.
|
||||
* @see RevisionID
|
||||
* @see MMOItemReforger
|
||||
*
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RevisionInventory extends EditionInventory {
|
||||
|
||||
static ItemStack name;
|
||||
static ItemStack lore;
|
||||
static ItemStack enchantments;
|
||||
static ItemStack upgrades;
|
||||
static ItemStack gemstones;
|
||||
static ItemStack soulbind;
|
||||
|
||||
static ItemStack revisionID;
|
||||
|
||||
static final String REVISION = "\u00a76Revision ID";
|
||||
|
||||
public RevisionInventory(@NotNull Player player, @NotNull MMOItemTemplate template) {
|
||||
super(player, template);
|
||||
|
||||
// If null
|
||||
if (revisionID == null) {
|
||||
|
||||
name = ItemFactory.of(Material.NAME_TAG).name("\u00a73Name").lore(SilentNumbers.Chop(
|
||||
"The display name of the old item will be transferred to the new one"
|
||||
, 40, "\u00a77")).build();
|
||||
|
||||
lore = ItemFactory.of(VersionMaterial.WRITABLE_BOOK.toMaterial()).name("\u00a7dLore").lore(SilentNumbers.Chop(
|
||||
"Specifically keeps lore lines that begin with the color code \u00a7n&7"
|
||||
, 40, "\u00a77")).build();
|
||||
|
||||
enchantments = ItemFactory.of(Material.EXPERIENCE_BOTTLE).name("\u00a7bEnchantments").lore(SilentNumbers.Chop(
|
||||
"This keeps specifically enchantments that are not accounted for in upgrades nor gem stones (presumably added by the player)."
|
||||
, 40, "\u00a77")).build();
|
||||
|
||||
upgrades = ItemFactory.of(Material.NETHER_STAR).name("\u00a7aUpgrades").lore(SilentNumbers.Chop(
|
||||
"Will this item retain the upgrade level after updating?"
|
||||
, 40, "\u00a77")).build();
|
||||
|
||||
gemstones = ItemFactory.of(Material.EMERALD).name("\u00a7eGem Stones").lore(SilentNumbers.Chop(
|
||||
"Will the item retain its gem stones when updating? (Note that this allows gemstone overflow - will keep ALL old gemstones even if you reduced the gem sockets)"
|
||||
, 40, "\u00a77")).build();
|
||||
|
||||
soulbind = ItemFactory.of(Material.ENDER_EYE).name("\u00a7cSoulbind").lore(SilentNumbers.Chop(
|
||||
"If the old item is soulbound, updating will transfer the soulbind to the new item."
|
||||
, 40, "\u00a77")).build();
|
||||
|
||||
|
||||
// Fill stack
|
||||
revisionID = ItemFactory.of(Material.ITEM_FRAME).name(REVISION).lore(SilentNumbers.Chop(
|
||||
"The updater is always active, increasing this number will update all instances of this MMOItem without further action."
|
||||
, 40, "\u00a77")).build();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull @Override public Inventory getInventory() {
|
||||
Inventory inv = Bukkit.createInventory(this, 54, "Revision Manager");
|
||||
|
||||
// Place corresponding item stacks in there
|
||||
for (int i = 0; i < inv.getSize(); i++) {
|
||||
|
||||
// What item to even put here
|
||||
ItemStack which = null;
|
||||
Boolean enable = null;
|
||||
Integer id = null;
|
||||
switch (i) {
|
||||
case 19:
|
||||
which = name.clone();
|
||||
enable = MMOItems.plugin.getLanguage().revisionOptions.shouldKeepName();
|
||||
break;
|
||||
case 20:
|
||||
which = lore.clone();
|
||||
enable = MMOItems.plugin.getLanguage().revisionOptions.shouldKeepLore();
|
||||
break;
|
||||
case 21:
|
||||
which = enchantments.clone();
|
||||
enable = MMOItems.plugin.getLanguage().revisionOptions.shouldKeepEnchantments();
|
||||
break;
|
||||
case 28:
|
||||
which = upgrades.clone();
|
||||
enable = MMOItems.plugin.getLanguage().revisionOptions.shouldKeepUpgrades();
|
||||
break;
|
||||
case 29:
|
||||
which = gemstones.clone();
|
||||
enable = MMOItems.plugin.getLanguage().revisionOptions.shouldKeepGemStones();
|
||||
break;
|
||||
case 30:
|
||||
which = soulbind.clone();
|
||||
enable = MMOItems.plugin.getLanguage().revisionOptions.shouldKeepSoulbind();
|
||||
break;
|
||||
case 33:
|
||||
id = getEditedSection().getInt(ItemStats.REVISION_ID.getPath(), 1);
|
||||
which = revisionID.clone();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// If an item corresponds to this slot
|
||||
if (which != null) {
|
||||
|
||||
// If it is defined to be enabled
|
||||
if (enable != null) {
|
||||
|
||||
// Add mentioning if enabled
|
||||
inv.setItem(i, addLore(which, "", "\u00a78Enabled (in config)? \u00a76" + enable.toString()));
|
||||
|
||||
// If ID is enabled
|
||||
} else if (id != null) {
|
||||
|
||||
// Add mentioning if enabled
|
||||
inv.setItem(i, addLore(which, "", "\u00a78Current Value: \u00a76" + id));
|
||||
|
||||
// Neither enable nor ID are defined
|
||||
} else {
|
||||
|
||||
// Add
|
||||
inv.setItem(i, which);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the top items, including the {Back} button
|
||||
addEditionInventoryItems(inv, true);
|
||||
|
||||
return inv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenClicked(InventoryClickEvent event) {
|
||||
// Get the clicked item
|
||||
ItemStack item = event.getCurrentItem();
|
||||
event.setCancelled(true);
|
||||
|
||||
// If the click did not happen in the correct inventory, or the item is not clickable
|
||||
if (event.getInventory() != event.getClickedInventory() || !MMOUtils.isMetaItem(item, false)) { return; }
|
||||
|
||||
// Is the player clicking the revision ID thing?
|
||||
if (item.getItemMeta().getDisplayName().equals(REVISION)) {
|
||||
|
||||
// Get the current ID
|
||||
int id = getEditedSection().getInt(ItemStats.REVISION_ID.getPath(), 1);
|
||||
|
||||
// If right click
|
||||
if (event.getAction() == InventoryAction.PICKUP_HALF) {
|
||||
|
||||
// Decrease by 1, but never before 1
|
||||
id = Math.max(id - 1, 1);
|
||||
|
||||
// Any other click
|
||||
} else {
|
||||
|
||||
// Increase by 1 until the ultimate maximum
|
||||
id = Math.min(id + 1, Integer.MAX_VALUE);
|
||||
|
||||
}
|
||||
|
||||
// Register edition
|
||||
getEditedSection().set(ItemStats.REVISION_ID.getPath(), id);
|
||||
registerTemplateEdition();
|
||||
|
||||
// Update ig
|
||||
event.setCurrentItem(addLore(revisionID.clone(), "", "\u00a78Current Value: \u00a76" + id));
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull ItemStack addLore(@NotNull ItemStack iSource, String... extraLines){
|
||||
|
||||
// Get its lore
|
||||
ArrayList<String> iLore = new ArrayList<>();
|
||||
ItemMeta iMeta = iSource.getItemMeta();
|
||||
if (iMeta != null && iMeta.getLore() != null) {iLore = new ArrayList<>(iMeta.getLore()); }
|
||||
|
||||
// Add lines
|
||||
iLore.addAll(Arrays.asList(extraLines));
|
||||
|
||||
// Put lore
|
||||
iMeta.setLore(iLore);
|
||||
|
||||
// Yes
|
||||
iSource.setItemMeta(iMeta);
|
||||
|
||||
// Yes
|
||||
return iSource;
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
package net.Indyuce.mmoitems.listener;
|
||||
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.util.ItemModInstance;
|
||||
import net.Indyuce.mmoitems.api.util.MMOItemReforger;
|
||||
import io.lumine.mythic.lib.api.item.NBTItem;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -14,6 +15,8 @@ import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ItemListener implements Listener {
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
@ -67,13 +70,26 @@ public class ItemListener implements Listener {
|
||||
if(newItem != null) player.getEquipment().setItemInOffHand(newItem);
|
||||
}
|
||||
|
||||
private ItemStack modifyItem(ItemStack stack, Player player, String type) {
|
||||
@Nullable private ItemStack modifyItem(@NotNull ItemStack stack, @NotNull Player player, @NotNull String type) {
|
||||
|
||||
// Get the item
|
||||
NBTItem nbt = NBTItem.get(stack);
|
||||
if (!nbt.hasType()) return null;
|
||||
ItemModInstance mod = new ItemModInstance(nbt);
|
||||
if (shouldUpdate(nbt, type))
|
||||
|
||||
// Not a MMOItem? not our problem.
|
||||
if (!nbt.hasType()) { return null; }
|
||||
|
||||
// ??
|
||||
MMOItemReforger mod = new MMOItemReforger(nbt);
|
||||
|
||||
// Should this item be updated (via the updater)
|
||||
if (shouldUpdate(nbt, type)) {
|
||||
mod.reforge(MMOItems.plugin.getLanguage().rerollOnItemUpdate ? player : null, MMOItems.plugin.getLanguage().revisionOptions);
|
||||
if (shouldSoulbind(nbt, type)) mod.applySoulbound(player);
|
||||
}
|
||||
|
||||
// Should this item be soulbount?
|
||||
if (shouldSoulbind(nbt, type)) { mod.applySoulbound(player); }
|
||||
|
||||
// Return either the changed one or null
|
||||
return mod.hasChanges() ? mod.toStack() : null;
|
||||
}
|
||||
|
||||
@ -87,9 +103,24 @@ public class ItemListener implements Listener {
|
||||
private boolean shouldUpdate(NBTItem nbt, String type) {
|
||||
if(!MMOItems.plugin.getTemplates().hasTemplate(nbt)) return false;
|
||||
|
||||
return !MMOItems.plugin.getConfig().getBoolean("item-revision.disable-on." + type) &&
|
||||
((MMOItems.plugin.getTemplates().getTemplate(nbt).getRevisionId() > (nbt.hasTag("MMOITEMS_REVISION_ID")
|
||||
? nbt.getInteger("MMOITEMS_REVISION_ID") : 1)) || (MMOItems.INTERNAL_REVISION_ID >
|
||||
(nbt.hasTag("MMOITEMS_INTERNAL_REVISION_ID") ? nbt.getInteger("MMOITEMS_INTERNAL_REVISION_ID") : 1)) );
|
||||
return
|
||||
// It must not be disabled for this item
|
||||
!MMOItems.plugin.getConfig().getBoolean("item-revision.disable-on." + type) &&
|
||||
|
||||
|
||||
// Either the normal revision ID or the internal revision IDs are out of date.
|
||||
(
|
||||
// Is the template's revision ID greater than the one currently in the item?
|
||||
(MMOItems.plugin.getTemplates().getTemplate(nbt).getRevisionId() >
|
||||
|
||||
// The one 'currently in the item' is the value of the stat, or 1 if missing.
|
||||
(nbt.hasTag(ItemStats.REVISION_ID.getNBTPath()) ? nbt.getInteger(ItemStats.REVISION_ID.getNBTPath()) : 1)) ||
|
||||
|
||||
// Or the MMOItems internal revision ID itself
|
||||
(MMOItems.INTERNAL_REVISION_ID >
|
||||
|
||||
// Same thing: either the value of the stat or 1 if missing.
|
||||
(nbt.hasTag(ItemStats.INTERNAL_REVISION_ID.getNBTPath()) ? nbt.getInteger(ItemStats.INTERNAL_REVISION_ID.getNBTPath()) : 1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author Gunging
|
||||
*/
|
||||
public class Amphibian extends ChooseStat implements ItemRestriction, GemStoneStat {
|
||||
public static final String
|
||||
NORMAL = "UNRESTRICTED",
|
||||
|
@ -30,6 +30,15 @@ public class DisplayName extends StringStat {
|
||||
format = format.replace("<tier-name>", tier != null ? ChatColor.stripColor(tier.getName()) : "");
|
||||
format = format.replace("<tier-color>", tier != null ? ChatColor.getLastColors(tier.getName()) : "&f");
|
||||
|
||||
int upgradeLevel = item.getMMOItem().getUpgradeLevel();
|
||||
String suffix = MythicLib.plugin.parseColors(MMOItems.plugin.getConfig().getString("item-upgrading.name-suffix"));
|
||||
String oldSuffix = suffix.replace("#lvl#", String.valueOf(upgradeLevel - 1));
|
||||
String actSuffix = suffix.replace("#lvl#", String.valueOf(upgradeLevel));
|
||||
String oldSuffix2 = suffix.replace("#lvl#", String.valueOf(upgradeLevel + 1));
|
||||
format = format.replace(oldSuffix, "").replace(oldSuffix2, "").replace(actSuffix, "");
|
||||
if (upgradeLevel != 0) { format = format + actSuffix; }
|
||||
|
||||
|
||||
item.getMeta().setDisplayName(MythicLib.inst().parseColors(format));
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,12 @@ import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import io.lumine.mythic.lib.api.util.ui.PlusMinusPercent;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
|
||||
import net.Indyuce.mmoitems.stat.data.DoubleData;
|
||||
import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo;
|
||||
import net.Indyuce.mmoitems.stat.type.DoubleStat;
|
||||
import net.Indyuce.mmoitems.stat.type.StatHistory;
|
||||
import net.Indyuce.mmoitems.stat.type.Upgradable;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -192,7 +194,16 @@ public class Enchants extends ItemStat implements Upgradable {
|
||||
|
||||
// Enchant Item
|
||||
EnchantListData enchants = (EnchantListData) data;
|
||||
for (Enchantment enchant : enchants.getEnchants()) { item.getMeta().addEnchant(enchant, enchants.getLevel(enchant), true); }
|
||||
for (Enchantment enchant : enchants.getEnchants()) {
|
||||
|
||||
// Get level
|
||||
int lvl = enchants.getLevel(enchant);
|
||||
|
||||
// Not ZERO bruh
|
||||
if (lvl != 0) {
|
||||
item.getMeta().addEnchant(enchant, enchants.getLevel(enchant), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply tags
|
||||
item.addItemTag(getAppliedNBT(data));
|
||||
@ -296,6 +307,57 @@ public class Enchants extends ItemStat implements Upgradable {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Players may enchant an item via 'vanilla' means (Not Gemstones, not Upgrades).
|
||||
* If they do so, the enchantments of their item will be wiped if they try to put
|
||||
* a gemstone in or upgrade the item, which is not nice.
|
||||
* <p></p>
|
||||
* This operation classifies these discrepancies in the enchantment levels as
|
||||
* 'externals' in the Stat History of enchantments. From then on, they will be
|
||||
* accounted for.
|
||||
*/
|
||||
public static void separateEnchantments(@NotNull MMOItem mmoitem) {
|
||||
|
||||
// Cancellation because the player could not have done so HMMM
|
||||
if (mmoitem.hasData(ItemStats.DISABLE_REPAIRING) && mmoitem.hasData(ItemStats.DISABLE_ENCHANTING)) { return; }
|
||||
|
||||
// Does it have enchantment data?
|
||||
if (mmoitem.hasData(ItemStats.ENCHANTS)) {
|
||||
|
||||
// Get that data
|
||||
EnchantListData data = (EnchantListData) mmoitem.getData(ItemStats.ENCHANTS);
|
||||
StatHistory<StatData> hist = StatHistory.From(mmoitem, ItemStats.ENCHANTS);
|
||||
|
||||
// All right, whats the expected enchantment levels?
|
||||
EnchantListData expected = (EnchantListData) hist.Recalculate();
|
||||
|
||||
// Gather a list of extraneous enchantments
|
||||
HashMap<Enchantment, Integer> discrepancies = new HashMap<>();
|
||||
|
||||
// For every enchantment
|
||||
for (Enchantment e : Enchantment.values()) {
|
||||
|
||||
// Get both
|
||||
int actual = data.getLevel(e);
|
||||
int ideal = expected.getLevel(e);
|
||||
|
||||
// Get difference, and register.
|
||||
int offset = actual - ideal;
|
||||
if (offset != 0) { discrepancies.put(e, offset); }
|
||||
}
|
||||
|
||||
// It has been extracted
|
||||
if (discrepancies.size() > 0) {
|
||||
// Generate enchantment list with offsets
|
||||
EnchantListData extraneous = new EnchantListData();
|
||||
for (Enchantment e : discrepancies.keySet()) { extraneous.addEnchant(e, discrepancies.get(e));}
|
||||
|
||||
// Register extraneous
|
||||
hist.registerExternalData(extraneous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class EnchantUpgradeInfo implements UpgradeInfo {
|
||||
@NotNull HashMap<Enchantment, PlusMinusPercent> perEnchantmentOperations = new HashMap<>();
|
||||
|
||||
|
@ -14,6 +14,9 @@ import org.bukkit.Material;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Gunging
|
||||
*/
|
||||
public class RequiredBiomes extends StringListStat implements ItemRestriction, GemStoneStat {
|
||||
public RequiredBiomes() {
|
||||
super("REQUIRED_BIOMES", Material.JUNGLE_SAPLING, "Required Biomes", new String[] { "The biome the player must be within", "for this item to activate." }, new String[] { "!block", "all" });
|
||||
|
@ -5,6 +5,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.lumine.mythic.lib.api.item.SupportedNBTTagValues;
|
||||
import net.Indyuce.mmoitems.api.util.MMOItemReforger;
|
||||
import net.Indyuce.mmoitems.gui.edition.AbilityListEdition;
|
||||
import net.Indyuce.mmoitems.gui.edition.RevisionInventory;
|
||||
import net.Indyuce.mmoitems.stat.type.GemStoneStat;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
@ -24,10 +27,15 @@ import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Regarding the auto updating of items
|
||||
* @see RevisionInventory
|
||||
* @see MMOItemReforger
|
||||
*/
|
||||
public class RevisionID extends ItemStat implements GemStoneStat {
|
||||
public RevisionID() {
|
||||
super("REVISION_ID", Material.ITEM_FRAME, "Revision ID", new String[] { "The Revision ID is used to determine",
|
||||
"if an item is outdated or not. You", "should increase this whenever", "you make changes to your item!"},
|
||||
"if an item is outdated or not. You", "should increase this whenever", "you make changes to your item!", "", "\u00a76The updater is smart and will apply", "\u00a76changes to the base stats of the item,", "\u00a76keeping gemstones intact (for example)."},
|
||||
new String[] { "all" });
|
||||
}
|
||||
|
||||
@ -54,19 +62,11 @@ public class RevisionID extends ItemStat implements GemStoneStat {
|
||||
|
||||
@Override
|
||||
public void whenClicked(@NotNull EditionInventory inv, @NotNull InventoryClickEvent event) {
|
||||
int id = inv.getEditedSection().getInt(getPath(), 1);
|
||||
if (event.getAction() == InventoryAction.PICKUP_HALF) {
|
||||
inv.getEditedSection().set(getPath(), Math.max(id - 1, 1));
|
||||
inv.registerTemplateEdition();
|
||||
return;
|
||||
}
|
||||
|
||||
inv.getEditedSection().set(getPath(), Math.min(id + 1, Integer.MAX_VALUE));
|
||||
inv.registerTemplateEdition();
|
||||
new RevisionInventory(inv.getPlayer(), inv.getEdited()).open(inv.getPage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) {}
|
||||
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) { }
|
||||
|
||||
@Override
|
||||
public void whenLoaded(@NotNull ReadMMOItem mmoitem) {
|
||||
|
@ -4,6 +4,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
|
||||
@ -32,8 +33,19 @@ public class EnchantListData implements StatData, Mergeable {
|
||||
public void merge(StatData data) {
|
||||
Validate.isTrue(data instanceof EnchantListData, "Cannot merge two different stat data types");
|
||||
Map<Enchantment, Integer> extra = ((EnchantListData) data).enchants;
|
||||
for (Enchantment enchant : extra.keySet())
|
||||
enchants.put(enchant, enchants.containsKey(enchant) ? Math.max(extra.get(enchant), enchants.get(enchant)) : extra.get(enchant));
|
||||
boolean additiveMerge = MMOItems.plugin.getConfig().getBoolean("stat-merging.additive-enchantments", false);
|
||||
|
||||
for (Enchantment enchant : extra.keySet()) {
|
||||
if (additiveMerge) {
|
||||
|
||||
// Additive
|
||||
enchants.put(enchant, extra.get(enchant) + enchants.get(enchant));
|
||||
} else {
|
||||
|
||||
// Max Enchantment
|
||||
enchants.put(enchant, enchants.containsKey(enchant) ? extra.get(enchant) + enchants.get(enchant) : extra.get(enchant));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -21,7 +21,10 @@ public class GemstoneData {
|
||||
@Nullable Integer levelPut = 0;
|
||||
@NotNull final UUID historicUUID;
|
||||
|
||||
@Nullable final String mmoitemType, mmoitemID, socketColor;
|
||||
@Nullable final String mmoitemType;
|
||||
@Nullable final String mmoitemID;
|
||||
@Nullable
|
||||
String socketColor;
|
||||
|
||||
/**
|
||||
* This constructor is not really performance friendly. It should only be
|
||||
@ -168,6 +171,16 @@ public class GemstoneData {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* If known, the socket colour this gem was put into
|
||||
*/
|
||||
@Nullable public String getColour() { return socketColor; }
|
||||
|
||||
/**
|
||||
* If known, the socket colour this gem was put into
|
||||
*/
|
||||
public void setColour(@Nullable String color) { socketColor = color; }
|
||||
|
||||
/**
|
||||
* Want to know which stats were given to the item by this gemstone (after applying upgrade scaling and such)? Use this!
|
||||
*/
|
||||
|
@ -106,28 +106,6 @@ public class UpgradeData implements StatData, RandomStatData {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Display Upgrade Level
|
||||
*/
|
||||
String suffix = MythicLib.plugin.parseColors(MMOItems.plugin.getConfig().getString("item-upgrading.name-suffix"));
|
||||
if (MMOItems.plugin.getConfig().getBoolean("item-upgrading.display-in-name"))
|
||||
if (mmoitem.hasData(ItemStats.NAME)) {
|
||||
StringData nameData = (StringData) mmoitem.getData(ItemStats.NAME);
|
||||
nameData.setString(level == 0 ? nameData.toString() + suffix.replace("#lvl#", "" + (level + 1))
|
||||
: nameData.toString().replace(suffix.replace("#lvl#", "" + level), suffix.replace("#lvl#", "" + (level + 1))));
|
||||
}
|
||||
|
||||
/*TODO: implement this as a new dynamic lore type
|
||||
else if (mmoitem.hasData(ItemStats.LORE)) {
|
||||
StringListData loreData = (StringListData) mmoitem.getData(ItemStats.LORE);
|
||||
loreData.getList().forEach(line -> {
|
||||
if (line.contains("%upgrade_level%") || line.contains(suffix.replace("#lvl#", "" + level))) {
|
||||
line.replace("%upgrade_level%", suffix.replace("#lvl#", "" + level + 1));
|
||||
line.replace(suffix.replace("#lvl#", "" + level), suffix.replace("#lvl#", "" + level + 1));
|
||||
}
|
||||
});
|
||||
}*/
|
||||
|
||||
/*
|
||||
* Go through every stat that must be ugpraded and apply
|
||||
*/
|
||||
|
@ -3,6 +3,7 @@ package net.Indyuce.mmoitems.stat.type;
|
||||
import io.lumine.mythic.lib.MythicLib;
|
||||
import io.lumine.mythic.lib.api.item.ItemTag;
|
||||
import io.lumine.mythic.lib.api.util.AltChar;
|
||||
import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
|
||||
@ -96,7 +97,7 @@ public abstract class ChooseStat extends StringStat {
|
||||
}
|
||||
|
||||
// Get Definition
|
||||
if (chooseableDefs.containsKey(def)) { for (String deff : Chop(chooseableDefs.get(def), 50)) { lore.add(ChatColor.GRAY + " " + deff); } }
|
||||
if (chooseableDefs.containsKey(def)) { for (String definition : SilentNumbers.Chop(chooseableDefs.get(def), 50, "")) { lore.add(ChatColor.GRAY + " " + definition); } }
|
||||
|
||||
lore.add("");
|
||||
lore.add(ChatColor.YELLOW + AltChar.listDash + " Right click to return to default value.");
|
||||
@ -123,39 +124,4 @@ public abstract class ChooseStat extends StringStat {
|
||||
*/
|
||||
@NotNull HashMap<String, String> chooseableDefs = new HashMap<>();
|
||||
public void HintChooseableDefs(@NotNull HashMap<String, String> list) { chooseableDefs = list; }
|
||||
|
||||
/**
|
||||
* Chops a long description into several parts.
|
||||
*/
|
||||
ArrayList<String> Chop(String longString, int paragraphWide) {
|
||||
|
||||
// Ret
|
||||
ArrayList<String> ret = new ArrayList<>();
|
||||
boolean skip = false;
|
||||
|
||||
// While longer
|
||||
while (longString.length() > paragraphWide) {
|
||||
|
||||
// Skip
|
||||
skip = true;
|
||||
|
||||
// Get the wide
|
||||
int idx = longString.lastIndexOf(" ", paragraphWide + 1);
|
||||
|
||||
// Chop
|
||||
ret.add(longString.substring(0, idx));
|
||||
|
||||
// Update
|
||||
longString = longString.substring(idx + 1);
|
||||
|
||||
// Add final.
|
||||
if (longString.length() <= paragraphWide) { ret.add(longString); }
|
||||
}
|
||||
|
||||
// Wasnt long at all
|
||||
if (!skip) { ret.add(longString); }
|
||||
|
||||
// Thats it
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.google.gson.*;
|
||||
import io.lumine.mythic.lib.api.item.ItemTag;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackCategory;
|
||||
import io.lumine.mythic.lib.api.util.ui.FriendlyFeedbackProvider;
|
||||
import net.Indyuce.mmoitems.ItemStats;
|
||||
import net.Indyuce.mmoitems.MMOItems;
|
||||
import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
|
||||
import net.Indyuce.mmoitems.api.util.message.FriendlyFeedbackPalette_MMOItems;
|
||||
@ -125,9 +126,9 @@ public class StatHistory<S extends StatData> {
|
||||
|
||||
// Found? Thats it
|
||||
if (hist != null) {
|
||||
//UPGRD//MMOItems. Log("Found Stat History of \u00a76" + ofStat.getNBTPath() + "\u00a77 in this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
|
||||
//UPGRD//MMOItems.Log("Found Stat History of \u00a76" + ofStat.getNBTPath() + "\u00a77 in this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
|
||||
return hist; }
|
||||
//UPGRD//MMOItems. Log("\u00a7aCreated Hisotry of \u00a76" + ofStat.getNBTPath() + "\u00a7a of this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
|
||||
//UPGRD//MMOItems.Log("\u00a7aCreated Hisotry of \u00a76" + ofStat.getNBTPath() + "\u00a7a of this \u00a7c" + ofItem.getType().getName() + " " + ofItem.getId());
|
||||
|
||||
// That is Mergeable right...
|
||||
Validate.isTrue(ofStat.getClearStatData() instanceof Mergeable, "Non-Mergeable stat data wont have a Stat History; they cannot be modified dynamically in the first place.");
|
||||
@ -136,11 +137,12 @@ public class StatHistory<S extends StatData> {
|
||||
StatData original = ofItem.getData(ofStat);
|
||||
if (original == null) {
|
||||
original = ofStat.getClearStatData();
|
||||
//UPGRD//MMOItems. Log("\u00a7e +\u00a77 Item didnt have this stat, original set as blanc.");
|
||||
ofItem.setData(ofStat, original);
|
||||
//UPGRD//MMOItems.Log("\u00a7e +\u00a77 Item didnt have this stat, original set as blanc.");
|
||||
}
|
||||
else {
|
||||
original = ((Mergeable) original).cloneData();
|
||||
//UPGRD//MMOItems. Log("\u00a7a +\u00a77 Found original data");
|
||||
//UPGRD//MMOItems.Log("\u00a7a +\u00a77 Found original data");
|
||||
}
|
||||
|
||||
// Create new
|
||||
@ -160,13 +162,72 @@ public class StatHistory<S extends StatData> {
|
||||
*/
|
||||
StatHistory(@NotNull MMOItem ofItem, @NotNull ItemStat ofStat, @NotNull S ogData) { itemStat = ofStat; originalData = ogData; parent = ofItem; }
|
||||
|
||||
/**
|
||||
* Checks the item and makes sure that the UUIDs
|
||||
* attributed to gemstones link to existing gem
|
||||
* stones. Removes them if no such gemstone exists.
|
||||
*/
|
||||
public void PurgeGemstones() {
|
||||
|
||||
// Which will get purged...
|
||||
ArrayList<UUID> extraneous = new ArrayList<>();
|
||||
GemSocketsData data = (GemSocketsData) getMMOItem().getData(ItemStats.GEM_SOCKETS);
|
||||
if (data == null) { data = new GemSocketsData(new ArrayList<>()); }
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister
|
||||
for (UUID ext : extraneous) {
|
||||
//UPGRD//MMOItems.Log("\u00a76 ||\u00a77 Purged Stone: \u00a7e" + ext.toString());
|
||||
|
||||
// Remove
|
||||
perGemstoneData.remove(ext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
@NotNull public S Recalculate() {
|
||||
@NotNull public S Recalculate() { return Recalculate(true); }
|
||||
/**
|
||||
* 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 S Recalculate(boolean withPurge) {
|
||||
if (withPurge) { PurgeGemstones(); }
|
||||
|
||||
// If its upgradeable and not level ZERO, it must apply upgrades
|
||||
if ((getMMOItem().getUpgradeLevel() != 0) &&
|
||||
@ -181,12 +242,26 @@ public class StatHistory<S extends StatData> {
|
||||
// Merge Normally
|
||||
return Recalculate_ThroughClone();
|
||||
}
|
||||
|
||||
/**
|
||||
* This recalculates values accounting only for gemstones and external data.
|
||||
* <p></p>
|
||||
* In case someone was wondered the contribution of upgrading the item.
|
||||
* In case someone was wondered the contribution of upgrading the item, just
|
||||
* substract it from {@link #Recalculate()}
|
||||
*/
|
||||
@NotNull public S Recalculate_Unupgraded() {
|
||||
@NotNull public S Recalculate_Unupgraded() { return Recalculate_Unupgraded(true); }
|
||||
|
||||
/**
|
||||
* 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()}
|
||||
* @param withPurge Check if the gemstones UUIDs are valid.
|
||||
* Leave <code>true</code> unless you know
|
||||
* what you're doing.
|
||||
*/
|
||||
@NotNull public S Recalculate_Unupgraded(boolean withPurge) {
|
||||
if (withPurge) { PurgeGemstones(); }
|
||||
|
||||
// Merge Normally
|
||||
return Recalculate_ThroughClone();
|
||||
@ -203,7 +278,7 @@ public class StatHistory<S extends StatData> {
|
||||
* <p>5: Sums external data (modifiers that are not linked to an ID, I suppose by external plugins).
|
||||
*/
|
||||
private S Recalculate_AsUpgradeable() {
|
||||
//UPGRD//MMOItems. Log("\u00a76|||\u00a77 Calculating as Upgradeable");
|
||||
//UPGRD//MMOItems.Log("\u00a76|||\u00a77 Calculating \u00a7f" + getItemStat().getNBTPath() + "\u00a77 as Upgradeable");
|
||||
|
||||
// Get Upgrade Info?
|
||||
UpgradeInfo inf = getMMOItem().getUpgradeTemplate().getUpgradeInfo(getItemStat());
|
||||
@ -216,7 +291,7 @@ public class StatHistory<S extends StatData> {
|
||||
|
||||
// Level up
|
||||
int lvl = getMMOItem().getUpgradeLevel();
|
||||
//UPGRD//MMOItems. Log("\u00a76 ||\u00a77 Item Level: \u00a7e" + lvl);
|
||||
//UPGRD//MMOItems.Log("\u00a76 ||\u00a77 Item Level: \u00a7e" + lvl);
|
||||
//UPGRD//MMOItems. Log("\u00a76 >\u00a77 Original Base: \u00a7e" + ((DoubleData) ogCloned).getValue());
|
||||
S ret = (S) ((Upgradable) getItemStat()).apply(ogCloned, inf, lvl);
|
||||
//UPGRD//MMOItems. Log("\u00a76 >\u00a77 Leveled Base: \u00a7e" + ((DoubleData) ret).getValue());
|
||||
@ -248,7 +323,7 @@ public class StatHistory<S extends StatData> {
|
||||
|
||||
// Calculate level difference
|
||||
int gLevel = lvl - level;
|
||||
//UPGRD//MMOItems. Log("\u00a76 |\u00a7b|\u00a76>\u00a77 Gemstone Level: \u00a7e" + gLevel + "\u00a77 (Put at \u00a7b" + level + "\u00a77)");
|
||||
//UPGRD//MMOItems.Log("\u00a76 |\u00a7b|\u00a76>\u00a77 Gemstone Level: \u00a7e" + gLevel + "\u00a77 (Put at \u00a7b" + level + "\u00a77)");
|
||||
|
||||
//UPGRD//MMOItems. Log("\u00a76 \u00a7b|>\u00a77 Gemstone Base: \u00a7e" + ((DoubleData) getGemstoneData(d)).getValue());
|
||||
// Apply upgrades
|
||||
@ -282,7 +357,7 @@ public class StatHistory<S extends StatData> {
|
||||
* <p>4: Sums external data (modifiers that are not linked to an ID, I suppose by external plugins).
|
||||
*/
|
||||
private S Recalculate_ThroughClone() {
|
||||
//UPGRD//MMOItems. Log("\u00a73|||\u00a77 Calculating as Clonium");
|
||||
//UPGRD//MMOItems.Log("\u00a73|||\u00a77 Calculating \u00a7f" + getItemStat().getNBTPath() + "\u00a77 as Clonium");
|
||||
|
||||
// Just clone bro
|
||||
S ret = (S) ((Mergeable) getOriginalData()).cloneData();
|
||||
@ -528,6 +603,32 @@ public class StatHistory<S extends StatData> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void Assimilate(@NotNull StatHistory<StatData> 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.perGemstoneData.keySet()) { registerGemstoneData(exUID, (S) other.getGemstoneData(exUID)); }
|
||||
|
||||
// Register externals
|
||||
for (StatData ex : other.perExternalData) { registerExternalData((S) ex); }
|
||||
|
||||
//UPDT//MMOItems.Log(" \u00a76:\u00a72: \u00a77Final Gemstones \u00a7f" + perGemstoneData.size());
|
||||
//UPDT//MMOItems.Log(" \u00a76:\u00a72: \u00a77Final Externals \u00a7f" + perExternalData.size());
|
||||
}
|
||||
}
|
||||
|
||||
static final String enc_Stat = "Stat";
|
||||
static final String enc_OGS = "OGStory";
|
||||
static final String enc_GSS = "Gemstory";
|
||||
|
Loading…
Reference in New Issue
Block a user