add critical strikes

This commit is contained in:
hyperdefined
2026-01-14 14:12:04 -05:00
parent 3087e3adcb
commit bd4ab50dc7
7 changed files with 239 additions and 5 deletions

View File

@@ -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.

View File

@@ -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<String> 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<Component> 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;

View File

@@ -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"));
}

View File

@@ -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);
}
}
}

View File

@@ -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<String> 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<Component> 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<Component> 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)) {

View File

@@ -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");

View File

@@ -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 <player>"
@@ -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"