diff --git a/src/main/java/net/Indyuce/mmoitems/ItemStats.java b/src/main/java/net/Indyuce/mmoitems/ItemStats.java index 1d28a54d..4f1cf009 100644 --- a/src/main/java/net/Indyuce/mmoitems/ItemStats.java +++ b/src/main/java/net/Indyuce/mmoitems/ItemStats.java @@ -170,6 +170,9 @@ public class ItemStats { // Abilities & Upgrading ABILITIES = new Abilities(), UPGRADE = new UpgradeStat(), + DOWNGRADE_ON_BREAK = new BooleanStat("BREAK_DOWNGRADE", Material.DAMAGED_ANVIL, "Downgrade when Broken", new String[]{"If this item's durability reaches 0,", "it will be fully repaired but also", "downgraded by one level.", "", "&cIt will only break if it cannot be", "&cdowngraded further", "", "Requires to define an &6Upgrade Template", "Required to define &6Custom Durability"}, new String[] { "piercing", "slashing", "blunt", "offhand", "range", "tool", "armor", "consumable", "accessory" }), + DOWNGRADE_ON_DEATH = new BooleanStat("DEATH_DOWNGRADE", Material.DAMAGED_ANVIL, "Downgrade on Death", new String[]{"If the wearer of this item dies, it may", "downgrade (based on &6Death Downgrade", "&6Chance &7stat)", "", "Required to define an &6Upgrade Template", "Requires keep-inventory gamerule. "}, new String[] { "piercing", "slashing", "blunt", "offhand", "range", "tool", "armor", "consumable", "accessory" }), + DOWNGRADE_ON_DEATH_CHANCE = new EvilDoubleStat("DEATH_DOWNGRADE_CHANCE", Material.SKELETON_SKULL, "Death Downgrade Chance", new String[]{"Probability that an item with &cDowngrade ", "&con Death&7 will be downgraded when the", "player dies. ", "", "Exceeding 100% will for sure downgrade", "one item, and roll again to downgrade", "another (with the excess probability).", "&6The same item wont be downgraded twice."}, new String[]{"!miscellaneous", "!block", "all"}), // Unique Item Stats SKULL_TEXTURE = new SkullTextureStat(), @@ -185,13 +188,13 @@ public class ItemStats { CUSTOM_DURABILITY = new CustomDurability(), STORED_TAGS = new StoredTags(), ITEM_LEVEL = new ItemLevel(), - INTERNAL_REVISION_ID = new InternalRevisionID(); + INTERNAL_REVISION_ID = new InternalRevisionID(), + BROWSER_DISPLAY_IDX = new BrowserDisplayIDX(); /** * @deprecated Item damage is now {@link ItemDamage} and * custom durability is now {@link CustomDurability} */ - @Deprecated - public static final ItemStat DURABILITY = ITEM_DAMAGE; + @Deprecated public static final ItemStat DURABILITY = ITEM_DAMAGE; } diff --git a/src/main/java/net/Indyuce/mmoitems/MMOItems.java b/src/main/java/net/Indyuce/mmoitems/MMOItems.java index d4a6d9ef..34a4c44c 100644 --- a/src/main/java/net/Indyuce/mmoitems/MMOItems.java +++ b/src/main/java/net/Indyuce/mmoitems/MMOItems.java @@ -99,9 +99,7 @@ public class MMOItems extends LuminePlugin { private static final int MYTHICLIB_COMPATIBILITY_INDEX = 4; - public MMOItems() { - plugin = this; - } + public MMOItems() { plugin = this; } @Override public void load() { diff --git a/src/main/java/net/Indyuce/mmoitems/ability/VectorAbility.java b/src/main/java/net/Indyuce/mmoitems/ability/VectorAbility.java index 9c7e457e..2d930792 100644 --- a/src/main/java/net/Indyuce/mmoitems/ability/VectorAbility.java +++ b/src/main/java/net/Indyuce/mmoitems/ability/VectorAbility.java @@ -6,7 +6,8 @@ import net.Indyuce.mmoitems.stat.data.AbilityData; import org.bukkit.entity.LivingEntity; /** - * Ability that requires a direction to be cast + * Ability that requires a direction to be cast. For + * instance, a projectile like {@link Firebolt} * * @deprecated Abilities were moved over to MythicLib. * Abilities are being replaced by {@link io.lumine.mythic.lib.skill.handler.SkillHandler} diff --git a/src/main/java/net/Indyuce/mmoitems/api/Type.java b/src/main/java/net/Indyuce/mmoitems/api/Type.java index 2535058c..1f62a035 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/Type.java +++ b/src/main/java/net/Indyuce/mmoitems/api/Type.java @@ -212,6 +212,19 @@ public class Type { return parent; } + /** + * @return The parentmost parent of this type, or itself, if it has no parent. + */ + public Type getSupertype() { + Type parentMost = this; + + while (parentMost.isSubtype()) { + parentMost = parentMost.getParent(); + } + + return parentMost; + } + /** * @return Either if the two types are the same, * or if this type is a subtype of the given type. diff --git a/src/main/java/net/Indyuce/mmoitems/api/UpgradeTemplate.java b/src/main/java/net/Indyuce/mmoitems/api/UpgradeTemplate.java index 26e9151c..8b74f25b 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/UpgradeTemplate.java +++ b/src/main/java/net/Indyuce/mmoitems/api/UpgradeTemplate.java @@ -122,7 +122,7 @@ public class UpgradeTemplate { UpgradeData dat; if (mmoitem.hasData(ItemStats.UPGRADE)) { dat = (UpgradeData) mmoitem.getData(ItemStats.UPGRADE); - } else { dat = new UpgradeData(null, null, false, false, 0, 100); } + } else { dat = new UpgradeData(null, null, false, false, 0, 0, 100); } dat.setLevel(level); mmoitem.setData(ItemStats.UPGRADE, dat); //UPGR//MMOItems.log("\u00a76>\u00a73>\u00a7a> \u00a77Upgrading to level \u00a7e" + dat.getLevel()); diff --git a/src/main/java/net/Indyuce/mmoitems/api/interaction/ItemSkin.java b/src/main/java/net/Indyuce/mmoitems/api/interaction/ItemSkin.java index ee1a5f74..e389f760 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/interaction/ItemSkin.java +++ b/src/main/java/net/Indyuce/mmoitems/api/interaction/ItemSkin.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; +import java.util.List; public class ItemSkin extends UseItem { public ItemSkin(Player player, NBTItem item) { @@ -41,15 +42,25 @@ public class ItemSkin extends UseItem { } boolean compatible = false; + + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a77 Applying onto " + MMOUtils.getDisplayName(target.getItem())); + if (getMMOItem().hasData(ItemStats.COMPATIBLE_TYPES)) { - for (String type : ((StringListData) getMMOItem().getData(ItemStats.COMPATIBLE_TYPES)).getList()) { + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a77 Testing that TYPE is compatible: "); + + List acceptedTypes = ((StringListData) getMMOItem().getData(ItemStats.COMPATIBLE_TYPES)).getList(); + + for (String type : acceptedTypes) { + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a7e >\u00a7f " + type); + if (type.equalsIgnoreCase(targetType.getId())) { - compatible = true; - break; - } + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a7a Matched"); + compatible = true; break; } } - if (!compatible) { + if (!compatible && acceptedTypes.size() > 0) { + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a7c Incompatible"); + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 2); Message.SKIN_INCOMPATIBLE.format(ChatColor.RED, "#item#", MMOUtils.getDisplayName(target.getItem())) .send(player); @@ -58,14 +69,21 @@ public class ItemSkin extends UseItem { } if (getMMOItem().hasData(ItemStats.COMPATIBLE_IDS)) { - for (String id : ((StringListData) getMMOItem().getData(ItemStats.COMPATIBLE_IDS)).getList()) { + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a77 Testing that ID is compatible: "); + + List acceptedIDs = ((StringListData) getMMOItem().getData(ItemStats.COMPATIBLE_IDS)).getList(); + + for (String id : acceptedIDs) { + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a76 >\u00a7f " + id); + if (id.equalsIgnoreCase(target.getString("MMOITEMS_ITEM_ID"))) { - compatible = true; - break; - } + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a7a Matched"); + compatible = true;break; } } - if (!compatible) { + if (!compatible && acceptedIDs.size() > 0) { + //SKIN//MMOItems.log("\u00a78SKIN \u00a7eCPT\u00a7c Incompatible"); + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 2); Message.SKIN_INCOMPATIBLE.format(ChatColor.RED, "#item#", MMOUtils.getDisplayName(target.getItem())) .send(player); diff --git a/src/main/java/net/Indyuce/mmoitems/api/interaction/util/DurabilityItem.java b/src/main/java/net/Indyuce/mmoitems/api/interaction/util/DurabilityItem.java index 97aafbc7..b04b1752 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/interaction/util/DurabilityItem.java +++ b/src/main/java/net/Indyuce/mmoitems/api/interaction/util/DurabilityItem.java @@ -1,13 +1,20 @@ package net.Indyuce.mmoitems.api.interaction.util; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; import io.lumine.mythic.lib.MythicLib; 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.ItemStats; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.event.item.CustomDurabilityDamage; import net.Indyuce.mmoitems.api.event.item.CustomDurabilityRepair; +import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem; import net.Indyuce.mmoitems.api.item.util.LoreUpdate; import net.Indyuce.mmoitems.api.player.PlayerData; +import net.Indyuce.mmoitems.stat.data.DoubleData; +import net.Indyuce.mmoitems.stat.data.UpgradeData; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -73,7 +80,7 @@ public class DurabilityItem { 0; } - public Player getPlayer() { + @Nullable public Player getPlayer() { return player; } @@ -113,6 +120,53 @@ public class DurabilityItem { public boolean isLostWhenBroken() { return nbtItem.getBoolean("MMOITEMS_WILL_BREAK"); } + public boolean isDowngradedWhenBroken() { + return nbtItem.getBoolean("MMOITEMS_BREAK_DOWNGRADE"); + } + + /** + * Assuming you already called {@link #isDowngradedWhenBroken()}, when the item is + * being broken. This method will downgrade the item for one level and apply changes to + * the stored {@link #getNBTItem()}. + * + * If the item cannot be downgraded (due to not having upgrade data / reaching minimum + * upgrades), this method will return null, no change will be made to the + * NBTItem, and you should break the item. + * + * @return If the item could not be downgraded, and thus should break, will be null. + * If the item was successfully downgraded, this will uuuh, will return the NBTItem of + * the downgraded version. + */ + @Nullable public ItemStack shouldBreakWhenDowngraded() { + ItemTag uTag = ItemTag.getTagAtPath(ItemStats.UPGRADE.getNBTPath(), getNBTItem(), SupportedNBTTagValues.STRING); + if (uTag == null) { return null; } + + try { + + // Read data + UpgradeData data = new UpgradeData(new JsonParser().parse((String) uTag.getValue()).getAsJsonObject()); + + // If it cannot be downgraded (reached min), DEATH + if (data.getLevel() <= data.getMin()) { return null; } + + // Downgrading operation + LiveMMOItem mmo = new LiveMMOItem(getNBTItem()); + + // Remove one level + mmo.getUpgradeTemplate().upgradeTo(mmo, data.getLevel() - 1); + + // Build NBT + NBTItem preRet = mmo.newBuilder().buildNBT(); + + // Set durability to zero (full repair) + DurabilityItem dur = new DurabilityItem(getPlayer(), preRet); + dur.addDurability(dur.getMaxDurability()); + + // Yes + return dur.toItem(); + + } catch (JsonSyntaxException |IllegalStateException exception) { return null; } + } /** * Since @@ -120,7 +174,7 @@ public class DurabilityItem { * @return If the item actually supports custom durability. */ public boolean isValid() { - return maxDurability > 0 && player.getGameMode() != GameMode.CREATIVE; + return maxDurability > 0 && player != null && player.getGameMode() != GameMode.CREATIVE; } public DurabilityItem addDurability(int gain) { @@ -180,8 +234,7 @@ public class DurabilityItem { if (!barHidden) { int damage = (durability == maxDurability) ? 0 : Math.max(1, (int) ((1. - ((double) durability / maxDurability)) * nbtItem.getItem().getType().getMaxDurability())); - nbtItem.addTag(new ItemTag("Damage", damage)); - } + nbtItem.addTag(new ItemTag("Damage", damage)); } nbtItem.addTag(new ItemTag("MMOITEMS_DURABILITY", durability)); @@ -189,9 +242,9 @@ public class DurabilityItem { ItemStack item = nbtItem.toItem(); // Item lore update - String format = MythicLib.inst().parseColors(MMOItems.plugin.getLanguage().getStatFormat("durability").replace("#m", "" + maxDurability)); - String old = format.replace("#c", "" + initialDurability); - String replaced = format.replace("#c", "" + durability); + String format = MythicLib.inst().parseColors(MMOItems.plugin.getLanguage().getStatFormat("durability").replace("#m", String.valueOf(maxDurability))); + String old = format.replace("#c", String.valueOf(initialDurability)); + String replaced = format.replace("#c", String.valueOf(durability)); return new LoreUpdate(item, old, replaced).updateLore(); } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/interaction/util/UntargetedDurabilityItem.java b/src/main/java/net/Indyuce/mmoitems/api/interaction/util/UntargetedDurabilityItem.java index fac89e37..0f512a4b 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/interaction/util/UntargetedDurabilityItem.java +++ b/src/main/java/net/Indyuce/mmoitems/api/interaction/util/UntargetedDurabilityItem.java @@ -1,9 +1,16 @@ package net.Indyuce.mmoitems.api.interaction.util; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import io.lumine.mythic.lib.api.item.ItemTag; +import io.lumine.mythic.lib.api.item.SupportedNBTTagValues; import io.lumine.mythic.lib.api.player.EquipmentSlot; +import net.Indyuce.mmoitems.ItemStats; +import net.Indyuce.mmoitems.stat.data.UpgradeData; import org.bukkit.entity.Player; import io.lumine.mythic.lib.api.item.NBTItem; +import org.bukkit.inventory.ItemStack; public class UntargetedDurabilityItem extends DurabilityItem { private final EquipmentSlot slot; @@ -26,12 +33,38 @@ public class UntargetedDurabilityItem extends DurabilityItem { public void update() { - if (isBroken() && isLostWhenBroken()) { - if (slot == EquipmentSlot.OFF_HAND) - getPlayer().getInventory().setItemInOffHand(null); - else - getPlayer().getInventory().setItemInMainHand(null); - return; + // Cannot update null player + if (getPlayer() == null) { return; } + + // If it broke (funny) + if (isBroken()) { + + // Attempt to counter by downgrading + if (isDowngradedWhenBroken()) { + + ItemStack counterUpgraded = shouldBreakWhenDowngraded(); + if (counterUpgraded != null) { + + // Edit item + getNBTItem().getItem().setItemMeta(counterUpgraded.getItemMeta()); + + // No more + return; + } + } + + // Still here? Remove if lost when broken + if (isLostWhenBroken()) { + + // Delete item + if (slot == EquipmentSlot.OFF_HAND) { + getPlayer().getInventory().setItemInOffHand(null); + + } else { getPlayer().getInventory().setItemInMainHand(null); } + + // No more + return; + } } getNBTItem().getItem().setItemMeta(toItem().getItemMeta()); diff --git a/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EditableEquippedItem.java b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EditableEquippedItem.java new file mode 100644 index 00000000..5f9cd064 --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EditableEquippedItem.java @@ -0,0 +1,26 @@ +package net.Indyuce.mmoitems.api.player.inventory; + +import io.lumine.mythic.lib.api.item.NBTItem; +import io.lumine.mythic.lib.api.player.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Nullable; + +public abstract class EditableEquippedItem extends EquippedItem { + public EditableEquippedItem(ItemStack item, EquipmentSlot slot) { + super(item, slot); + } + + public EditableEquippedItem(NBTItem item, EquipmentSlot slot) { + super(item, slot); + } + + /** + * Allows editing the item, wherever it is that it is + * currently equipped, due to stats like + * {@link net.Indyuce.mmoitems.ItemStats#DOWNGRADE_ON_DEATH} + * that target equipped items. + * + * @param item Item to replace in the current slot + */ + public abstract void setItem(@Nullable ItemStack item); +} diff --git a/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedItem.java b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedItem.java index cd7829f4..8a2bf5af 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedItem.java +++ b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedItem.java @@ -18,7 +18,7 @@ public class EquippedItem { * @param slot The corresponding MMOItems slot type */ public EquippedItem(ItemStack item, EquipmentSlot slot) { - this(MythicLib.plugin.getVersion().getWrapper().getNBTItem(item), slot); + this(NBTItem.get(item), slot); } /** diff --git a/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedPlayerItem.java b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedPlayerItem.java index 39384381..80ac0c38 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedPlayerItem.java +++ b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/EquippedPlayerItem.java @@ -6,6 +6,7 @@ import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem; public class EquippedPlayerItem { private final VolatileMMOItem item; private final EquipmentSlot slot; + private final EquippedItem equipped; /** * An item equipped by a player in a specific slot @@ -13,15 +14,19 @@ public class EquippedPlayerItem { * @param item The item equipped */ public EquippedPlayerItem(EquippedItem item) { + this.equipped = item; this.item = new VolatileMMOItem(item.getItem()); this.slot = item.getSlot(); } - public VolatileMMOItem getItem() { - return item; - } + /** + * @return honestly I do not know why EquippedPlayerItem even exists? + * you can get all the values from the {@link EquippedItem} + * it came from. Its like a funny wrapper. + */ + public EquippedItem getEquipped() { return equipped; } - public EquipmentSlot getSlot() { - return slot; - } + public VolatileMMOItem getItem() { return item; } + + public EquipmentSlot getSlot() { return slot; } } diff --git a/src/main/java/net/Indyuce/mmoitems/api/player/inventory/InventoryUpdateHandler.java b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/InventoryUpdateHandler.java index 9ef7d854..a02dd20a 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/player/inventory/InventoryUpdateHandler.java +++ b/src/main/java/net/Indyuce/mmoitems/api/player/inventory/InventoryUpdateHandler.java @@ -30,9 +30,7 @@ public class InventoryUpdateHandler { /** * Used to handle player inventory updates. */ - public InventoryUpdateHandler(PlayerData player) { - this.player = player; - } + public InventoryUpdateHandler(PlayerData player) { this.player = player; } /** * @return All equipped MMOItems in the player's inventory. Also includes @@ -43,8 +41,7 @@ public class InventoryUpdateHandler { } public void updateCheck() { - if (!player.isOnline()) - return; + if (!player.isOnline()) { return; } PlayerInventory inv = player.getPlayer().getInventory(); if (isNotSame(helmet, inv.getHelmet()) || isNotSame(chestplate, inv.getChestplate()) || isNotSame(leggings, inv.getLeggings()) diff --git a/src/main/java/net/Indyuce/mmoitems/api/util/message/Message.java b/src/main/java/net/Indyuce/mmoitems/api/util/message/Message.java index d00ccfca..ecc357fd 100644 --- a/src/main/java/net/Indyuce/mmoitems/api/util/message/Message.java +++ b/src/main/java/net/Indyuce/mmoitems/api/util/message/Message.java @@ -63,6 +63,7 @@ public enum Message { UPGRADE_SUCCESS("You successfully upgraded your &6#item#&e!"), NOT_HAVE_ITEM_UPGRADE("You don't have the item to upgrade!"), UPGRADE_REQUIREMENT_SAFE_CHECK("You would not meet the upgraded item requirements."), + DEATH_DOWNGRADING("&cYour &6#item#&c got severely damaged that fight..."), // Crafting stations NOT_ENOUGH_MATERIALS("You do not have enough materials to craft this item."), diff --git a/src/main/java/net/Indyuce/mmoitems/comp/inventory/DefaultPlayerInventory.java b/src/main/java/net/Indyuce/mmoitems/comp/inventory/DefaultPlayerInventory.java index 0ddae914..a1739383 100644 --- a/src/main/java/net/Indyuce/mmoitems/comp/inventory/DefaultPlayerInventory.java +++ b/src/main/java/net/Indyuce/mmoitems/comp/inventory/DefaultPlayerInventory.java @@ -4,6 +4,7 @@ import io.lumine.mythic.lib.api.player.EquipmentSlot; import net.Indyuce.mmoitems.api.player.inventory.EquippedItem; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -14,19 +15,25 @@ import java.util.List; * Armor slots, mainhand and offhand. */ public class DefaultPlayerInventory implements PlayerInventory { + @Override + @NotNull public List getInventory(Player player) { List list = new ArrayList<>(); + if (player.getEquipment() == null) { return list; } + // Mainhand - list.add(new EquippedItem(player.getEquipment().getItemInMainHand(), EquipmentSlot.MAIN_HAND)); + list.add(new EIDefaultInventory(player, -7, player.getEquipment().getItemInMainHand(), EquipmentSlot.MAIN_HAND)); // Offhand - list.add(new EquippedItem(player.getEquipment().getItemInOffHand(), EquipmentSlot.OFF_HAND)); + list.add(new EIDefaultInventory(player, -106, player.getEquipment().getItemInOffHand(), EquipmentSlot.OFF_HAND)); - // Armour - for (ItemStack armor : player.getInventory().getArmorContents()) - list.add(new EquippedItem(armor, EquipmentSlot.ARMOR)); + // Armor + list.add(new EIDefaultInventory(player, 103, player.getEquipment().getHelmet(), EquipmentSlot.ARMOR)); + list.add(new EIDefaultInventory(player, 102, player.getEquipment().getChestplate(), EquipmentSlot.ARMOR)); + list.add(new EIDefaultInventory(player, 101, player.getEquipment().getLeggings(), EquipmentSlot.ARMOR)); + list.add(new EIDefaultInventory(player, 100, player.getEquipment().getBoots(), EquipmentSlot.ARMOR)); return list; } diff --git a/src/main/java/net/Indyuce/mmoitems/comp/inventory/EIDefaultInventory.java b/src/main/java/net/Indyuce/mmoitems/comp/inventory/EIDefaultInventory.java new file mode 100644 index 00000000..fee45cd5 --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/comp/inventory/EIDefaultInventory.java @@ -0,0 +1,57 @@ +package net.Indyuce.mmoitems.comp.inventory; + +import io.lumine.mythic.lib.api.item.NBTItem; +import io.lumine.mythic.lib.api.player.EquipmentSlot; +import net.Indyuce.mmoitems.api.player.inventory.EditableEquippedItem; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class EIDefaultInventory extends EditableEquippedItem { + @NotNull public Player getPlayer() { return player; } + @NotNull Player player; + + public int getSlotNumber() { return slotNumber; } + int slotNumber; + + public EIDefaultInventory(@NotNull Player player, int slotNumber, ItemStack item, EquipmentSlot slot) { + super(item, slot); + this.player = player; + this.slotNumber = slotNumber; + } + + public EIDefaultInventory(@NotNull Player player, int slotNumber, NBTItem item, EquipmentSlot slot) { + super(item, slot); + this.player = player; + this.slotNumber = slotNumber; + } + + @Override + public void setItem(@Nullable ItemStack item) { + + switch (getSlotNumber()) { + case -106: + getPlayer().getInventory().setItemInOffHand(item); + break; + case -7: + getPlayer().getInventory().setItemInMainHand(item); + break; + case 103: + getPlayer().getInventory().setHelmet(item); + break; + case 102: + getPlayer().getInventory().setChestplate(item); + break; + case 101: + getPlayer().getInventory().setLeggings(item); + break; + case 100: + getPlayer().getInventory().setBoots(item); + break; + default: + getPlayer().getInventory().setItem(getSlotNumber(), item); + break; + } + } +} diff --git a/src/main/java/net/Indyuce/mmoitems/gui/ItemBrowser.java b/src/main/java/net/Indyuce/mmoitems/gui/ItemBrowser.java index ffe39588..ee5c3a47 100644 --- a/src/main/java/net/Indyuce/mmoitems/gui/ItemBrowser.java +++ b/src/main/java/net/Indyuce/mmoitems/gui/ItemBrowser.java @@ -4,14 +4,15 @@ import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.util.AltChar; +import io.lumine.mythic.lib.api.util.ui.SilentNumbers; import io.lumine.mythic.lib.version.VersionMaterial; -import io.lumine.mythic.utils.adventure.text.Component; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOUtils; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.edition.NewItemEdition; import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; import net.Indyuce.mmoitems.gui.edition.ItemEdition; +import net.Indyuce.mmoitems.stat.BrowserDisplayIDX; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -23,6 +24,7 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; import java.util.*; @@ -47,106 +49,100 @@ public class ItemBrowser extends PluginInventory { } - @Override - public Inventory getInventory() { - int[] usedSlots = type != null && type.isFourGUIMode() ? slotsAlt : slots; - int min = (page - 1) * usedSlots.length; - int max = page * usedSlots.length; - int n = 0; + @NotNull @Override public Inventory getInventory() { /* - * Displays all possible item types if no - * type was previously selected by the player + * ------------------------------ + * TYPE BROWSER + * + * Displays all possible item types if no type was previously selected by the player. + * ------------------------------ */ if (type == null) { + + int[] usedSlots = slots; + int min = (page - 1) * usedSlots.length; + int max = page * usedSlots.length; + int n = 0; + + // Create inventory Inventory inv = Bukkit.createInventory(this, 54, "Item Explorer"); + + // Fetch the list of types List types = new ArrayList<>(MMOItems.plugin.getTypes().getAll()); for (int j = min; j < Math.min(max, types.size()); j++) { - Type type = types.get(j); - int items = MMOItems.plugin.getTemplates().getTemplates(type).size(); - ItemStack item = type.getItem(); + // Current type to display into the GUI + Type currentType = types.get(j); + + // Get number of items + int items = MMOItems.plugin.getTemplates().getTemplates(currentType).size(); + + // Display how many items are in the type + ItemStack item = currentType.getItem(); item.setAmount(Math.max(1, Math.min(64, items))); ItemMeta meta = item.getItemMeta(); - meta.setDisplayName(ChatColor.GREEN + type.getName() + ChatColor.DARK_GRAY + " (Click to browse)"); + meta.setDisplayName(ChatColor.GREEN + currentType.getName() + ChatColor.DARK_GRAY + " (Click to browse)"); meta.addItemFlags(ItemFlag.values()); List lore = new ArrayList<>(); - lore.add(ChatColor.GRAY + "" + ChatColor.ITALIC + "There " + (items != 1 ? "are" : "is") + " " - + (items < 1 ? "" + ChatColor.RED + ChatColor.ITALIC + "no" : "" + ChatColor.GOLD + ChatColor.ITALIC + items) + ChatColor.GRAY - + ChatColor.ITALIC + " item" + (items != 1 ? "s" : "") + " in that type."); + lore.add(String.valueOf(ChatColor.GRAY) + ChatColor.ITALIC + "There " + (items == 1 ? "is" : "are") + " " + + (items < 1 ? String.valueOf(ChatColor.RED) + ChatColor.ITALIC + "no" : String.valueOf(ChatColor.GOLD) + ChatColor.ITALIC + items) + ChatColor.GRAY + + ChatColor.ITALIC + " item" + (items == 1 ? "" : "s") + " in that currentType."); meta.setLore(lore); item.setItemMeta(meta); - inv.setItem(slots[n++], NBTItem.get(item).addTag(new ItemTag("typeId", type.getId())).toItem()); + // Set item + inv.setItem(slots[n++], NBTItem.get(item).addTag(new ItemTag("typeId", currentType.getId())).toItem()); } + // Fill remainder slots with 'No Type' notice ItemStack glass = VersionMaterial.GRAY_STAINED_GLASS_PANE.toItem(); ItemMeta glassMeta = glass.getItemMeta(); glassMeta.setDisplayName(ChatColor.RED + "- No type -"); glass.setItemMeta(glassMeta); + // Next Page ItemStack next = new ItemStack(Material.ARROW); ItemMeta nextMeta = next.getItemMeta(); nextMeta.setDisplayName(ChatColor.GREEN + "Next Page"); next.setItemMeta(nextMeta); + // Previous Page ItemStack previous = new ItemStack(Material.ARROW); ItemMeta previousMeta = previous.getItemMeta(); previousMeta.setDisplayName(ChatColor.GREEN + "Previous Page"); previous.setItemMeta(previousMeta); - while (n < slots.length) - inv.setItem(slots[n++], glass); + // Fill + while (n < slots.length) { inv.setItem(slots[n++], glass); } inv.setItem(18, page > 1 ? previous : null); inv.setItem(26, max >= MMOItems.plugin.getTypes().getAll().size() ? null : next); + // Done return inv; } + /* + * ------------------------------ + * ITEM BROWSER + * + * Displays all the items of the chosen Type + * ------------------------------ + */ + Inventory inv = Bukkit.createInventory(this, 54, (deleteMode ? ("Delete Mode: ") : ("Item Explorer: ")) + type.getName()); + + /* + * Build cool Item Stacks for buttons and sh + */ ItemStack error = VersionMaterial.RED_STAINED_GLASS_PANE.toItem(); ItemMeta errorMeta = error.getItemMeta(); errorMeta.setDisplayName(ChatColor.RED + "- Error -"); List errorLore = new ArrayList<>(); - errorLore.add(ChatColor.GRAY + "" + ChatColor.ITALIC + "An error occurred while"); - errorLore.add(ChatColor.GRAY + "" + ChatColor.ITALIC + "trying to generate that item."); + errorLore.add("\u00a7\u00a7oAn error occurred while"); + errorLore.add("\u00a7\u00a7otrying to generate that item."); errorMeta.setLore(errorLore); error.setItemMeta(errorMeta); - List templates = new ArrayList<>(MMOItems.plugin.getTemplates().getTemplates(type)); - - /* - * Displays every item in a specific type. Items are cached inside the - * map at the top to reduce performance impact and are directly rendered - */ - Inventory inv = Bukkit.createInventory(this, 54, (deleteMode ? ("Delete Mode: ") : ("Item Explorer: ")) + type.getName()); - for (int j = min; j < Math.min(max, templates.size()); j++) { - MMOItemTemplate template = templates.get(j); - ItemStack item = template.newBuilder(playerData.getRPG()).build().newBuilder().build(); - if (item == null || item.getType() == Material.AIR) { - cached.put(template.getId(), error); - inv.setItem(usedSlots[n++], error); - continue; - } - - ItemMeta meta = item.getItemMeta(); - List lore = meta.hasLore() ? meta.getLore() : new ArrayList<>(); - lore.add(""); - - if (deleteMode) { - lore.add(ChatColor.RED + AltChar.cross + " CLICK TO DELETE " + AltChar.cross); - meta.setDisplayName(ChatColor.RED + "DELETE: " + (meta.hasDisplayName() ? meta.getDisplayName() : MMOUtils.getDisplayName(item))); - } else { - lore.add(ChatColor.YELLOW + AltChar.smallListDash + " Left click to obtain this item."); - lore.add(ChatColor.YELLOW + AltChar.smallListDash + " Right click to edit this item."); - } - - meta.setLore(lore); - item.setItemMeta(meta); - cached.put(template.getId(), item); - - inv.setItem(usedSlots[n++], cached.get(template.getId())); - } - ItemStack noItem = VersionMaterial.GRAY_STAINED_GLASS_PANE.toItem(); ItemMeta noItemMeta = noItem.getItemMeta(); noItemMeta.setDisplayName(ChatColor.RED + "- No Item -"); @@ -185,17 +181,121 @@ public class ItemBrowser extends PluginInventory { ChatColor.RED + "By downloading the default resourcepack you can", ChatColor.RED + "edit the blocks however you want.", ChatColor.RED + "You will still have to add it to your server!")); downloadPack.setItemMeta(downloadMeta); - inv.setItem(45, downloadPack); + inv.setItem(45, downloadPack); } + + // Get templates of this type + HashMap> templates = BrowserDisplayIDX.select(MMOItems.plugin.getTemplates().getTemplates(type)); + + /* + * ----------- + * CALCULATE GUI BOUNDS AND PAGE SIZES + * + * Each display index claims the entire column of items, such that there will be + * empty spaces added to fill the inventories. + * + * In Four GUI mode, columns are four slots tall, else they are three slots tall. + * ----------- + */ + int[] usedSlots = type.isFourGUIMode() ? slotsAlt : slots; + int min = (page - 1) * usedSlots.length; + int max = page * usedSlots.length; + int n = 0; + + int sc = type.isFourGUIMode() ? 4 : 3; + int totalSpaceCount = 0; + + for (Map.Entry> indexTemplates : templates.entrySet()) { + + // Claim columns + int totalSpaceAdd = indexTemplates.getValue().size(); + while (totalSpaceAdd > 0) { totalSpaceCount += sc; totalSpaceAdd -= sc; } } + + /* + * Over the page-range currently in use... + */ + for (int j = min; j < Math.min(max, totalSpaceCount); j++) { + MMOItemTemplate template = BrowserDisplayIDX.getAt(j, templates); + + // No template here? + if (template == null) { + + // Set Item + inv.setItem(usedSlots[n], noItem); + + /* + * Calculate next n from the slots. + * + * #1 Adding 7 will give you the slot immediately under + * + * #2 If it overflows, subtract 7sc (column space * 7) + * and add one + */ + n += 7; + if (n >= usedSlots.length) { n -= 7 * sc; n++; } + continue; + } + + // Build item -> any errors? + ItemStack item = template.newBuilder(playerData.getRPG()).build().newBuilder().build(); + if (item == null || item.getType().isAir() || !item.getType().isItem() || item.getItemMeta() == null) { + + // Set Item + cached.put(template.getId(), error); + inv.setItem(usedSlots[n], error); + + /* + * Calculate next n from the slots. + * + * #1 Adding 7 will give you the slot immediately under + * + * #2 If it overflows, subtract 7sc (column space * 7) + * and add one + */ + n += 7; + if (n >= usedSlots.length) { n -= 7 * sc; n++; } + continue; } + + ItemMeta meta = item.getItemMeta(); + List lore = meta.getLore(); + if (lore == null) { lore = new ArrayList<>(); } + lore.add(""); + + // Deleting lore? + if (deleteMode) { + lore.add(ChatColor.RED + AltChar.cross + " CLICK TO DELETE " + AltChar.cross); + meta.setDisplayName(ChatColor.RED + "DELETE: " + (meta.hasDisplayName() ? meta.getDisplayName() : MMOUtils.getDisplayName(item))); + + // Editing lore? + } else { + lore.add(ChatColor.YELLOW + AltChar.smallListDash + " Left click to obtain this item."); + lore.add(ChatColor.YELLOW + AltChar.smallListDash + " Right click to edit this item."); } + + meta.setLore(lore); + item.setItemMeta(meta); + + // Set item + cached.put(template.getId(), item); + inv.setItem(usedSlots[n], cached.get(template.getId())); + + /* + * Calculate next n from the slots. + * + * #1 Adding 7 will give you the slot immediately under + * + * #2 If it overflows, subtract 7sc (column space * 7) + * and add one + */ + n += 7; + if (n >= usedSlots.length) { n -= 7 * sc; n++; } } - while (n < usedSlots.length) - inv.setItem(usedSlots[n++], noItem); - if (!deleteMode) - inv.setItem(51, create); + // Put the buttons + if (!deleteMode) { inv.setItem(51, create); } inv.setItem(47, delete); inv.setItem(49, back); inv.setItem(18, page > 1 ? previous : null); - inv.setItem(26, max >= templates.size() ? null : next); + inv.setItem(26, max >= totalSpaceCount ? null : next); + for (int i : usedSlots) { if (SilentNumbers.isAir(inv.getItem(i))) { inv.setItem(i, noItem); } } return inv; } diff --git a/src/main/java/net/Indyuce/mmoitems/gui/edition/UpgradingEdition.java b/src/main/java/net/Indyuce/mmoitems/gui/edition/UpgradingEdition.java index 2c69dc19..6ba866a8 100644 --- a/src/main/java/net/Indyuce/mmoitems/gui/edition/UpgradingEdition.java +++ b/src/main/java/net/Indyuce/mmoitems/gui/edition/UpgradingEdition.java @@ -84,6 +84,22 @@ public class UpgradingEdition extends EditionInventory { maxItemMeta.setLore(maxItemLore); maxItem.setItemMeta(maxItemMeta); inv.setItem(40, maxItem); + + int min = getEditedSection().getInt("upgrade.min", 0); + ItemStack minItem = new ItemStack(Material.BARRIER); + ItemMeta minItemMeta = minItem.getItemMeta(); + minItemMeta.setDisplayName(ChatColor.GREEN + "Min Upgrades"); + List minItemLore = new ArrayList<>(); + minItemLore.add(ChatColor.GRAY + "The minimum level your item can be"); + minItemLore.add(ChatColor.GRAY + "downgraded to (by dying or breaking)."); + minItemLore.add(""); + minItemLore.add(ChatColor.GRAY + "Current Value: " + (min == 0 ? ChatColor.RED + "0" : ChatColor.GOLD + String.valueOf(min))); + minItemLore.add(""); + minItemLore.add(ChatColor.YELLOW + AltChar.listDash + " Click to chance this value."); + minItemLore.add(ChatColor.YELLOW + AltChar.listDash + " Right click to reset."); + minItemMeta.setLore(minItemLore); + minItem.setItemMeta(minItemMeta); + inv.setItem(41, minItem); } else { inv.setItem(20, notAvailable); inv.setItem(22, notAvailable); @@ -182,6 +198,17 @@ public class UpgradingEdition extends EditionInventory { } } + if (item.getItemMeta().getDisplayName().equals(ChatColor.GREEN + "Min Upgrades")) { + if (event.getAction() == InventoryAction.PICKUP_ALL) + new StatEdition(this, ItemStats.UPGRADE, "min").enable("Write in the chat the number you want."); + + if (event.getAction() == InventoryAction.PICKUP_HALF && getEditedSection().contains("upgrade.min")) { + getEditedSection().set("upgrade.min", null); + registerTemplateEdition(); + player.sendMessage(MMOItems.plugin.getPrefix() + "Successfully reset the number of min level."); + } + } + if (item.getItemMeta().getDisplayName().equals(ChatColor.GREEN + "Upgrade Template")) { if (event.getAction() == InventoryAction.PICKUP_ALL) new StatEdition(this, ItemStats.UPGRADE, "template").enable("Write in the chat the upgrade template ID you want."); diff --git a/src/main/java/net/Indyuce/mmoitems/listener/DurabilityListener.java b/src/main/java/net/Indyuce/mmoitems/listener/DurabilityListener.java index 84f8fa8b..4c108857 100644 --- a/src/main/java/net/Indyuce/mmoitems/listener/DurabilityListener.java +++ b/src/main/java/net/Indyuce/mmoitems/listener/DurabilityListener.java @@ -76,9 +76,32 @@ public class DurabilityListener implements Listener { * If the item is broken and if it is meant to be lost when broken, * do NOT cancel the event and make sure the item is destroyed */ - if (item.isBroken() && item.isLostWhenBroken()) { - event.setDamage(999); - return; + if (item.isBroken()) { + + // Attempt to counter by downgrading + if (item.isDowngradedWhenBroken()) { + + ItemStack counterUpgraded = item.shouldBreakWhenDowngraded(); + if (counterUpgraded != null) { + + // Counter Event + event.setCancelled(true); + event.getItem().setItemMeta(counterUpgraded.getItemMeta()); + + // No more + return; + } + } + + // Still here? Remove if lost when broken + if (item.isLostWhenBroken()) { + + // Delete item + event.setDamage(999); + + // Allow event to proceed + return; + } } event.setCancelled(true); @@ -105,10 +128,32 @@ public class DurabilityListener implements Listener { if (item.isValid() && stack.getType().getMaxDurability() == 0) { item.decreaseDurability(damage); - if (item.isBroken() && item.isLostWhenBroken()) { - player.getInventory().setItem(slot, null); - player.getWorld().playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f); - return; + if (item.isBroken()) { + + // Attempt to counter by downgrading + if (item.isDowngradedWhenBroken()) { + + ItemStack counterUpgraded = item.shouldBreakWhenDowngraded(); + if (counterUpgraded != null) { + + // Edit item + player.getInventory().getItem(slot).setItemMeta(counterUpgraded.getItemMeta()); + + // No more + return; + } + } + + // Still here? Remove if lost when broken + if (item.isLostWhenBroken()) { + + // Delete item + player.getInventory().setItem(slot, null); + player.getWorld().playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f); + + // No more + return; + } } player.getInventory().getItem(slot).setItemMeta(item.toItem().getItemMeta()); diff --git a/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java b/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java index 86f9d60a..80fd0053 100644 --- a/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java +++ b/src/main/java/net/Indyuce/mmoitems/listener/PlayerListener.java @@ -1,27 +1,41 @@ package net.Indyuce.mmoitems.listener; +import com.google.gson.JsonParser; import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.api.event.skill.PlayerCastSkillEvent; import io.lumine.mythic.lib.api.item.NBTItem; import io.lumine.mythic.lib.api.player.EquipmentSlot; +import io.lumine.mythic.lib.api.util.ui.SilentNumbers; import io.lumine.mythic.lib.skill.trigger.TriggerType; import io.lumine.mythic.utils.Schedulers; import io.lumine.mythic.utils.events.extra.ArmorEquipEvent; +import net.Indyuce.mmoitems.ItemStats; import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOUtils; import net.Indyuce.mmoitems.api.SoulboundInfo; import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.event.AbilityUseEvent; +import net.Indyuce.mmoitems.api.interaction.util.DurabilityItem; import net.Indyuce.mmoitems.api.interaction.util.InteractItem; import net.Indyuce.mmoitems.api.interaction.weapon.Weapon; +import net.Indyuce.mmoitems.api.item.mmoitem.LiveMMOItem; +import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem; import net.Indyuce.mmoitems.api.player.PlayerData; +import net.Indyuce.mmoitems.api.player.inventory.EditableEquippedItem; +import net.Indyuce.mmoitems.api.player.inventory.EquippedItem; +import net.Indyuce.mmoitems.api.player.inventory.EquippedPlayerItem; +import net.Indyuce.mmoitems.api.util.message.Message; import net.Indyuce.mmoitems.skill.RegisteredSkill; import net.Indyuce.mmoitems.stat.data.AbilityData; +import net.Indyuce.mmoitems.stat.data.UpgradeData; +import net.Indyuce.mmoitems.stat.type.NameData; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Trident; +import org.bukkit.event.Cancellable; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -30,10 +44,7 @@ import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.event.player.*; import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.*; public class PlayerListener implements Listener { private final Map> deathItems = new HashMap<>(); @@ -41,22 +52,156 @@ public class PlayerListener implements Listener { @EventHandler(priority = EventPriority.NORMAL) public void loadPlayerData(PlayerJoinEvent event) { MMOItems.plugin.getRecipes().refreshRecipeBook(event.getPlayer()); - PlayerData.load(event.getPlayer()); - } + PlayerData.load(event.getPlayer()); } @EventHandler(priority = EventPriority.HIGH) - public void savePlayerData(PlayerQuitEvent event) { - PlayerData.get(event.getPlayer()).save(); + public void savePlayerData(PlayerQuitEvent event) { PlayerData.get(event.getPlayer()).save(); } + + + public static boolean ChanceSuccess(int percentChance) { + Random rand = new Random(); + return rand.nextInt(100) <= percentChance; } - /* + /** + * If the player dies, its time to roll the death-downgrade stat! + */ + @EventHandler(priority = EventPriority.MONITOR) + public void onDeathForUpgradeLoss(PlayerDeathEvent event) { + + // No + if (event instanceof Cancellable) { if (((Cancellable) event).isCancelled()) { return; } } + + // Get Player + PlayerData data = PlayerData.get(event.getEntity()); + + // Get total downgrade chance, anything less than zero is invalid + double deathChance = data.getStats().getStat(ItemStats.DOWNGRADE_ON_DEATH_CHANCE); + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Current chance:\u00a7b " + deathChance); + if (deathChance <= 0) { return; } + + List items = data.getInventory().getEquipped(); + ArrayList equipped = new ArrayList<>(); + + // Equipped Player Items yeah... + for (EquippedPlayerItem playerItem : items) { + + // Null + if (playerItem == null) { continue; } + //DET//playerItem.getItem().hasData(ItemStats.NAME); + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Item:\u00a7b " + playerItem.getItem().getData(ItemStats.NAME)); + + // Cannot perform operations of items that are uneditable + if (!(playerItem.getEquipped() instanceof EditableEquippedItem)) { + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Not equippable. \u00a7cCancel"); + continue; } + + // Not downgradeable on death? Snooze + if (!playerItem.getItem().hasData(ItemStats.DOWNGRADE_ON_DEATH)) { + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Not Downgradeable. \u00a7cCancel"); + continue; } + + // No upgrade template no snooze + if(!playerItem.getItem().hasData(ItemStats.UPGRADE)) { + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Not Upgradeable. \u00a7cCancel"); + continue; } + if (!playerItem.getItem().hasUpgradeTemplate()) { + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Null Template. \u00a7cCancel"); + continue; } + + // If it can be downgraded by one level... + UpgradeData upgradeData = (UpgradeData) playerItem.getItem().getData(ItemStats.UPGRADE); + if (upgradeData.getLevel() <= upgradeData.getMin()) { + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Too downgraded. \u00a7cCancel"); + continue; } + + // Okay explore stat + equipped.add((EditableEquippedItem) playerItem.getEquipped()); + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Yes. \u00a7aAccepted"); + } + + // Nothing to perform operations? Snooze + if (equipped.size() == 0) { + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 No items to downgrade. "); + return; } + Random random = new Random(); + + // Degrade those items! + while (deathChance >= 100 && equipped.size() > 0) { + + // Decrease + deathChance -= 100; + + // Downgrade random item + int d = random.nextInt(equipped.size()); + + /* + * The item was chosen, we must downgrade it by one level. + */ + EditableEquippedItem equip = equipped.get(d); + LiveMMOItem mmo = new LiveMMOItem(equip.getItem()); + mmo.getUpgradeTemplate().upgradeTo(mmo, mmo.getUpgradeLevel() - 1); + + // Build NBT + ItemStack bakedItem = mmo.newBuilder().build(); + + // Set durability to zero (full repair) + DurabilityItem dur = new DurabilityItem(event.getEntity(), mmo.newBuilder().buildNBT()); + + if (dur.getDurability() != dur.getMaxDurability()) { + dur.addDurability(dur.getMaxDurability()); + bakedItem.setItemMeta(dur.toItem().getItemMeta());} + + // AH + equip.setItem(bakedItem); + equipped.remove(d); + + Message.DEATH_DOWNGRADING.format(ChatColor.RED, "#item#", SilentNumbers.getItemName(equip.getItem().getItem(), false)) + .send(event.getEntity()); + + //DET//MMOItems.log("\u00a78DETH \u00a7cDG\u00a77 Autodegrading\u00a7a " + mmo.getData(ItemStats.NAME)); + } + + // If there is chance, and there is size, and there is chance success + if (deathChance > 0 && equipped.size() > 0 && random.nextInt(100) < deathChance) { + + // Downgrade random item + int d = random.nextInt(equipped.size()); + + /* + * The item was chosen, we must downgrade it by one level. + */ + EditableEquippedItem equip = equipped.get(d); + LiveMMOItem mmo = new LiveMMOItem(equip.getItem()); + mmo.getUpgradeTemplate().upgradeTo(mmo, mmo.getUpgradeLevel() - 1); + + // Build NBT + ItemStack bakedItem = mmo.newBuilder().build(); + + // Set durability to zero (full repair) + DurabilityItem dur = new DurabilityItem(event.getEntity(), mmo.newBuilder().buildNBT()); + + if (dur.getDurability() != dur.getMaxDurability()) { + dur.addDurability(dur.getMaxDurability()); + bakedItem.setItemMeta(dur.toItem().getItemMeta());} + + // AH + equip.setItem(bakedItem); + equipped.remove(d); + + Message.DEATH_DOWNGRADING.format(ChatColor.RED, "#item#", SilentNumbers.getItemName(equip.getItem().getItem(), false)) + .send(event.getEntity()); + } + } + + /** * Prevent players from dropping items which are bound to them with a * soulbound. Items are cached inside a map waiting for the player to * respawn. If he does not respawn the items are dropped on the ground, this * way there don't get lost */ @EventHandler(priority = EventPriority.HIGH) - public void onDeath(PlayerDeathEvent event) { + public void onDeathForSoulbound(PlayerDeathEvent event) { if (event.getKeepInventory() || !MMOItems.plugin.getLanguage().keepSoulboundOnDeath) return; diff --git a/src/main/java/net/Indyuce/mmoitems/listener/reforging/RFGKeepUpgrades.java b/src/main/java/net/Indyuce/mmoitems/listener/reforging/RFGKeepUpgrades.java index cc717115..8cc6cc60 100644 --- a/src/main/java/net/Indyuce/mmoitems/listener/reforging/RFGKeepUpgrades.java +++ b/src/main/java/net/Indyuce/mmoitems/listener/reforging/RFGKeepUpgrades.java @@ -27,7 +27,7 @@ public class RFGKeepUpgrades implements Listener { //UPGRD//MMOItems.log(" \u00a7e* \u00a77Existing Upgrade Detected"); // Get current ig - UpgradeData processed = new UpgradeData(newOne.getReference(), newOne.getTemplateName(), newOne.isWorkbench(), newOne.isDestroy(), newOne.getMax(), newOne.getSuccess()); + UpgradeData processed = new UpgradeData(newOne.getReference(), newOne.getTemplateName(), newOne.isWorkbench(), newOne.isDestroy(), newOne.getMax(), newOne.getMin(), newOne.getSuccess()); // Edit level processed.setLevel(Math.min(upgrade.getLevel(), newOne.getMaxUpgrades())); diff --git a/src/main/java/net/Indyuce/mmoitems/stat/BrowserDisplayIDX.java b/src/main/java/net/Indyuce/mmoitems/stat/BrowserDisplayIDX.java new file mode 100644 index 00000000..e96c57a4 --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/stat/BrowserDisplayIDX.java @@ -0,0 +1,110 @@ +package net.Indyuce.mmoitems.stat; + +import net.Indyuce.mmoitems.ItemStats; +import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder; +import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; +import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; +import net.Indyuce.mmoitems.api.util.NumericStatFormula; +import net.Indyuce.mmoitems.stat.data.type.StatData; +import net.Indyuce.mmoitems.stat.type.DoubleStat; +import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class BrowserDisplayIDX extends DoubleStat { + + public BrowserDisplayIDX() { super("BROWSER_IDX", Material.GHAST_TEAR, "Browser Index", new String[] {"Used to display similar items together,", "neatly in the GUI \u00a7a/mmoitems browse", "", "Items with the same index are grouped."}, new String[]{"all"}); } + + // Does not participate in actual items + @Override public void whenApplied(@NotNull ItemStackBuilder item, @NotNull StatData data) { } + @Override public void whenLoaded(@NotNull ReadMMOItem mmoitem) { } + + /** + * They will be ordered. + * + * @return The MMOItem Templates separated by Index. Those with no index + * will be linked to the null index. + */ + @NotNull public static HashMap> select(@NotNull Collection templates) { + HashMap> ret = new HashMap<>(); + + // Go through them all + for (MMOItemTemplate template : templates) { + if (template == null) { continue; } + + Double armorIDX = null; + if (template.getType().getAvailableStats().contains(ItemStats.BROWSER_DISPLAY_IDX)) { + NumericStatFormula indexData = (NumericStatFormula) template.getBaseItemData().get(ItemStats.BROWSER_DISPLAY_IDX); + + // Get value if it existed + if (indexData != null && indexData.getBase() != 0) { armorIDX = indexData.getBase(); } + } + + // Get that map + ArrayList perIndexTemplates = ret.get(armorIDX); + if (perIndexTemplates == null) { perIndexTemplates = new ArrayList<>(); } + + // Include template + perIndexTemplates.add(template); + ret.put(armorIDX, perIndexTemplates); + } + + return ret; + } + + /** + * @param i Index you search + * + * @param templates Templates from which to gather + * + * @return The Ith Template of this Array. + */ + @Nullable public static MMOItemTemplate getAt(int i, @NotNull HashMap> templates) { + Map.Entry> nullEntry = null; + + // Iterate every entry + for (Map.Entry> entry : templates.entrySet()) { + + // Null entry are always displayed at the end, reference and skip. + if (entry.getKey() == null) { nullEntry = entry; continue; } + + // Identify list, add null entries until it has a size multiple of four. + @NotNull ArrayList list = entry.getValue(); + while (list.size() % 4 != 0) { list.add(null); } + + /* + * Go through each entry until i equals zero + */ + for (MMOItemTemplate observed : list) { + + // Yes + if (i == 0) { return observed; } + i--; + } + } + + // No more + if (nullEntry == null) { return null; } + + // Still standing... + @NotNull ArrayList list = nullEntry.getValue(); + + /* + * Go through each entry until i equals zero + */ + for (MMOItemTemplate observed : list) { + + // Yes + if (i == 0) { return observed; } + i--; + } + + // None found + return null; + } +} diff --git a/src/main/java/net/Indyuce/mmoitems/stat/LostWhenBroken.java b/src/main/java/net/Indyuce/mmoitems/stat/LostWhenBroken.java index d59ae6dd..5bb4216f 100644 --- a/src/main/java/net/Indyuce/mmoitems/stat/LostWhenBroken.java +++ b/src/main/java/net/Indyuce/mmoitems/stat/LostWhenBroken.java @@ -13,10 +13,4 @@ public class LostWhenBroken extends BooleanStat { public LostWhenBroken() { super("WILL_BREAK", Material.SHEARS, "Lost when Broken?", new String[] { "If set to true, the item will be lost", "once it reaches 0 durability." }, new String[] { "!block", "all" }); } - - @Override - public void whenApplied(@NotNull ItemStackBuilder item, @NotNull StatData data) { - if (((BooleanData) data).isEnabled()) - item.addItemTag(new ItemTag("MMOITEMS_WILL_BREAK", true)); - } } diff --git a/src/main/java/net/Indyuce/mmoitems/stat/RepairPower.java b/src/main/java/net/Indyuce/mmoitems/stat/RepairPower.java index 06ef178f..9d290edf 100644 --- a/src/main/java/net/Indyuce/mmoitems/stat/RepairPower.java +++ b/src/main/java/net/Indyuce/mmoitems/stat/RepairPower.java @@ -56,7 +56,7 @@ public class RepairPower extends DoubleStat implements ConsumableItemInteraction if (durItem.getDurability() < durItem.getMaxDurability()) { target.getItem().setItemMeta(durItem.addDurability(called.getRepaired()).toItem().getItemMeta()); Message.REPAIRED_ITEM - .format(ChatColor.YELLOW, "#item#", MMOUtils.getDisplayName(target.getItem()), "#amount#", "" + called.getRepaired()) + .format(ChatColor.YELLOW, "#item#", MMOUtils.getDisplayName(target.getItem()), "#amount#", String.valueOf(called.getRepaired())) .send(player); CustomSoundListener.playConsumableSound(consumable.getItem(), player); } diff --git a/src/main/java/net/Indyuce/mmoitems/stat/UpgradeStat.java b/src/main/java/net/Indyuce/mmoitems/stat/UpgradeStat.java index 7320f333..7ddadad6 100644 --- a/src/main/java/net/Indyuce/mmoitems/stat/UpgradeStat.java +++ b/src/main/java/net/Indyuce/mmoitems/stat/UpgradeStat.java @@ -113,6 +113,15 @@ public class UpgradeStat extends ItemStat implements ConsumableItemInteraction { return; } + if (info[0].equals("min")) { + int i = Integer.parseInt(message); + inv.getEditedSection().set("upgrade.min", i); + inv.registerTemplateEdition(); + inv.getPlayer() + .sendMessage(MMOItems.plugin.getPrefix() + "Min level successfully set to " + ChatColor.GOLD + i + ChatColor.GRAY + "."); + return; + } + if (info[0].equals("rate")) { double d = MMOUtils.parseDouble(message); inv.getEditedSection().set("upgrade.success", d); @@ -173,7 +182,7 @@ public class UpgradeStat extends ItemStat implements ConsumableItemInteraction { @NotNull @Override - public StatData getClearStatData() { return new UpgradeData(null, null, false, false, 0, 0D); } + public StatData getClearStatData() { return new UpgradeData(null, null, false, false, 0, 0, 0D); } @Override public boolean handleConsumableEffect(@NotNull InventoryClickEvent event, @NotNull PlayerData playerData, @NotNull Consumable consumable, @NotNull NBTItem target, Type targetType) { diff --git a/src/main/java/net/Indyuce/mmoitems/stat/data/UpgradeData.java b/src/main/java/net/Indyuce/mmoitems/stat/data/UpgradeData.java index a9aa5422..b7f59dba 100644 --- a/src/main/java/net/Indyuce/mmoitems/stat/data/UpgradeData.java +++ b/src/main/java/net/Indyuce/mmoitems/stat/data/UpgradeData.java @@ -42,22 +42,51 @@ public class UpgradeData implements StatData, RandomStatData, Cloneable { /** * @return Max amount of upgrades this can hold */ - public int getMax() { - return max; - } + public int getMax() { return max; } + /** + * @return Minimum level this item can be downgraded to + */ + public int getMin() { return min; } @Nullable private final String reference, template; private final boolean workbench, destroy; private final double success; private final int max; + private final int min; private int level; + /** + * Create a new UpgradeData + * + * @param reference Upgrade Reference to use + * @param template Upgrade Template to follow + * @param workbench If it is upgraded in workbench (I don't know for sure)? + * @param destroy If it will destroy the item if the upgrade fails + * @param max Max Level attainable + * @param success Success Chance + */ public UpgradeData(@Nullable String reference, @Nullable String template, boolean workbench, boolean destroy, int max, double success) { + this(reference, template, workbench, destroy, max, 0, success); + } + + /** + * Create a new UpgradeData + * + * @param reference Upgrade Reference to use + * @param template Upgrade Template to follow + * @param workbench If it is upgraded in workbench (I don't know for sure)? + * @param destroy If it will destroy the item if the upgrade fails + * @param max Max Level attainable + * @param min Min Level attainable through downgrading + * @param success Success Chance + */ + public UpgradeData(@Nullable String reference, @Nullable String template, boolean workbench, boolean destroy, int max, int min, double success) { this.reference = reference; this.template = template; this.workbench = workbench; this.destroy = destroy; this.max = max; + this.min = min; this.success = success; } @@ -67,6 +96,7 @@ public class UpgradeData implements StatData, RandomStatData, Cloneable { workbench = section.getBoolean("workbench"); destroy = section.getBoolean("destroy"); max = section.getInt("max"); + min = section.getInt("min", 0); success = section.getDouble("success") / 100; } @@ -77,6 +107,7 @@ public class UpgradeData implements StatData, RandomStatData, Cloneable { reference = object.has("Reference") ? object.get("Reference").getAsString() : null; level = object.get("Level").getAsInt(); max = object.get("Max").getAsInt(); + min = object.has("Min") ? object.get("Min").getAsInt() : 0; success = object.get("Success").getAsDouble(); } @@ -100,9 +131,7 @@ public class UpgradeData implements StatData, RandomStatData, Cloneable { */ public void setLevel(int l) { level = l; } - public int getMaxUpgrades() { - return max; - } + public int getMaxUpgrades() { return max; } public boolean canLevelUp() { return max == 0 || level < max; @@ -150,6 +179,7 @@ public class UpgradeData implements StatData, RandomStatData, Cloneable { json.addProperty("Destroy", destroy); json.addProperty("Level", level); json.addProperty("Max", max); + json.addProperty("Min", min); json.addProperty("Success", success); return json; @@ -169,5 +199,5 @@ public class UpgradeData implements StatData, RandomStatData, Cloneable { public UpgradeData clone() { try { super.clone(); } catch (CloneNotSupportedException ignored) { } - return new UpgradeData(reference, template, workbench, destroy, max, success); } + return new UpgradeData(reference, template, workbench, destroy, max, min, success); } } \ No newline at end of file diff --git a/src/main/java/net/Indyuce/mmoitems/stat/type/EvilDoubleStat.java b/src/main/java/net/Indyuce/mmoitems/stat/type/EvilDoubleStat.java new file mode 100644 index 00000000..ce8bd89f --- /dev/null +++ b/src/main/java/net/Indyuce/mmoitems/stat/type/EvilDoubleStat.java @@ -0,0 +1,11 @@ +package net.Indyuce.mmoitems.stat.type; + +import org.bukkit.Material; + +public class EvilDoubleStat extends DoubleStat { + + public EvilDoubleStat(String id, Material mat, String name, String[] lore) { super(id, mat, name, lore); } + public EvilDoubleStat(String id, Material mat, String name, String[] lore, String[] types, Material... materials) { super(id, mat, name, lore, types, materials); } + + @Override public boolean moreIsBetter() { return false; } +}