diff --git a/src/main/java/lol/hyper/toolstats/ToolStats.java b/src/main/java/lol/hyper/toolstats/ToolStats.java index 254c2f7..8f1fc99 100644 --- a/src/main/java/lol/hyper/toolstats/ToolStats.java +++ b/src/main/java/lol/hyper/toolstats/ToolStats.java @@ -115,6 +115,10 @@ public final class ToolStats extends JavaPlugin { * Key for withers killed. */ public final NamespacedKey enderDragonKills = new NamespacedKey(this, "enderdragon-kills"); + /** + * Key for critical strikes. + */ + public final NamespacedKey criticalStrikes = new NamespacedKey(this, "critical-strikes"); /** * Stores how an item was created. diff --git a/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java b/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java index a62f120..7edea42 100644 --- a/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java +++ b/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java @@ -406,6 +406,14 @@ public class CommandToolStats implements TabExecutor { } } } + if (toolStats.config.getBoolean("enabled.critical-strikes")) { + if (container.has(toolStats.criticalStrikes, PersistentDataType.INTEGER)) { + Integer strikes = container.get(toolStats.criticalStrikes, PersistentDataType.INTEGER); + if (strikes != null) { + lore.add(toolStats.configTools.formatLore("critical-strikes", "{strikes}", toolStats.numberFormat.formatInt(strikes))); + } + } + } finalMeta.lore(lore); finalItem.setItemMeta(finalMeta); int slot = player.getInventory().getHeldItemSlot(); @@ -788,6 +796,35 @@ public class CommandToolStats implements TabExecutor { } break; } + case "critical-strikes": { + if (!toolStats.config.getBoolean("enabled.critical-strikes")) { + player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED)); + return; + } + if (container.has(toolStats.criticalStrikes)) { + int value; + try { + value = Integer.parseInt((String) userValue); + } catch (NumberFormatException exception) { + player.sendMessage(Component.text("That is not a valid number.", NamedTextColor.RED)); + return; + } + if (value < 0) { + player.sendMessage(Component.text("Number must be positive.", NamedTextColor.RED)); + return; + } + Integer statValue = container.get(toolStats.criticalStrikes, PersistentDataType.INTEGER); + if (statValue == null) { + player.sendMessage(Component.text("Unable to get stat from item.", NamedTextColor.RED)); + return; + } + int difference = value - statValue; + editedItemMeta = toolStats.itemLore.updateCriticalStrikes(editedItem, difference); + } else { + player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); + } + break; + } default: { player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED)); return; @@ -1122,6 +1159,34 @@ public class CommandToolStats implements TabExecutor { } break; } + case "critical-strikes": { + if (container.has(toolStats.criticalStrikes)) { + Integer statValue = container.get(toolStats.criticalStrikes, PersistentDataType.INTEGER); + if (statValue == null) { + player.sendMessage(Component.text("Unable to get stat from item.", NamedTextColor.RED)); + return; + } + String tokens = container.get(toolStats.tokenApplied, PersistentDataType.STRING); + if (tokens == null) { + player.sendMessage(Component.text("Unable to get tokens from item.", NamedTextColor.RED)); + return; + } + container.remove(toolStats.criticalStrikes); + List newTokens = toolStats.itemChecker.removeToken(tokens, "critical-strikes"); + if (newTokens.isEmpty()) { + container.remove(toolStats.tokenApplied); + } else { + container.set(toolStats.tokenApplied, PersistentDataType.STRING, String.join(",", newTokens)); + } + + Component oldLine = toolStats.configTools.formatLore("critical-strikes", "{strikes}", toolStats.numberFormat.formatInt(statValue)); + List newLore = toolStats.itemLore.removeLore(editedItemMeta.lore(), oldLine); + editedItemMeta.lore(newLore); + } else { + player.sendMessage(Component.text("This item does not have that stat.", NamedTextColor.RED)); + } + break; + } default: { player.sendMessage(Component.text("That is not a valid stat to update.", NamedTextColor.RED)); return; diff --git a/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java b/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java index 857b274..7249208 100644 --- a/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java +++ b/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java @@ -135,6 +135,10 @@ public class AnvilEvent implements Listener { addToken(event, tokenType, "enderdragon-kills", clone); return; } + if (tokenType.equalsIgnoreCase("critical-strikes")) { + addToken(event, tokenType, "critical-strikes", clone); + return; + } } return; } @@ -167,6 +171,10 @@ public class AnvilEvent implements Listener { addToken(event, tokenType, "enderdragon-kills", clone); return; } + if (tokenType.equalsIgnoreCase("critical-strikes")) { + addToken(event, tokenType, "critical-strikes", clone); + return; + } return; } if (firstSlotMaterial == Material.BOW || firstSlotMaterial == Material.CROSSBOW) { @@ -333,6 +341,15 @@ public class AnvilEvent implements Listener { } break; } + case "critical-strikes": { + if (toolStats.config.getBoolean("enabled.critical-strikes")) { + newItem.setItemMeta(toolStats.itemLore.updateCriticalStrikes(newItem, 0)); + } else { + event.setResult(null); + return; + } + break; + } } event.setResult(newItem); event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken)); @@ -450,6 +467,14 @@ public class AnvilEvent implements Listener { meta = toolStats.itemLore.updateBossesKilled(finalItem, -enderDragonKills, "enderdragon"); finalItem.setItemMeta(meta); } + if (container.has(toolStats.criticalStrikes)) { + Integer criticalStrikes = container.get(toolStats.criticalStrikes, PersistentDataType.INTEGER); + if (criticalStrikes == null) { + return; + } + meta = toolStats.itemLore.updateCriticalStrikes(finalItem, -criticalStrikes); + finalItem.setItemMeta(meta); + } event.setResult(finalItem); event.getView().setRepairCost(toolStats.itemChecker.getCost("reset")); } diff --git a/src/main/java/lol/hyper/toolstats/events/EntityDamage.java b/src/main/java/lol/hyper/toolstats/events/EntityDamage.java index a7c4aaf..c9a4c9f 100644 --- a/src/main/java/lol/hyper/toolstats/events/EntityDamage.java +++ b/src/main/java/lol/hyper/toolstats/events/EntityDamage.java @@ -67,6 +67,7 @@ public class EntityDamage implements Listener { double finalDamage = event.getFinalDamage(); boolean modDied = mobBeingAttacked.getHealth() - finalDamage <= 0; EntityType mobAttackedType = event.getEntityType(); + boolean critical = event.isCritical(); // player attacks something if (playerAttacking) { @@ -80,6 +81,11 @@ public class EntityDamage implements Listener { // update their weapon's damage updateWeaponDamage(playerAttackingInventory, event.getFinalDamage()); + // if the player crit + if (critical) { + updateCriticalStrikes(playerAttackingInventory); + } + // the mob the player attacked died if (modDied) { // player killed another player @@ -88,12 +94,11 @@ public class EntityDamage implements Listener { } else { // player kills a regular mob updateWeaponKills(playerAttackingInventory, "mob"); - // reget the player inventory since we updated above if (mobAttackedType == EntityType.WITHER) { - updateBossesKilled(player.getInventory(), "wither"); + updateBossesKilled(playerAttackingInventory, "wither"); } if (mobAttackedType == EntityType.ENDER_DRAGON) { - updateBossesKilled(player.getInventory(), "enderdragon"); + updateBossesKilled(playerAttackingInventory, "enderdragon"); } } } @@ -306,8 +311,7 @@ public class EntityDamage implements Listener { private void updateBossesKilled(PlayerInventory playerInventory, String boss) { ItemStack heldWeapon = playerInventory.getItemInMainHand(); - ItemMeta newHeldWeaponMeta = null; - newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldWeapon, 1, boss); + ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateBossesKilled(heldWeapon, 1, boss); if (newHeldWeaponMeta != null) { playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta); } @@ -333,4 +337,12 @@ public class EntityDamage implements Listener { } } } + + private void updateCriticalStrikes(PlayerInventory playerInventory) { + ItemStack heldWeapon = playerInventory.getItemInMainHand(); + ItemMeta newHeldWeaponMeta = toolStats.itemLore.updateCriticalStrikes(heldWeapon, 1); + if (newHeldWeaponMeta != null) { + playerInventory.getItemInMainHand().setItemMeta(newHeldWeaponMeta); + } + } } diff --git a/src/main/java/lol/hyper/toolstats/tools/ItemLore.java b/src/main/java/lol/hyper/toolstats/tools/ItemLore.java index 4bef92b..cc7d862 100644 --- a/src/main/java/lol/hyper/toolstats/tools/ItemLore.java +++ b/src/main/java/lol/hyper/toolstats/tools/ItemLore.java @@ -1217,6 +1217,104 @@ public class ItemLore { return meta; } + /** + * Add x to critical strikes stat. + * + * @param weapon The weapon used. + */ + public ItemMeta updateCriticalStrikes(ItemStack weapon, int add) { + 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(); + + // 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.critical-strikes")) { + if (container.has(toolStats.criticalStrikes)) { + Integer criticalStrikes = container.get(toolStats.criticalStrikes, PersistentDataType.INTEGER); + if (criticalStrikes == null) { + return null; + } + container.remove(toolStats.criticalStrikes); + // 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, "critical-strikes"); + if (!newTokens.isEmpty()) { + container.set(toolStats.tokenApplied, PersistentDataType.STRING, String.join(",", newTokens)); + } else { + container.remove(toolStats.tokenApplied); + } + } + } + if (meta.hasLore()) { + String oldCriticalStrikes = toolStats.numberFormat.formatInt(criticalStrikes); + Component lineToRemove = toolStats.configTools.formatLore("critical-strikes", "{strikes}", oldCriticalStrikes); + List newLore = removeLore(meta.lore(), lineToRemove); + meta.lore(newLore); + } + return meta; + } + return null; + } + + // check for tokens + boolean validToken = toolStats.itemChecker.checkTokens(container, "critical-strikes"); + // check for tokens + if (toolStats.config.getBoolean("tokens.enabled")) { + // if the item has stats but no token, add the token + if (container.has(toolStats.criticalStrikes) && !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 criticalStrikes = 0; + if (container.has(toolStats.criticalStrikes, PersistentDataType.INTEGER)) { + criticalStrikes = container.get(toolStats.criticalStrikes, PersistentDataType.INTEGER); + } + + if (criticalStrikes == null) { + criticalStrikes = 0; + toolStats.logger.warn("{} does not have valid fish-caught set! Resting to zero. This should NEVER happen.", clone); + } + + container.set(toolStats.criticalStrikes, PersistentDataType.INTEGER, criticalStrikes + add); + String oldCriticalStrikesFormatted = toolStats.numberFormat.formatInt(criticalStrikes); + String newCriticalStrikesFormatted = toolStats.numberFormat.formatInt(criticalStrikes + add); + Component oldLine = toolStats.configTools.formatLore("critical-strikes", "{strikes}", oldCriticalStrikesFormatted); + Component newLine = toolStats.configTools.formatLore("critical-strikes", "{strikes}", newCriticalStrikesFormatted); + if (oldLine == null || newLine == null) { + return null; + } + List newLore = updateItemLore(meta, oldLine, newLine); + meta.lore(newLore); + return meta; + } + /** * Format the item owner lore. * @@ -1461,6 +1559,16 @@ public class ItemLore { finalItem.setItemMeta(meta); } } + if (container.has(toolStats.criticalStrikes)) { + Integer criticalStrikes = container.get(toolStats.criticalStrikes, PersistentDataType.INTEGER); + if (criticalStrikes != null) { + container.remove(toolStats.criticalStrikes); + String criticalStrikesFormatted = toolStats.numberFormat.formatInt(criticalStrikes); + Component lineToRemove = toolStats.configTools.formatLore("critical-strikes", "{strikes}", criticalStrikesFormatted); + 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 ed8b0a2..5676b27 100644 --- a/src/main/java/lol/hyper/toolstats/tools/TokenData.java +++ b/src/main/java/lol/hyper/toolstats/tools/TokenData.java @@ -139,6 +139,13 @@ public class TokenData { enderDragonKillsRecipe.setIngredient('E', Material.ENDER_PEARL); recipes.add(enderDragonKillsRecipe); + NamespacedKey criticalStrikesKey = new NamespacedKey(toolStats, "critical-strikes-token"); + ShapedRecipe criticalStrikesRecipe = new ShapedRecipe(criticalStrikesKey, createToken("critical-strikes")); + criticalStrikesRecipe.shape(" P ", "PSP", " P "); + enderDragonKillsRecipe.setIngredient('P', Material.PAPER); + enderDragonKillsRecipe.setIngredient('S', Material.GOLDEN_SWORD); + recipes.add(criticalStrikesRecipe); + tokenTypes.add("crops-mined"); tokenTypes.add("blocks-mined"); tokenTypes.add("damage-taken"); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 24f1e73..33c5b7d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -158,6 +158,17 @@ tokens: enabled: false type: float value: 1001 + critical-strikes: + title: "&7ToolStats: &8Critical Strikes Token" + lore: + - "&8Combine with a melee or ranged weapon in an anvil to track critical strikes." + - "&8Uses &7{levels} &8level." + levels: 1 + material: PAPER + custom-model-data: + enabled: false + type: float + value: 1001 enabled: # Will show "Crafted by " @@ -319,6 +330,7 @@ enabled: arrows-shot: true flight-time: true crops-harvested: true + critical-strikes: true messages: crafted: @@ -354,6 +366,7 @@ messages: arrows-shot: "&7Arrows shot: &8{arrows}" flight-time: "&7Flight time: &8{years}y {months}m {days}d {hours}h {minutes}m {seconds}s" damage-done: "&7Damage done: &8{damage}" + critical-strikes: "&7Critical strikes: &8{strikes}" # Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html mobs: ZOMBIE: "Zombie"