From 6a13c7fef73b40146a21f245881b2a6d21ad5611 Mon Sep 17 00:00:00 2001 From: hyperdefined Date: Sun, 5 Apr 2026 20:18:43 -0400 Subject: [PATCH] add logs stripped --- .../toolstats/commands/CommandToolStats.java | 66 ++++++++++ .../hyper/toolstats/events/AnvilEvent.java | 21 +++ .../hyper/toolstats/events/BlockBreak.java | 2 +- .../hyper/toolstats/events/EntityDamage.java | 1 - .../hyper/toolstats/events/GenerateLoot.java | 4 +- .../toolstats/events/PlayerInteract.java | 32 ++++- .../hyper/toolstats/tools/ItemChecker.java | 32 +++++ .../lol/hyper/toolstats/tools/ItemLore.java | 122 +++++++++++++++++- .../lol/hyper/toolstats/tools/TokenData.java | 8 ++ .../hyper/toolstats/tools/ToolStatsKeys.java | 7 + src/main/resources/config.yml | 15 ++- 11 files changed, 301 insertions(+), 9 deletions(-) diff --git a/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java b/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java index 90998b9..3b8b258 100644 --- a/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java +++ b/src/main/java/lol/hyper/toolstats/commands/CommandToolStats.java @@ -423,6 +423,14 @@ public class CommandToolStats implements BasicCommand { } } } + if (toolStats.config.getBoolean("enabled.logs-stripped")) { + if (container.has(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER)) { + Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER); + if (logsStripped != null) { + lore.add(toolStats.configTools.formatLore("logs-stripped", "{logs}", toolStats.numberFormat.formatInt(logsStripped))); + } + } + } finalMeta.lore(lore); finalItem.setItemMeta(finalMeta); int slot = player.getInventory().getHeldItemSlot(); @@ -878,6 +886,36 @@ public class CommandToolStats implements BasicCommand { } break; } + case "logs-stripped": { + if (!toolStats.config.getBoolean("enabled.logs-stripped")) { + player.sendMessage(Component.text("This stat is disabled.", NamedTextColor.RED)); + return; + } + if (container.has(toolStats.toolStatsKeys.getLogsStripped())) { + 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.toolStatsKeys.getLogsStripped(), 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.updateLogsStripped(editedItem, difference); + updated = true; + } 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; @@ -1271,6 +1309,34 @@ public class CommandToolStats implements BasicCommand { } break; } + case "logs-stripped": { + if (container.has(toolStats.toolStatsKeys.getLogsStripped())) { + Integer statValue = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER); + if (statValue == null) { + player.sendMessage(Component.text("Unable to get stat from item.", NamedTextColor.RED)); + return; + } + String tokens = container.get(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING); + if (tokens == null) { + player.sendMessage(Component.text("Unable to get tokens from item.", NamedTextColor.RED)); + return; + } + container.remove(toolStats.toolStatsKeys.getLogsStripped()); + List newTokens = toolStats.itemChecker.removeToken(tokens, "logs-stripped"); + if (newTokens.isEmpty()) { + container.remove(toolStats.toolStatsKeys.getTokenApplied()); + } else { + container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, String.join(",", newTokens)); + } + + Component oldLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", 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 d9fa4a8..edf9ebe 100755 --- a/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java +++ b/src/main/java/lol/hyper/toolstats/events/AnvilEvent.java @@ -139,6 +139,10 @@ public class AnvilEvent implements Listener { addToken(event, tokenType, "critical-strikes", clone); return; } + if (tokenType.equalsIgnoreCase("logs-stripped")) { + addToken(event, tokenType, "logs-stripped", clone); + return; + } } return; } @@ -363,6 +367,15 @@ public class AnvilEvent implements Listener { } break; } + case "logs-stripped": { + if (toolStats.config.getBoolean("enabled.logs-stripped")) { + newItem.setItemMeta(toolStats.itemLore.updateLogsStripped(newItem, 0)); + } else { + event.setResult(null); + return; + } + break; + } } event.setResult(newItem); event.getView().setRepairCost(toolStats.itemChecker.getCost(targetToken)); @@ -496,6 +509,14 @@ public class AnvilEvent implements Listener { meta = toolStats.itemLore.updateTridentThrows(finalItem, -tridentThrows); finalItem.setItemMeta(meta); } + if (container.has(toolStats.toolStatsKeys.getLogsStripped())) { + Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER); + if (logsStripped == null) { + return; + } + meta = toolStats.itemLore.updateLogsStripped(finalItem, -logsStripped); + finalItem.setItemMeta(meta); + } event.setResult(finalItem); event.getView().setRepairCost(toolStats.itemChecker.getCost("reset")); } diff --git a/src/main/java/lol/hyper/toolstats/events/BlockBreak.java b/src/main/java/lol/hyper/toolstats/events/BlockBreak.java index e4b3bf6..ae62b20 100755 --- a/src/main/java/lol/hyper/toolstats/events/BlockBreak.java +++ b/src/main/java/lol/hyper/toolstats/events/BlockBreak.java @@ -39,7 +39,7 @@ import java.util.Locale; public class BlockBreak implements Listener { private final ToolStats toolStats; - public List brokenContainers = new ArrayList<>(); + public final List brokenContainers = new ArrayList<>(); public BlockBreak(ToolStats toolStats) { this.toolStats = toolStats; diff --git a/src/main/java/lol/hyper/toolstats/events/EntityDamage.java b/src/main/java/lol/hyper/toolstats/events/EntityDamage.java index 2bc62ab..de92feb 100755 --- a/src/main/java/lol/hyper/toolstats/events/EntityDamage.java +++ b/src/main/java/lol/hyper/toolstats/events/EntityDamage.java @@ -34,7 +34,6 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.projectiles.ProjectileSource; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; public class EntityDamage implements Listener { diff --git a/src/main/java/lol/hyper/toolstats/events/GenerateLoot.java b/src/main/java/lol/hyper/toolstats/events/GenerateLoot.java index 8485180..d9c2919 100755 --- a/src/main/java/lol/hyper/toolstats/events/GenerateLoot.java +++ b/src/main/java/lol/hyper/toolstats/events/GenerateLoot.java @@ -39,8 +39,8 @@ import java.util.Map; public class GenerateLoot implements Listener { private final ToolStats toolStats; - public Map generatedInventory = new HashMap<>(); - public List droppedLootLocations = new ArrayList<>(); + public final Map generatedInventory = new HashMap<>(); + public final List droppedLootLocations = new ArrayList<>(); public GenerateLoot(ToolStats toolStats) { this.toolStats = toolStats; diff --git a/src/main/java/lol/hyper/toolstats/events/PlayerInteract.java b/src/main/java/lol/hyper/toolstats/events/PlayerInteract.java index 26cb8f8..b5e5473 100755 --- a/src/main/java/lol/hyper/toolstats/events/PlayerInteract.java +++ b/src/main/java/lol/hyper/toolstats/events/PlayerInteract.java @@ -33,9 +33,13 @@ import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class PlayerInteract implements Listener { @@ -74,7 +78,31 @@ public class PlayerInteract implements Listener { Inventory holderInventory = holder.getInventory(); openedChests.add(block); chestInventories.add(holderInventory); - Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedChests.remove(block), 20); + Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, _ -> openedChests.remove(block), 20); + } + // player right-clicked a log + String blockType = block.getType().toString().toLowerCase(Locale.ROOT); + if (blockType.endsWith("_log") && !blockType.contains("stripped")) { + PlayerInventory playerInventory = player.getInventory(); + ItemStack axe = toolStats.itemChecker.getAxe(playerInventory); + + // not holding an axe + if (axe == null) { + return; + } + + ItemMeta newAxe = toolStats.itemLore.updateLogsStripped(axe, 1); + if (newAxe != null) { + boolean isMain = playerInventory.getItemInMainHand().getType().toString().endsWith("_AXE"); + boolean isOffHand = playerInventory.getItemInOffHand().getType().toString().endsWith("_AXE"); + if (isMain && isOffHand) { + playerInventory.getItemInMainHand().setItemMeta(newAxe); + } else if (isMain) { + playerInventory.getItemInMainHand().setItemMeta(newAxe); + } else if (isOffHand) { + playerInventory.getItemInOffHand().setItemMeta(newAxe); + } + } } } @@ -91,7 +119,7 @@ public class PlayerInteract implements Listener { Inventory mineCartInventory = storageMinecart.getInventory(); mineCartChestInventories.add(mineCartInventory); openedMineCarts.add(storageMinecart); - Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, scheduledTask -> openedMineCarts.remove(storageMinecart), 20); + Bukkit.getGlobalRegionScheduler().runDelayed(toolStats, _ -> openedMineCarts.remove(storageMinecart), 20); } } } diff --git a/src/main/java/lol/hyper/toolstats/tools/ItemChecker.java b/src/main/java/lol/hyper/toolstats/tools/ItemChecker.java index e34c60a..f844b72 100755 --- a/src/main/java/lol/hyper/toolstats/tools/ItemChecker.java +++ b/src/main/java/lol/hyper/toolstats/tools/ItemChecker.java @@ -306,6 +306,35 @@ public class ItemChecker { return null; } + /** + * Get the player's axe. + * + * @param inventory Their inventory. + * @return Their axe, either main or offhand. + */ + public @Nullable ItemStack getAxe(PlayerInventory inventory) { + ItemStack main = inventory.getItemInMainHand(); + ItemStack offHand = inventory.getItemInOffHand(); + + boolean isMain = main.getType().toString().endsWith("_AXE"); + boolean isOffHand = offHand.getType().toString().endsWith("_AXE"); + + // if the player is holding an axe in their main hand, use that one + // if the axe is in their offhand instead, use that one after checking main hand + // Minecraft prioritizes main hand if the player holds in both hands + if (isMain && isOffHand) { + return main; + } + if (isMain) { + return main; + } + if (isOffHand) { + return offHand; + } + + return null; + } + /** * Checks the keys of the item and returns the tokens we should add. * If the server swaps token systems this should allow compatability. @@ -363,6 +392,9 @@ public class ItemChecker { if (container.has(toolStats.toolStatsKeys.getTridentThrows())) { tokens.add("trident-throws"); } + if (container.has(toolStats.toolStatsKeys.getLogsStripped())) { + tokens.add("logs-stripped"); + } if (tokens.isEmpty()) { return null; } diff --git a/src/main/java/lol/hyper/toolstats/tools/ItemLore.java b/src/main/java/lol/hyper/toolstats/tools/ItemLore.java index b802f8a..42e1860 100755 --- a/src/main/java/lol/hyper/toolstats/tools/ItemLore.java +++ b/src/main/java/lol/hyper/toolstats/tools/ItemLore.java @@ -1299,7 +1299,7 @@ public class ItemLore { if (criticalStrikes == null) { criticalStrikes = 0; - toolStats.logger.warn("{} does not have valid fish-caught set! Resting to zero. This should NEVER happen.", clone); + toolStats.logger.warn("{} does not have valid critical-strikes set! Resting to zero. This should NEVER happen.", clone); } container.set(toolStats.toolStatsKeys.getCriticalStrikes(), PersistentDataType.INTEGER, criticalStrikes + add); @@ -1397,7 +1397,7 @@ public class ItemLore { if (tridentThrows == null) { tridentThrows = 0; - toolStats.logger.warn("{} does not have valid fish-caught set! Resting to zero. This should NEVER happen.", clone); + toolStats.logger.warn("{} does not have valid trident-throws set! Resting to zero. This should NEVER happen.", clone); } container.set(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER, tridentThrows + add); @@ -1413,6 +1413,104 @@ public class ItemLore { return meta; } + /** + * Add x to logs stripped. + * + * @param axe The axe used. + */ + public ItemMeta updateLogsStripped(ItemStack axe, int add) { + ItemStack clone = axe.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.logs-stripped")) { + if (container.has(toolStats.toolStatsKeys.getLogsStripped())) { + Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER); + if (logsStripped == null) { + return null; + } + container.remove(toolStats.toolStatsKeys.getLogsStripped()); + // remove the applied token if this stat is disabled + if (container.has(toolStats.toolStatsKeys.getTokenApplied())) { + String appliedTokens = container.get(toolStats.toolStatsKeys.getTokenApplied(), 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, "logs-stripped"); + if (!newTokens.isEmpty()) { + container.set(toolStats.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, String.join(",", newTokens)); + } else { + container.remove(toolStats.toolStatsKeys.getTokenApplied()); + } + } + } + if (meta.hasLore()) { + String oldLogsStripped = toolStats.numberFormat.formatInt(logsStripped); + Component lineToRemove = toolStats.configTools.formatLore("logs-stripped", "{logs}", oldLogsStripped); + List newLore = removeLore(meta.lore(), lineToRemove); + meta.lore(newLore); + } + return meta; + } + return null; + } + + // check for tokens + boolean validToken = toolStats.itemChecker.checkTokens(container, "logs-stripped"); + // check for tokens + if (toolStats.config.getBoolean("tokens.enabled")) { + // if the item has stats but no token, add the token + if (container.has(toolStats.toolStatsKeys.getLogsStripped()) && !validToken) { + String newTokens = toolStats.itemChecker.addTokensToExisting(clone); + if (newTokens != null) { + container.set(toolStats.toolStatsKeys.getTokenApplied(), 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.toolStatsKeys.getTokenApplied(), PersistentDataType.STRING, newTokens); + } + } + } + + Integer logsStripped = 0; + if (container.has(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER)) { + logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER); + } + + if (logsStripped == null) { + logsStripped = 0; + toolStats.logger.warn("{} does not have valid logs-stripped set! Resting to zero. This should NEVER happen.", clone); + } + + container.set(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER, logsStripped + add); + String oldLogsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped); + String newLogsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped + add); + Component oldLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", oldLogsStrippedFormatted); + Component newLine = toolStats.configTools.formatLore("logs-stripped", "{logs}", newLogsStrippedFormatted); + if (oldLine == null || newLine == null) { + return null; + } + List newLore = updateItemLore(meta, oldLine, newLine); + meta.lore(newLore); + return meta; + } + /** * Format the item owner lore. * @@ -1667,6 +1765,26 @@ public class ItemLore { finalItem.setItemMeta(meta); } } + if (container.has(toolStats.toolStatsKeys.getTridentThrows())) { + Integer tridentThrows = container.get(toolStats.toolStatsKeys.getTridentThrows(), PersistentDataType.INTEGER); + if (tridentThrows != null) { + container.remove(toolStats.toolStatsKeys.getTridentThrows()); + String tridentThrowsFormatted = toolStats.numberFormat.formatInt(tridentThrows); + Component lineToRemove = toolStats.configTools.formatLore("trident-throws", "{times}", tridentThrowsFormatted); + meta.lore(removeLore(meta.lore(), lineToRemove)); + finalItem.setItemMeta(meta); + } + } + if (container.has(toolStats.toolStatsKeys.getLogsStripped())) { + Integer logsStripped = container.get(toolStats.toolStatsKeys.getLogsStripped(), PersistentDataType.INTEGER); + if (logsStripped != null) { + container.remove(toolStats.toolStatsKeys.getLogsStripped()); + String logsStrippedFormatted = toolStats.numberFormat.formatInt(logsStripped); + Component lineToRemove = toolStats.configTools.formatLore("logs-stripped", "{logs}", logsStrippedFormatted); + meta.lore(removeLore(meta.lore(), lineToRemove)); + finalItem.setItemMeta(meta); + } + } if (removeMeta) { Integer origin = null; if (container.has(toolStats.toolStatsKeys.getOriginType())) { diff --git a/src/main/java/lol/hyper/toolstats/tools/TokenData.java b/src/main/java/lol/hyper/toolstats/tools/TokenData.java index 0225b49..c98af34 100755 --- a/src/main/java/lol/hyper/toolstats/tools/TokenData.java +++ b/src/main/java/lol/hyper/toolstats/tools/TokenData.java @@ -153,6 +153,13 @@ public class TokenData { tridentThrowsRecipe.setIngredient('S', Material.PRISMARINE_SHARD); recipes.add(tridentThrowsRecipe); + NamespacedKey logsStrippedKey = new NamespacedKey(toolStats, "logs-stripped-token"); + ShapedRecipe logsStrippedRecipe = new ShapedRecipe(logsStrippedKey, createToken("logs-stripped")); + logsStrippedRecipe.shape(" P ", "PSP", " P "); + logsStrippedRecipe.setIngredient('P', Material.PAPER); + logsStrippedRecipe.setIngredient('S', Material.WOODEN_AXE); + recipes.add(logsStrippedRecipe); + tokenTypes.add("crops-mined"); tokenTypes.add("blocks-mined"); tokenTypes.add("damage-taken"); @@ -169,6 +176,7 @@ public class TokenData { tokenTypes.add("enderdragon-kills"); tokenTypes.add("critical-strikes"); tokenTypes.add("trident-throws"); + tokenTypes.add("logs-stripped"); } public Set getRecipes() { diff --git a/src/main/java/lol/hyper/toolstats/tools/ToolStatsKeys.java b/src/main/java/lol/hyper/toolstats/tools/ToolStatsKeys.java index cbeeebc..d9e65cb 100755 --- a/src/main/java/lol/hyper/toolstats/tools/ToolStatsKeys.java +++ b/src/main/java/lol/hyper/toolstats/tools/ToolStatsKeys.java @@ -37,6 +37,7 @@ public class ToolStatsKeys { private NamespacedKey criticalStrikes; private NamespacedKey tridentThrows; private NamespacedKey originType; + private NamespacedKey logsStripped; public void make() { itemOwner = new NamespacedKey(toolStats, "owner"); @@ -61,6 +62,7 @@ public class ToolStatsKeys { criticalStrikes = new NamespacedKey(toolStats, "critical-strikes"); tridentThrows = new NamespacedKey(toolStats, "trident-throws"); originType = new NamespacedKey(toolStats, "origin"); + logsStripped = new NamespacedKey(toolStats, "logs-stripped"); // save which stat can be used by a reset token tokenKeys.add(blocksMined); @@ -76,6 +78,7 @@ public class ToolStatsKeys { tokenKeys.add(enderDragonKills); tokenKeys.add(criticalStrikes); tokenKeys.add(tridentThrows); + tokenKeys.add(logsStripped); } public NamespacedKey getItemOwner() { @@ -162,6 +165,10 @@ public class ToolStatsKeys { return tridentThrows; } + public NamespacedKey getLogsStripped() { + return logsStripped; + } + /** * Stores how an item was created. * 0 = crafted. diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d4e5b17..b0fed26 100755 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -180,6 +180,17 @@ tokens: enabled: false type: float value: 1001 + logs-stripped: + title: "&7ToolStats: &8Logs Stripped Token" + lore: + - "&8Combine with an axe in an anvil to track logs stripped." + - "&8Uses &7{levels} &8level." + levels: 1 + material: PAPER + custom-model-data: + enabled: false + type: float + value: 1001 enabled: # Will show "Crafted by " @@ -349,6 +360,7 @@ enabled: crops-harvested: true critical-strikes: true trident-throws: true + logs-stripped: true messages: crafted: @@ -386,6 +398,7 @@ messages: damage-done: "&7Damage done: &8{damage}" critical-strikes: "&7Critical strikes: &8{strikes}" trident-throws: "&7Times thrown: &8{times}" + logs-stripped: "&7Logs stripped: &8{logs}" # Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html mobs: ZOMBIE: "Zombie" @@ -424,4 +437,4 @@ world-limit: - world_1 - world_2 -config-version: 15 \ No newline at end of file +config-version: 16 \ No newline at end of file