diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/ListenerManager.java b/src/main/java/studio/magemonkey/divinity/manager/listener/ListenerManager.java index dbb20fe1..4f268771 100644 --- a/src/main/java/studio/magemonkey/divinity/manager/listener/ListenerManager.java +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/ListenerManager.java @@ -18,6 +18,7 @@ public class ListenerManager implements Loadable { private ItemRequirementListener lisReq; private DynamicStatListener lisDynamic; private ItemUpdaterListener updater; + private ItemAutoUpdateListener autoUpdater; private VanillaWrapperListener lisQuantum; private HookListener hookListener; private GrindstoneListener grindstoneListener; @@ -53,6 +54,9 @@ public void setup() { this.updater = new ItemUpdaterListener(this.plugin); this.updater.registerListeners(); + this.autoUpdater = new ItemAutoUpdateListener(this.plugin); + this.autoUpdater.registerListeners(); + this.hookListener = new HookListener(this.plugin); this.hookListener.registerListeners(); @@ -76,6 +80,10 @@ public void shutdown() { this.lisReq.unregisterListeners(); this.lisReq = null; } + if (this.autoUpdater != null) { + this.autoUpdater.unregisterListeners(); + this.autoUpdater = null; + } if (this.lisQuantum != null) { this.lisQuantum.unregisterListeners(); this.lisQuantum = null; diff --git a/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemAutoUpdateListener.java b/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemAutoUpdateListener.java new file mode 100644 index 00000000..8f654ca0 --- /dev/null +++ b/src/main/java/studio/magemonkey/divinity/manager/listener/object/ItemAutoUpdateListener.java @@ -0,0 +1,153 @@ +package studio.magemonkey.divinity.manager.listener.object; + +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerItemHeldEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.inventory.meta.PotionMeta; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import studio.magemonkey.codex.manager.IListener; +import studio.magemonkey.divinity.Divinity; +import studio.magemonkey.divinity.modules.LeveledItem; +import studio.magemonkey.divinity.modules.ModuleItem; +import studio.magemonkey.divinity.modules.api.QModuleDrop; +import studio.magemonkey.divinity.stats.items.ItemStats; + +import java.util.HashSet; + +public class ItemAutoUpdateListener extends IListener { + + public ItemAutoUpdateListener(@NotNull Divinity plugin) { + super(plugin); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onItemHeld(PlayerItemHeldEvent event) { + Player player = event.getPlayer(); + ItemStack item = player.getInventory().getItem(event.getNewSlot()); + ItemStack updated = updateItem(item); + if (updated != null) { + player.getInventory().setItem(event.getNewSlot(), updated); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerJoin(PlayerJoinEvent event) { + updateInventory(event.getPlayer()); + } + + private void updateInventory(@NotNull Player player) { + PlayerInventory inv = player.getInventory(); + ItemStack[] contents = inv.getContents(); + for (int i = 0; i < contents.length; i++) { + ItemStack updated = updateItem(contents[i]); + if (updated != null) { + inv.setItem(i, updated); + } + } + } + + @Nullable + private ItemStack updateItem(@Nullable ItemStack item) { + if (item == null || item.getType() == Material.AIR) return null; + + QModuleDrop module = ItemStats.getModule(item); + if (module == null || !module.isAutoUpdate()) return null; + + String itemId = ItemStats.getId(item); + if (itemId == null) return null; + + ModuleItem moduleItem = module.getItemById(itemId); + if (moduleItem == null) return null; + + ItemStack updated = moduleItem.update(item); + if (module.isAutoUpdateOverrideChanges()) { + return updated; + } + + ItemStack template = createTemplate(moduleItem, item); + if (!hasPlayerItemChanges(item, template)) { + return updated; + } + + return reapplyPlayerChanges(updated, item); + } + + @NotNull + private ItemStack createTemplate(@NotNull ModuleItem moduleItem, @NotNull ItemStack sourceItem) { + if (moduleItem instanceof LeveledItem) { + return ((LeveledItem) moduleItem).create(ItemStats.getLevel(sourceItem)); + } + return moduleItem.create(); + } + + /** + * Detects whether the source item differs from a clean module template in player-modifiable fields: + * display name, lore, enchantments, item flags, item damage and armor/potion color. + */ + private boolean hasPlayerItemChanges(@NotNull ItemStack source, @NotNull ItemStack template) { + if (!source.getEnchantments().equals(template.getEnchantments())) return true; + + ItemMeta sourceMeta = source.getItemMeta(); + ItemMeta templateMeta = template.getItemMeta(); + if (sourceMeta == null || templateMeta == null) return sourceMeta != templateMeta; + + if (sourceMeta.hasDisplayName() != templateMeta.hasDisplayName()) return true; + if (sourceMeta.hasDisplayName() && !sourceMeta.getDisplayName().equals(templateMeta.getDisplayName())) return true; + + if (sourceMeta instanceof Damageable && templateMeta instanceof Damageable) { + if (((Damageable) sourceMeta).getDamage() != ((Damageable) templateMeta).getDamage()) return true; + } + + if (sourceMeta instanceof LeatherArmorMeta && templateMeta instanceof LeatherArmorMeta) { + if (!((LeatherArmorMeta) sourceMeta).getColor().equals(((LeatherArmorMeta) templateMeta).getColor())) return true; + } + + if (sourceMeta instanceof PotionMeta && templateMeta instanceof PotionMeta) { + Color sourceColor = ((PotionMeta) sourceMeta).getColor(); + Color templateColor = ((PotionMeta) templateMeta).getColor(); + if (sourceColor == null ? templateColor != null : !sourceColor.equals(templateColor)) return true; + } + + return false; + } + + @NotNull + private ItemStack reapplyPlayerChanges(@NotNull ItemStack updated, @NotNull ItemStack source) { + ItemMeta sourceMeta = source.getItemMeta(); + ItemMeta updatedMeta = updated.getItemMeta(); + if (sourceMeta == null || updatedMeta == null) return updated; + + if (sourceMeta.hasDisplayName()) { + updatedMeta.setDisplayName(sourceMeta.getDisplayName()); + } + + if (sourceMeta instanceof Damageable && updatedMeta instanceof Damageable) { + ((Damageable) updatedMeta).setDamage(((Damageable) sourceMeta).getDamage()); + } + + if (sourceMeta instanceof LeatherArmorMeta && updatedMeta instanceof LeatherArmorMeta) { + ((LeatherArmorMeta) updatedMeta).setColor(((LeatherArmorMeta) sourceMeta).getColor()); + } + + if (sourceMeta instanceof PotionMeta && updatedMeta instanceof PotionMeta) { + ((PotionMeta) updatedMeta).setColor(((PotionMeta) sourceMeta).getColor()); + } + + updated.setItemMeta(updatedMeta); + new HashSet<>(updated.getEnchantments().keySet()).forEach(updated::removeEnchantment); + source.getEnchantments().forEach((enchantment, level) -> updated.addUnsafeEnchantment(enchantment, level)); + + return updated; + } +} diff --git a/src/main/java/studio/magemonkey/divinity/modules/LeveledItem.java b/src/main/java/studio/magemonkey/divinity/modules/LeveledItem.java index 50782182..6bf4980f 100644 --- a/src/main/java/studio/magemonkey/divinity/modules/LeveledItem.java +++ b/src/main/java/studio/magemonkey/divinity/modules/LeveledItem.java @@ -132,6 +132,14 @@ public final ItemStack create() { return this.create(-1); } + @NotNull + @Override + public ItemStack update(@NotNull ItemStack item) { + ItemStack updated = this.build(item.clone(), ItemStats.getLevel(item)); + updated.setAmount(item.getAmount()); + return updated; + } + private void updateConfig(@NotNull JYML cfg) { cfg.addMissing("tier", JStrings.DEFAULT); diff --git a/src/main/java/studio/magemonkey/divinity/modules/ModuleItem.java b/src/main/java/studio/magemonkey/divinity/modules/ModuleItem.java index b40d17e1..99cc082c 100644 --- a/src/main/java/studio/magemonkey/divinity/modules/ModuleItem.java +++ b/src/main/java/studio/magemonkey/divinity/modules/ModuleItem.java @@ -214,6 +214,13 @@ public ItemStack create() { return this.build(); } + @NotNull + public ItemStack update(@NotNull ItemStack item) { + ItemStack updated = this.build(item.clone()); + updated.setAmount(item.getAmount()); + return updated; + } + @NotNull protected ItemStack build() { return build(this.getMaterial().create()); @@ -275,6 +282,13 @@ protected ItemStack build(@NotNull ItemStack item) { meta.addEnchant(NamespaceResolver.getEnchantment("POWER", "ARROW_DAMAGE"), 1, true); // ARROW_DAMAGE/POWER } + if (meta.hasAttributeModifiers()) { + Set existing = new HashSet<>(meta.getAttributeModifiers().keySet()); + for (Attribute attr : existing) { + meta.removeAttributeModifier(attr); + } + } + for (Map.Entry attribute : this.attributes.entrySet()) { if (attribute != null) { meta.addAttributeModifier(attribute.getKey(), attribute.getValue()); diff --git a/src/main/java/studio/magemonkey/divinity/modules/api/QModuleDrop.java b/src/main/java/studio/magemonkey/divinity/modules/api/QModuleDrop.java index 73cba2eb..e3add36c 100644 --- a/src/main/java/studio/magemonkey/divinity/modules/api/QModuleDrop.java +++ b/src/main/java/studio/magemonkey/divinity/modules/api/QModuleDrop.java @@ -42,6 +42,8 @@ public abstract class QModuleDrop extends QModule { private String itemNameFormat; private List itemLoreFormat; + private boolean autoUpdate; + private boolean autoUpdateOverrideChanges; public QModuleDrop(@NotNull Divinity plugin, @NotNull Class clazz) { super(plugin); @@ -61,10 +63,14 @@ protected void loadSettings() { String path = "item-format."; cfg.addMissing(path + "name", ItemTags.PLACEHOLDER_ITEM_NAME); cfg.addMissing(path + "lore", Arrays.asList(ItemTags.PLACEHOLDER_ITEM_LORE)); + cfg.addMissing("auto-update", false); + cfg.addMissing("auto-update-override-changes", false); cfg.saveChanges(); this.itemNameFormat = StringUT.color(cfg.getString(path + "name", ItemTags.PLACEHOLDER_ITEM_NAME)); this.itemLoreFormat = StringUT.color(cfg.getStringList(path + "lore")); + this.autoUpdate = cfg.getBoolean("auto-update"); + this.autoUpdateOverrideChanges = cfg.getBoolean("auto-update-override-changes"); } protected void loadItems() { @@ -107,6 +113,14 @@ public List getItemLoreFormat() { return this.itemLoreFormat; } + public boolean isAutoUpdate() { + return this.autoUpdate; + } + + public boolean isAutoUpdateOverrideChanges() { + return this.autoUpdateOverrideChanges; + } + @Nullable public I getItemById(@NotNull String id, @Nullable String sTier) { if (this.items == null || this.items.isEmpty()) return null; diff --git a/src/main/resources/modules/active_items/settings.yml b/src/main/resources/modules/active_items/settings.yml index bc27cd2b..2e0a7e80 100644 --- a/src/main/resources/modules/active_items/settings.yml +++ b/src/main/resources/modules/active_items/settings.yml @@ -1,4 +1,6 @@ command-aliases: activeitems +auto-update: false +auto-update-override-changes: false item-format: name: '%TIER_COLOR%%ITEM_NAME% %ITEM_LEVEL_ROMAN%' @@ -9,4 +11,4 @@ item-format: - '%USER_LEVEL%' - '%USER_CLASS%' - '' - - '%ITEM_LORE%' \ No newline at end of file + - '%ITEM_LORE%' diff --git a/src/main/resources/modules/arrows/settings.yml b/src/main/resources/modules/arrows/settings.yml index 0ee879b1..c1818fb7 100644 --- a/src/main/resources/modules/arrows/settings.yml +++ b/src/main/resources/modules/arrows/settings.yml @@ -1,4 +1,6 @@ command-aliases: arrows +auto-update: false +auto-update-override-changes: false settings: allow-infinity-enchant: false diff --git a/src/main/resources/modules/consumables/settings.yml b/src/main/resources/modules/consumables/settings.yml index 827ed926..09bb2407 100644 --- a/src/main/resources/modules/consumables/settings.yml +++ b/src/main/resources/modules/consumables/settings.yml @@ -1,4 +1,6 @@ command-aliases: consumables,consumes +auto-update: false +auto-update-override-changes: false consuming: allow-on-full-health: true allow-on-full-food: true diff --git a/src/main/resources/modules/custom_items/settings.yml b/src/main/resources/modules/custom_items/settings.yml index e78ad83d..31e9875a 100644 --- a/src/main/resources/modules/custom_items/settings.yml +++ b/src/main/resources/modules/custom_items/settings.yml @@ -1,4 +1,6 @@ command-aliases: customitems +auto-update: false +auto-update-override-changes: false item-format: name: '%ITEM_NAME%' lore: diff --git a/src/main/resources/modules/dismantle/settings.yml b/src/main/resources/modules/dismantle/settings.yml index 55857d47..dd97696c 100644 --- a/src/main/resources/modules/dismantle/settings.yml +++ b/src/main/resources/modules/dismantle/settings.yml @@ -1,4 +1,6 @@ command-aliases: dismantle +auto-update: false +auto-update-override-changes: false general: actions-complete: default: diff --git a/src/main/resources/modules/essences/settings.yml b/src/main/resources/modules/essences/settings.yml index c7f82bdc..3dc8205a 100644 --- a/src/main/resources/modules/essences/settings.yml +++ b/src/main/resources/modules/essences/settings.yml @@ -1,5 +1,7 @@ cfg_version: 1.0 command-aliases: essence,essences +auto-update: false +auto-update-override-changes: false socketing: allow-duplicated-items: true animated-bar: @@ -100,4 +102,4 @@ gui: name: '&c&l&nCancel' lore: [ ] slots: '0' - type: EXIT \ No newline at end of file + type: EXIT diff --git a/src/main/resources/modules/extractor/settings.yml b/src/main/resources/modules/extractor/settings.yml index ee04205d..f7ff1c7e 100644 --- a/src/main/resources/modules/extractor/settings.yml +++ b/src/main/resources/modules/extractor/settings.yml @@ -1,4 +1,6 @@ command-aliases: extractor +auto-update: false +auto-update-override-changes: false extraction: price: GEM: diff --git a/src/main/resources/modules/fortify/settings.yml b/src/main/resources/modules/fortify/settings.yml index ea5b4d2b..75247948 100644 --- a/src/main/resources/modules/fortify/settings.yml +++ b/src/main/resources/modules/fortify/settings.yml @@ -1,4 +1,6 @@ command-aliases: fortify +auto-update: false +auto-update-override-changes: false format: item-name: as-prefix: false diff --git a/src/main/resources/modules/gems/settings.yml b/src/main/resources/modules/gems/settings.yml index 16d60a97..095df4ed 100644 --- a/src/main/resources/modules/gems/settings.yml +++ b/src/main/resources/modules/gems/settings.yml @@ -1,4 +1,6 @@ command-aliases: gems +auto-update: false +auto-update-override-changes: false socketing: allow-duplicated-items: true diff --git a/src/main/resources/modules/identify/settings.yml b/src/main/resources/modules/identify/settings.yml index 277701ff..f665f981 100644 --- a/src/main/resources/modules/identify/settings.yml +++ b/src/main/resources/modules/identify/settings.yml @@ -1,4 +1,6 @@ command-aliases: identify,identifying +auto-update: false +auto-update-override-changes: false general: actions-complete: default: diff --git a/src/main/resources/modules/item_generator/settings.yml b/src/main/resources/modules/item_generator/settings.yml index d7f23fcd..a9295990 100644 --- a/src/main/resources/modules/item_generator/settings.yml +++ b/src/main/resources/modules/item_generator/settings.yml @@ -1,5 +1,7 @@ command-aliases: itemgenerator,itemgen +auto-update: false +auto-update-override-changes: false item-format: name: '%TIER_COLOR%%ITEM_NAME%' lore: - - '%ITEM_LORE%' \ No newline at end of file + - '%ITEM_LORE%' diff --git a/src/main/resources/modules/magic_dust/settings.yml b/src/main/resources/modules/magic_dust/settings.yml index 737c7d8f..ed915c9e 100644 --- a/src/main/resources/modules/magic_dust/settings.yml +++ b/src/main/resources/modules/magic_dust/settings.yml @@ -1,4 +1,6 @@ command-aliases: magicdust +auto-update: false +auto-update-override-changes: false general: actions-apply: default: diff --git a/src/main/resources/modules/money/settings.yml b/src/main/resources/modules/money/settings.yml index c0c27296..e5f94508 100644 --- a/src/main/resources/modules/money/settings.yml +++ b/src/main/resources/modules/money/settings.yml @@ -1,4 +1,6 @@ command-aliases: moneymodule +auto-update: false +auto-update-override-changes: false item-format: name: '%ITEM_NAME%' diff --git a/src/main/resources/modules/refine/settings.yml b/src/main/resources/modules/refine/settings.yml index f3e7cc20..52f8a5a4 100644 --- a/src/main/resources/modules/refine/settings.yml +++ b/src/main/resources/modules/refine/settings.yml @@ -1,4 +1,6 @@ command-aliases: refine +auto-update: false +auto-update-override-changes: false refine: actions-on-success: default: diff --git a/src/main/resources/modules/repair/settings.yml b/src/main/resources/modules/repair/settings.yml index adbc861b..64cd5d54 100644 --- a/src/main/resources/modules/repair/settings.yml +++ b/src/main/resources/modules/repair/settings.yml @@ -1,4 +1,6 @@ command-aliases: rpgrepair +auto-update: false +auto-update-override-changes: false repair: actions-complete: default: diff --git a/src/main/resources/modules/runes/settings.yml b/src/main/resources/modules/runes/settings.yml index b144e7d2..ea6afc9e 100644 --- a/src/main/resources/modules/runes/settings.yml +++ b/src/main/resources/modules/runes/settings.yml @@ -1,4 +1,6 @@ command-aliases: runes +auto-update: false +auto-update-override-changes: false general: stack-levels: false socketing: