diff --git a/src/main/java/lol/hyper/toolstats/ToolStats.java b/src/main/java/lol/hyper/toolstats/ToolStats.java index 03acc83..254c2f7 100644 --- a/src/main/java/lol/hyper/toolstats/ToolStats.java +++ b/src/main/java/lol/hyper/toolstats/ToolStats.java @@ -107,6 +107,15 @@ public final class ToolStats extends JavaPlugin { * Key for applied token. This is what goes onto the tool/armor to record the type. */ public final NamespacedKey tokenApplied = new NamespacedKey(this, "token-applied"); + /** + * Key for withers killed. + */ + public final NamespacedKey witherKills = new NamespacedKey(this, "wither-kills"); + /** + * Key for withers killed. + */ + public final NamespacedKey enderDragonKills = new NamespacedKey(this, "enderdragon-kills"); + /** * Stores how an item was created. * 0 = crafted. diff --git a/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java b/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java index 7bb31c4..857b274 100644 --- a/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java +++ b/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java @@ -127,6 +127,14 @@ public class AnvilEvent implements Listener { addToken(event, tokenType, "damage-done", clone); return; } + if (tokenType.equalsIgnoreCase("wither-kills")) { + addToken(event, tokenType, "wither-kills", clone); + return; + } + if (tokenType.equalsIgnoreCase("enderdragon-kills")) { + addToken(event, tokenType, "enderdragon-kills", clone); + return; + } } return; } @@ -151,6 +159,14 @@ public class AnvilEvent implements Listener { addToken(event, tokenType, "damage-done", clone); return; } + if (tokenType.equalsIgnoreCase("wither-kills")) { + addToken(event, tokenType, "wither-kills", clone); + return; + } + if (tokenType.equalsIgnoreCase("enderdragon-kills")) { + addToken(event, tokenType, "enderdragon-kills", clone); + return; + } return; } if (firstSlotMaterial == Material.BOW || firstSlotMaterial == Material.CROSSBOW) { @@ -170,6 +186,14 @@ public class AnvilEvent implements Listener { addToken(event, tokenType, "damage-done", clone); return; } + if (tokenType.equalsIgnoreCase("wither-kills")) { + addToken(event, tokenType, "wither-kills", clone); + return; + } + if (tokenType.equalsIgnoreCase("enderdragon-kills")) { + addToken(event, tokenType, "enderdragon-kills", clone); + return; + } return; } if (firstSlotMaterial == Material.FISHING_ROD) { @@ -291,6 +315,24 @@ public class AnvilEvent implements Listener { } break; } + case "wither-kills": { + if (toolStats.config.getBoolean("enabled.bosses-killed.wither")) { + newItem.setItemMeta(toolStats.itemLore.updateBossesKilled(newItem, 0, "wither")); + } else { + event.setResult(null); + return; + } + break; + } + case "enderdragon-kills": { + if (toolStats.config.getBoolean("enabled.bosses-killed.enderdragon")) { + newItem.setItemMeta(toolStats.itemLore.updateBossesKilled(newItem, 0, "enderdragon")); + } else { + event.setResult(null); + return; + } + break; + } } event.setResult(newItem); event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken)); @@ -392,6 +434,22 @@ public class AnvilEvent implements Listener { meta = toolStats.itemLore.updateFlightTime(finalItem, -flightTime); finalItem.setItemMeta(meta); } + if (container.has(toolStats.witherKills)) { + Integer witherKills = container.get(toolStats.witherKills, PersistentDataType.INTEGER); + if (witherKills == null) { + return; + } + meta = toolStats.itemLore.updateBossesKilled(finalItem, -witherKills, "wither"); + finalItem.setItemMeta(meta); + } + if (container.has(toolStats.enderDragonKills)) { + Integer enderDragonKills = container.get(toolStats.enderDragonKills, PersistentDataType.INTEGER); + if (enderDragonKills == null) { + return; + } + meta = toolStats.itemLore.updateBossesKilled(finalItem, -enderDragonKills, "enderdragon"); + finalItem.setItemMeta(meta); + } event.setResult(finalItem); event.getView().setRepairCost(toolStats.itemChecker.getCost("reset")); } diff --git a/src/main/java/lol/hyper/toolstats/tools/ItemLore.java b/src/main/java/lol/hyper/toolstats/tools/ItemLore.java index e283dfa..4bef92b 100644 --- a/src/main/java/lol/hyper/toolstats/tools/ItemLore.java +++ b/src/main/java/lol/hyper/toolstats/tools/ItemLore.java @@ -22,6 +22,7 @@ import lol.hyper.toolstats.ToolStats; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataContainer; @@ -848,7 +849,7 @@ public class ItemLore { } } if (meta.hasLore()) { - String oldSheepShearedFormatted = toolStats.numberFormat.formatDouble(sheepSheared); + String oldSheepShearedFormatted = toolStats.numberFormat.formatInt(sheepSheared); Component lineToRemove = toolStats.configTools.formatLore("sheep-sheared", "{sheep}", oldSheepShearedFormatted); List newLore = removeLore(meta.lore(), lineToRemove); meta.lore(newLore); @@ -946,7 +947,7 @@ public class ItemLore { } } if (meta.hasLore()) { - String oldArrowsShotFormatted = toolStats.numberFormat.formatDouble(arrowsShot); + String oldArrowsShotFormatted = toolStats.numberFormat.formatInt(arrowsShot); Component lineToRemove = toolStats.configTools.formatLore("arrows-shot", "{arrows}", oldArrowsShotFormatted); List newLore = removeLore(meta.lore(), lineToRemove); meta.lore(newLore); @@ -1046,7 +1047,7 @@ public class ItemLore { } } if (meta.hasLore()) { - String oldFishCaught = toolStats.numberFormat.formatDouble(fishCaught); + String oldFishCaught = toolStats.numberFormat.formatInt(fishCaught); Component lineToRemove = toolStats.configTools.formatLore("fished.fish-caught", "{fish}", oldFishCaught); List newLore = removeLore(meta.lore(), lineToRemove); meta.lore(newLore); @@ -1104,6 +1105,118 @@ public class ItemLore { return meta; } + /** + * Add x to bosses killed stat. + * + * @param weapon The weapon used. + * @param add How many to add. + * @param boss The boss killed + */ + public ItemMeta updateBossesKilled(ItemStack weapon, int add, String boss) { + ItemStack clone = weapon.clone(); + ItemMeta meta = clone.getItemMeta(); + if (meta == null) { + toolStats.logger.warn("{} does NOT have any meta! Unable to update stats.", clone); + return null; + } + + PersistentDataContainer container = meta.getPersistentDataContainer(); + + NamespacedKey bossesKey = null; + if (boss.equalsIgnoreCase("wither")) { + bossesKey = toolStats.witherKills; + } + if (boss.equalsIgnoreCase("enderdragon")) { + bossesKey = toolStats.enderDragonKills; + } + + if (bossesKey == null) { + return null; + } + + // if it's disabled, don't update the stats + // check to see if the item has the stats, remove them if it does + if (!toolStats.config.getBoolean("enabled.bosses-killed." + boss)) { + if (container.has(bossesKey)) { + Integer bossesKilled = container.get(bossesKey, PersistentDataType.INTEGER); + if (bossesKilled == null) { + return null; + } + container.remove(bossesKey); + // remove the applied token if this stat is disabled + if (container.has(toolStats.tokenApplied)) { + String appliedTokens = container.get(toolStats.tokenApplied, PersistentDataType.STRING); + if (appliedTokens != null) { + // remove the token from the list + // if the list is empty, remove the PDC + // otherwise set the PDC back with the new list + List newTokens = toolStats.itemChecker.removeToken(appliedTokens, "wither-kills"); + if (!newTokens.isEmpty()) { + container.set(toolStats.tokenApplied, PersistentDataType.STRING, String.join(",", newTokens)); + } else { + container.remove(toolStats.tokenApplied); + } + } + } + if (meta.hasLore()) { + String oldBossesKilled = toolStats.numberFormat.formatInt(bossesKilled); + Component lineToRemove = toolStats.configTools.formatLore("bosses-killed." + boss, "{kills}", oldBossesKilled); + List newLore = removeLore(meta.lore(), lineToRemove); + meta.lore(newLore); + } + return meta; + } + return null; + } + + // check for tokens + boolean validToken = toolStats.itemChecker.checkTokens(container, "wither-kills"); + // check for tokens + if (toolStats.config.getBoolean("tokens.enabled")) { + // if the item has stats but no token, add the token + if (container.has(bossesKey) && !validToken) { + String newTokens = toolStats.itemChecker.addTokensToExisting(clone); + if (newTokens != null) { + container.set(toolStats.tokenApplied, PersistentDataType.STRING, newTokens); + } + } + + // the item does not have a valid token + if (!validToken) { + return null; + } + } else { + if (!validToken) { + String newTokens = toolStats.itemChecker.addTokensToExisting(clone); + if (newTokens != null) { + container.set(toolStats.tokenApplied, PersistentDataType.STRING, newTokens); + } + } + } + + Integer bossesKilled = 0; + if (container.has(bossesKey, PersistentDataType.INTEGER)) { + bossesKilled = container.get(bossesKey, PersistentDataType.INTEGER); + } + + if (bossesKilled == null) { + bossesKilled = 0; + toolStats.logger.warn("{} does not have valid {} set! Resting to zero. This should NEVER happen.", clone, boss); + } + + container.set(bossesKey, PersistentDataType.INTEGER, bossesKilled + add); + String oldBossesKilledFormatted = toolStats.numberFormat.formatInt(bossesKilled); + String newBossesKilledFormatted = toolStats.numberFormat.formatInt(bossesKilled + add); + Component oldLine = toolStats.configTools.formatLore("bosses-killed." + boss, "{kills}", oldBossesKilledFormatted); + Component newLine = toolStats.configTools.formatLore("bosses-killed." + boss, "{kills}", newBossesKilledFormatted); + if (oldLine == null || newLine == null) { + return null; + } + List newLore = updateItemLore(meta, oldLine, newLine); + meta.lore(newLore); + return meta; + } + /** * Format the item owner lore. * @@ -1312,7 +1425,6 @@ public class ItemLore { Integer arrowsShot = container.get(toolStats.arrowsShot, PersistentDataType.INTEGER); if (arrowsShot != null) { container.remove(toolStats.arrowsShot); - String arrowsShotFormatted = toolStats.numberFormat.formatInt(arrowsShot); Component lineToRemove = toolStats.configTools.formatLore("arrows-shot", "{arrows}", arrowsShotFormatted); meta.lore(removeLore(meta.lore(), lineToRemove)); @@ -1329,6 +1441,26 @@ public class ItemLore { finalItem.setItemMeta(meta); } } + if (container.has(toolStats.witherKills)) { + Integer witherKills = container.get(toolStats.witherKills, PersistentDataType.INTEGER); + if (witherKills != null) { + container.remove(toolStats.witherKills); + String witherKillsFormatted = toolStats.numberFormat.formatInt(witherKills); + Component lineToRemove = toolStats.configTools.formatLore("bosses-killed.wither", "{kills}", witherKillsFormatted); + meta.lore(removeLore(meta.lore(), lineToRemove)); + finalItem.setItemMeta(meta); + } + } + if (container.has(toolStats.enderDragonKills)) { + Integer enderDragonKills = container.get(toolStats.enderDragonKills, PersistentDataType.INTEGER); + if (enderDragonKills != null) { + container.remove(toolStats.enderDragonKills); + String enderDragonKillsFormatted = toolStats.numberFormat.formatInt(enderDragonKills); + Component lineToRemove = toolStats.configTools.formatLore("bosses-killed.enderdragon", "{kills}", enderDragonKillsFormatted); + meta.lore(removeLore(meta.lore(), lineToRemove)); + finalItem.setItemMeta(meta); + } + } if (removeMeta) { Integer origin = null; if (container.has(toolStats.originType)) { diff --git a/src/main/java/lol/hyper/toolstats/tools/TokenData.java b/src/main/java/lol/hyper/toolstats/tools/TokenData.java index f3cca81..c94d1c9 100644 --- a/src/main/java/lol/hyper/toolstats/tools/TokenData.java +++ b/src/main/java/lol/hyper/toolstats/tools/TokenData.java @@ -125,6 +125,20 @@ public class TokenData { removeRecipe.setIngredient('P', Material.PAPER); recipes.add(removeRecipe); + NamespacedKey witherKillsKey = new NamespacedKey(toolStats, "wither-kills-token"); + ShapedRecipe witherKillsRecipe = new ShapedRecipe(witherKillsKey, createToken("wither-kills")); + witherKillsRecipe.shape(" P ", "PWP", " P "); + witherKillsRecipe.setIngredient('P', Material.PAPER); + witherKillsRecipe.setIngredient('W', Material.WITHER_ROSE); + recipes.add(witherKillsRecipe); + + NamespacedKey enderDragonKillsKey = new NamespacedKey(toolStats, "enderdragon-kills-token"); + ShapedRecipe enderDragonKillsRecipe = new ShapedRecipe(enderDragonKillsKey, createToken("enderdragon-kills")); + enderDragonKillsRecipe.shape(" P ", "PEP", " P "); + enderDragonKillsRecipe.setIngredient('P', Material.PAPER); + enderDragonKillsRecipe.setIngredient('W', Material.ENDER_PEARL); + recipes.add(enderDragonKillsRecipe); + tokenTypes.add("crops-mined"); tokenTypes.add("blocks-mined"); tokenTypes.add("damage-taken"); @@ -137,6 +151,8 @@ public class TokenData { tokenTypes.add("fish-caught"); tokenTypes.add("reset"); tokenTypes.add("remove"); + tokenTypes.add("wither-kills"); + tokenTypes.add("enderdragon-kills"); } public Set getRecipes() { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index de4589e..24f1e73 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -136,6 +136,28 @@ tokens: enabled: false type: float value: 1001 + wither-kills: + title: "&7ToolStats: &8Wither Kills Token" + lore: + - "&8Combine with a melee or ranged weapon in an anvil to track wither kills." + - "&8Uses &7{levels} &8level." + levels: 1 + material: PAPER + custom-model-data: + enabled: false + type: float + value: 1001 + enderdragon-kills: + title: "&7ToolStats: &8Ender Dragon Kills Token" + lore: + - "&8Combine with a melee or ranged weapon in an anvil to track Ender Dragon kills." + - "&8Uses &7{levels} &8level." + levels: 1 + material: PAPER + custom-model-data: + enabled: false + type: float + value: 1001 enabled: # Will show "Crafted by " @@ -285,6 +307,9 @@ enabled: mace: true fishing-rod: true spear: true + bosses-killed: + wither: true + enderdragon: true fish-caught: true sheep-sheared: true armor-damage: true @@ -317,6 +342,9 @@ messages: spawned-in: spawned-by: "&7Spawned in by: &8{player}" spawned-on: "&7Spawned on: &8{date}" + bosses-killed: + wither: "&7Withers killed: &8{kills}" + enderdragon: "&7Ender Dragons killed: &8{kills}" blocks-mined: "&7Blocks mined: &8{blocks}" crops-harvested: "&7Crops harvested: &8{crops}" sheep-sheared: "&7Sheep sheared: &8{sheep}"