From cdc87d6e92f5e3c6633f95ddfe3716dcc6c7431c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 04:26:43 +0000 Subject: [PATCH 1/3] Add MC 1.20.5+/1.21+ vanilla attribute support Adds Divinity stat types for the vanilla attributes introduced in recent Minecraft versions (scale, water/movement efficiency, sneaking speed, block/entity interaction range, block break speed, explosion knockback resistance, fall damage multiplier, flying speed, gravity, jump strength, max absorption, mining efficiency, oxygen bonus, safe fall distance, step height, submerged mining speed) and applies them as real Bukkit attribute modifiers (keyed by fixed UUIDs so they can be cleanly replaced on recalculation) during EntityStats#updateAll. Also relocates item_stats/stats.yml to item_stats/stats/general_stats.yml (a stats/ subdirectory) and bakes the previously code-injected ARMOR_TOUGHNESS defaults directly into the shipped config, since the new vanilla-attribute stat entries live alongside it in the same file. --- .../magemonkey/divinity/config/Config.java | 10 +- .../divinity/stats/EntityStats.java | 166 ++++++++ .../stats/items/attributes/api/TypedStat.java | 21 + src/main/resources/item_stats/stats.yml | 191 --------- .../item_stats/stats/general_stats.yml | 402 ++++++++++++++++++ 5 files changed, 591 insertions(+), 199 deletions(-) delete mode 100644 src/main/resources/item_stats/stats.yml create mode 100644 src/main/resources/item_stats/stats/general_stats.yml diff --git a/src/main/java/studio/magemonkey/divinity/config/Config.java b/src/main/java/studio/magemonkey/divinity/config/Config.java index f2096441..8441dfef 100644 --- a/src/main/java/studio/magemonkey/divinity/config/Config.java +++ b/src/main/java/studio/magemonkey/divinity/config/Config.java @@ -174,20 +174,14 @@ private void setupDefense() { private void setupStats() { JYML cfg; try { - cfg = JYML.loadOrExtract(plugin, "/item_stats/stats.yml"); + cfg = JYML.loadOrExtract(plugin, "/item_stats/stats/general_stats.yml"); } catch (InvalidConfigurationException e) { this.plugin.error("Failed to load stats config (" + this.plugin.getName() - + "/item_stats/stats.yml): Configuration error"); + + "/item_stats/stats/general_stats.yml): Configuration error"); e.printStackTrace(); return; } - cfg.addMissing("ARMOR_TOUGHNESS.enabled", true); - cfg.addMissing("ARMOR_TOUGHNESS.name", "Armor Toughness"); - cfg.addMissing("ARMOR_TOUGHNESS.format", "&9▸ %name%: &f%value% %condition%"); - cfg.addMissing("ARMOR_TOUGHNESS.capacity", 100.0); - cfg.save(); - for (SimpleStat.Type statType : TypedStat.Type.values()) { String path2 = statType.name() + "."; if (!cfg.getBoolean(path2 + "enabled")) { diff --git a/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java b/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java index 5320a32a..fbc40879 100644 --- a/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java +++ b/src/main/java/studio/magemonkey/divinity/stats/EntityStats.java @@ -2,6 +2,7 @@ import lombok.Getter; import org.bukkit.Material; +import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeModifier; import org.bukkit.block.Biome; @@ -575,6 +576,171 @@ private void updateBonusAttributes() { this.applyBonusAttribute(nbt, value); } + + // MC 1.21+ vanilla attributes — handled separately, not in NBTAttribute enum + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("SCALE"); + if (attr != null) this.applyScaleAttribute(attr, calcVanillaStatValue(TypedStat.Type.SCALE)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("WATER_MOVEMENT_EFFICIENCY"); + if (attr != null) applyVanillaAttributeModifier(attr, WATER_MOV_EFF_MODIFIER_UUID, + "divinity.water_movement_efficiency", calcVanillaStatValue(TypedStat.Type.WATER_MOVEMENT_EFFICIENCY)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("MOVEMENT_EFFICIENCY"); + if (attr != null) applyVanillaAttributeModifier(attr, MOV_EFF_MODIFIER_UUID, + "divinity.movement_efficiency", calcVanillaStatValue(TypedStat.Type.MOVEMENT_EFFICIENCY)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("SNEAKING_SPEED"); + if (attr != null) applyVanillaAttributeModifier(attr, SNEAK_SPEED_MODIFIER_UUID, + "divinity.sneaking_speed", calcVanillaStatValue(TypedStat.Type.SNEAKING_SPEED)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("BLOCK_BREAK_SPEED"); + if (attr != null) applyVanillaAttributeModifier(attr, BLOCK_BREAK_SPEED_UUID, + "divinity.block_break_speed", calcVanillaStatValue(TypedStat.Type.BLOCK_BREAK_SPEED)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("BLOCK_INTERACTION_RANGE"); + if (attr != null) applyVanillaAttributeModifier(attr, BLOCK_INTERACT_RANGE_UUID, + "divinity.block_interaction_range", calcVanillaStatValue(TypedStat.Type.BLOCK_INTERACTION_RANGE)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("ENTITY_INTERACTION_RANGE"); + if (attr != null) applyVanillaAttributeModifier(attr, ENTITY_INTERACT_RANGE_UUID, + "divinity.entity_interaction_range", calcVanillaStatValue(TypedStat.Type.ENTITY_INTERACTION_RANGE)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("EXPLOSION_KNOCKBACK_RESISTANCE"); + if (attr != null) applyVanillaAttributeModifier(attr, EXPLOSION_KB_RES_UUID, + "divinity.explosion_knockback_resistance", calcVanillaStatValue(TypedStat.Type.EXPLOSION_KNOCKBACK_RESISTANCE)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("FALL_DAMAGE_MULTIPLIER"); + if (attr != null) applyVanillaAttributeModifier(attr, FALL_DAMAGE_MULT_UUID, + "divinity.fall_damage_multiplier", calcVanillaStatValue(TypedStat.Type.FALL_DAMAGE_MULTIPLIER)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("FLYING_SPEED"); + if (attr != null) applyVanillaAttributeModifier(attr, FLYING_SPEED_UUID, + "divinity.flying_speed", calcVanillaStatValue(TypedStat.Type.FLYING_SPEED)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("GRAVITY"); + if (attr != null) applyVanillaAttributeModifier(attr, GRAVITY_UUID, + "divinity.gravity", calcVanillaStatValue(TypedStat.Type.GRAVITY)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("JUMP_STRENGTH"); + if (attr != null) applyVanillaAttributeModifier(attr, JUMP_STRENGTH_UUID, + "divinity.jump_strength", calcVanillaStatValue(TypedStat.Type.JUMP_STRENGTH)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("MAX_ABSORPTION"); + if (attr != null) applyVanillaAttributeModifier(attr, MAX_ABSORPTION_UUID, + "divinity.max_absorption", calcVanillaStatValue(TypedStat.Type.MAX_ABSORPTION)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("MINING_EFFICIENCY"); + if (attr != null) applyVanillaAttributeModifier(attr, MINING_EFFICIENCY_UUID, + "divinity.mining_efficiency", calcVanillaStatValue(TypedStat.Type.MINING_EFFICIENCY)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("OXYGEN_BONUS"); + if (attr != null) applyVanillaAttributeModifier(attr, OXYGEN_BONUS_UUID, + "divinity.oxygen_bonus", calcVanillaStatValue(TypedStat.Type.OXYGEN_BONUS)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("SAFE_FALL_DISTANCE"); + if (attr != null) applyVanillaAttributeModifier(attr, SAFE_FALL_DISTANCE_UUID, + "divinity.safe_fall_distance", calcVanillaStatValue(TypedStat.Type.SAFE_FALL_DISTANCE)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("STEP_HEIGHT"); + if (attr != null) applyVanillaAttributeModifier(attr, STEP_HEIGHT_UUID, + "divinity.step_height", calcVanillaStatValue(TypedStat.Type.STEP_HEIGHT)); + } catch (Exception ignored) {} + + try { + Attribute attr = (Attribute) VersionManager.getNms().getAttribute("SUBMERGED_MINING_SPEED"); + if (attr != null) applyVanillaAttributeModifier(attr, SUBMERGED_MINING_SPEED_UUID, + "divinity.submerged_mining_speed", calcVanillaStatValue(TypedStat.Type.SUBMERGED_MINING_SPEED)); + } catch (Exception ignored) {} + } + + private static final UUID SCALE_MODIFIER_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000001"); + private static final UUID WATER_MOV_EFF_MODIFIER_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000002"); + private static final UUID MOV_EFF_MODIFIER_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000003"); + private static final UUID SNEAK_SPEED_MODIFIER_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000004"); + private static final UUID BLOCK_BREAK_SPEED_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000005"); + private static final UUID BLOCK_INTERACT_RANGE_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000006"); + private static final UUID ENTITY_INTERACT_RANGE_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000007"); + private static final UUID EXPLOSION_KB_RES_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000008"); + private static final UUID FALL_DAMAGE_MULT_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000009"); + private static final UUID FLYING_SPEED_UUID = UUID.fromString("d141e000-5ca1-4000-0000-00000000000a"); + private static final UUID GRAVITY_UUID = UUID.fromString("d141e000-5ca1-4000-0000-00000000000b"); + private static final UUID JUMP_STRENGTH_UUID = UUID.fromString("d141e000-5ca1-4000-0000-00000000000c"); + private static final UUID MAX_ABSORPTION_UUID = UUID.fromString("d141e000-5ca1-4000-0000-00000000000d"); + private static final UUID MINING_EFFICIENCY_UUID = UUID.fromString("d141e000-5ca1-4000-0000-00000000000e"); + private static final UUID OXYGEN_BONUS_UUID = UUID.fromString("d141e000-5ca1-4000-0000-00000000000f"); + private static final UUID SAFE_FALL_DISTANCE_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000010"); + private static final UUID STEP_HEIGHT_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000011"); + private static final UUID SUBMERGED_MINING_SPEED_UUID = UUID.fromString("d141e000-5ca1-4000-0000-000000000012"); + + @SuppressWarnings("deprecation") + private void applyVanillaAttributeModifier(@NotNull Attribute attr, @NotNull UUID modUuid, @NotNull String modName, double value) { + AttributeInstance attInst = this.entity.getAttribute(attr); + if (attInst == null) return; + for (AttributeModifier mod : new HashSet<>(attInst.getModifiers())) { + try { + if (modUuid.equals(mod.getUniqueId())) { + if (mod.getAmount() == value) return; + attInst.removeModifier(mod); + break; + } + } catch (Exception ignored) {} + } + if (value == 0D) return; + attInst.addModifier(new AttributeModifier(modUuid, modName, value, Operation.ADD_NUMBER)); + } + + @SuppressWarnings("deprecation") + private void applyScaleAttribute(@NotNull Attribute scaleAttr, double value) { + applyVanillaAttributeModifier(scaleAttr, SCALE_MODIFIER_UUID, "divinity.scale", value); + } + + private double calcVanillaStatValue(@NotNull TypedStat.Type type) { + TypedStat typedStat = ItemStats.getStat(type); + if (!(typedStat instanceof SimpleStat)) return 0D; + SimpleStat ss = (SimpleStat) typedStat; + List> bonuses = new ArrayList<>(); + for (ItemStack item : this.getEquipment()) { + if (item == null || item.getType().isAir()) continue; + bonuses.addAll(ss.get(item, player)); + } + bonuses.addAll(this.getBonuses(ss)); + double value = BonusCalculator.SIMPLE_FULL.apply(0D, bonuses); + value = this.getEffectBonus(ss, false).applyAsDouble(value); + if (ss.getCapability() >= 0 && value > ss.getCapability()) value = ss.getCapability(); + return value / 100D; } private void applyBonusAttribute(@NotNull NBTAttribute att, double value) { diff --git a/src/main/java/studio/magemonkey/divinity/stats/items/attributes/api/TypedStat.java b/src/main/java/studio/magemonkey/divinity/stats/items/attributes/api/TypedStat.java index a611f3d3..3248fc95 100644 --- a/src/main/java/studio/magemonkey/divinity/stats/items/attributes/api/TypedStat.java +++ b/src/main/java/studio/magemonkey/divinity/stats/items/attributes/api/TypedStat.java @@ -79,6 +79,27 @@ enum Type { THORNMAIL(SimpleStat.ItemType.ARMOR, true, false, true), HEALTH_REGEN(SimpleStat.ItemType.BOTH, true, true, true), MANA_REGEN(SimpleStat.ItemType.BOTH, true, true, true), + // Entity size (maps to generic.scale Bukkit attribute) + SCALE(SimpleStat.ItemType.BOTH, false, true, true), + // MC 1.21+ vanilla attributes + WATER_MOVEMENT_EFFICIENCY(SimpleStat.ItemType.BOTH, true, false, true), + MOVEMENT_EFFICIENCY(SimpleStat.ItemType.BOTH, true, false, true), + SNEAKING_SPEED(SimpleStat.ItemType.BOTH, true, false, true), + // MC 1.20.5+ vanilla attributes + BLOCK_BREAK_SPEED(SimpleStat.ItemType.BOTH, true, false, true), + BLOCK_INTERACTION_RANGE(SimpleStat.ItemType.BOTH, false, true, true), + ENTITY_INTERACTION_RANGE(SimpleStat.ItemType.BOTH, false, true, true), + EXPLOSION_KNOCKBACK_RESISTANCE(SimpleStat.ItemType.BOTH, true, false, true), + FALL_DAMAGE_MULTIPLIER(SimpleStat.ItemType.BOTH, true, true, true), + FLYING_SPEED(SimpleStat.ItemType.BOTH, false, false, true), + GRAVITY(SimpleStat.ItemType.BOTH, false, true, true), + JUMP_STRENGTH(SimpleStat.ItemType.BOTH, false, false, true), + MAX_ABSORPTION(SimpleStat.ItemType.BOTH, false, false, true), + MINING_EFFICIENCY(SimpleStat.ItemType.BOTH, false, false, true), + OXYGEN_BONUS(SimpleStat.ItemType.BOTH, false, false, true), + SAFE_FALL_DISTANCE(SimpleStat.ItemType.BOTH, false, false, true), + STEP_HEIGHT(SimpleStat.ItemType.BOTH, false, false, true), + SUBMERGED_MINING_SPEED(SimpleStat.ItemType.BOTH, true, false, true), ; private final SimpleStat.ItemType type; diff --git a/src/main/resources/item_stats/stats.yml b/src/main/resources/item_stats/stats.yml deleted file mode 100644 index bdeaa360..00000000 --- a/src/main/resources/item_stats/stats.yml +++ /dev/null @@ -1,191 +0,0 @@ -#DIRECT_DAMAGE: -# enabled: true -# name: Direct Damage -# format: '&f▸ %name%: &f%value%' -# capacity: 100.0 - -# Vanilla Stats -ARMOR: - enabled: true - name: Armor - format: '&3▸ %name%: &f%value% %condition%' - capacity: -1 - -ARMOR_TOUGHNESS: - enabled: true - name: Armor Toughness - format: '&9▸ %name%: &f%value% %condition%' - capacity: 100.0 - -BASE_ATTACK_SPEED: - enabled: true - name: Base Attack Speed - format: '&e▸ %name%: &f%value% %condition%' - capacity: -1 - -ATTACK_SPEED: - enabled: true - name: Attack Speed - format: '&e▸ %name%: &f%value% %condition%' - capacity: -1 - -KNOCKBACK_RESISTANCE: - enabled: true - name: Knockback Resistance - format: '&e▸ %name%: &f%value% %condition%' - capacity: -1 - -MOVEMENT_SPEED: - enabled: true - name: Movement Speed - format: '&e▸ %name%: &f%value% %condition%' - capacity: 70.0 - -MAX_HEALTH: - enabled: true - name: Max Health - format: '&c▸ %name%: &f%value% %condition%' - capacity: -1 - -# Custom Stats -AOE_DAMAGE: - enabled: true - name: AoE Damage - format: '&3▸ %name%: &f%value% %condition%' - capacity: -1 - -PVP_DAMAGE: - enabled: true - name: PvP Damage - format: '&b▸ %name%: &f%value% %condition%' - capacity: 200.0 - -PVE_DAMAGE: - enabled: true - name: PvE Damage - format: '&b▸ %name%: &f%value% %condition%' - capacity: 200.0 - -DODGE_RATE: - enabled: true - name: Dodge Rate - format: '&6▸ %name%: &f%value% %condition%' - capacity: 45.0 - -ACCURACY_RATE: - enabled: true - name: Accuracy Rate - format: '&6▸ %name%: &f%value% %condition%' - capacity: 30.0 - -BLOCK_RATE: - enabled: true - name: Block Rate - format: '&6▸ %name%: &f%value% %condition%' - capacity: 100.0 - -BLOCK_DAMAGE: - enabled: true - name: Block Damage - format: '&6▸ %name%: &f%value% %condition%' - capacity: 100.0 - -CRITICAL_RATE: - enabled: true - name: Crit. Strike Rate - format: '&a▸ %name%: &f%value% %condition%' - capacity: 100.0 - -CRITICAL_DAMAGE: - enabled: true - name: Crit. Strike Dmg - format: '&a▸ %name%: &f%value% %condition%' - capacity: 3.5 - -PVP_DEFENSE: - enabled: true - name: PvP Defense - format: '&b▸ %name%: &f%value% %condition%' - capacity: 100.0 - -PVE_DEFENSE: - enabled: true - name: PvE Defense - format: '&b▸ %name%: &f%value% %condition%' - capacity: 100.0 - -LOOT_RATE: - enabled: true - name: Loot Rate - format: '&e▸ %name%: &f%value% %condition%' - capacity: 250.0 - -DURABILITY: - enabled: true - name: Durability - format: '&7▸ %name%: &f%value%' - capacity: -1 - -PENETRATION: - enabled: true - name: Armor Penetration - format: '&c▸ %name%: &f%value% %condition%' - capacity: 60.0 - -VAMPIRISM: - enabled: true - name: Vampirism - format: '&c▸ %name%: &f%value% %condition%' - capacity: 35.0 - -BURN_RATE: - enabled: true - name: Chance to Burn - format: '&c▸ %name%: &f%value% %condition%' - capacity: 100.0 - -SALE_PRICE: - enabled: true - name: Sale Price - format: '&d▸ %name%: &f%value% %condition%' - capacity: -1 - -BLEED_RATE: - enabled: true - name: Chance to Open Wounds - format: '&c▸ %name%: &f%value% %condition%' - capacity: 75.0 - settings: - damage: '%damage% * 0.5' - of-max-health: false - duration: 10.0 - -DISARM_RATE: - enabled: true - name: Chance to Disarm - format: '&c▸ %name%: &f%value% %condition%' - capacity: 25.0 - -RANGE: - enabled: true - name: Range - format: '&6▸ %name%: &f%value% %condition%' - capacity: 7.0 - -THORNMAIL: - enabled: true - name: Thornmail - format: '&c▸ %name%: &f%value% %condition%' - capacity: 35.0 - -HEALTH_REGEN: - enabled: true - name: Health Regen - format: '&c▸ %name%: &f%value% %condition%' - capacity: -1.0 - -MANA_REGEN: - enabled: true - name: Mana Regen - format: '&9▸ %name%: &f%value% %condition%' - capacity: -1.0 \ No newline at end of file diff --git a/src/main/resources/item_stats/stats/general_stats.yml b/src/main/resources/item_stats/stats/general_stats.yml new file mode 100644 index 00000000..79c60532 --- /dev/null +++ b/src/main/resources/item_stats/stats/general_stats.yml @@ -0,0 +1,402 @@ +#DIRECT_DAMAGE: +# enabled: true +# name: Direct Damage +# format: '&f▸ %name%: &f%value%' +# capacity: 100.0 + +# Vanilla Stats +ARMOR: + enabled: true + name: Armor + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +ARMOR_TOUGHNESS: + enabled: true + name: Armor Toughness + format: '&9▸ %name%: &f%value% %condition%' + capacity: 100.0 + +BASE_ATTACK_SPEED: + enabled: true + name: Base Attack Speed + format: '&e▸ %name%: &f%value% %condition%' + capacity: -1 + +ATTACK_SPEED: + enabled: true + name: Attack Speed + format: '&e▸ %name%: &f%value% %condition%' + capacity: -1 + +KNOCKBACK_RESISTANCE: + enabled: true + name: Knockback Resistance + format: '&e▸ %name%: &f%value% %condition%' + capacity: -1 + +MOVEMENT_SPEED: + enabled: true + name: Movement Speed + format: '&e▸ %name%: &f%value% %condition%' + capacity: 70.0 + +MAX_HEALTH: + enabled: true + name: Max Health + format: '&c▸ %name%: &f%value% %condition%' + capacity: -1 +BLOCK_BREAK_SPEED: + enabled: true + name: Block Break Speed + format: '&3▸ %name%: &f%value% %condition%' + capacity: 200.0 + +BLOCK_INTERACTION_RANGE: + enabled: true + name: Block Reach + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +SCALE: + enabled: true + name: Scale + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +WATER_MOVEMENT_EFFICIENCY: + enabled: true + name: Water Movement Efficiency + format: '&3▸ %name%: &f%value% %condition%' + capacity: 100.0 + +MOVEMENT_EFFICIENCY: + enabled: true + name: Movement Efficiency + format: '&3▸ %name%: &f%value% %condition%' + capacity: 100.0 + +SNEAKING_SPEED: + enabled: true + name: Sneaking Speed + format: '&3▸ %name%: &f%value% %condition%' + capacity: 70.0 + +ENTITY_INTERACTION_RANGE: + enabled: true + name: Entity Reach + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +EXPLOSION_KNOCKBACK_RESISTANCE: + enabled: true + name: Explosion KB Resistance + format: '&3▸ %name%: &f%value% %condition%' + capacity: 100.0 + +FALL_DAMAGE_MULTIPLIER: + enabled: true + name: Fall Damage Modifier + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +FLYING_SPEED: + enabled: true + name: Flying Speed + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +GRAVITY: + enabled: true + name: Gravity + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +JUMP_STRENGTH: + enabled: true + name: Jump Strength + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +MAX_ABSORPTION: + enabled: true + name: Max Absorption + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +MINING_EFFICIENCY: + enabled: true + name: Mining Efficiency + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +OXYGEN_BONUS: + enabled: true + name: Oxygen Bonus + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +SAFE_FALL_DISTANCE: + enabled: true + name: Safe Fall Distance + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +STEP_HEIGHT: + enabled: true + name: Step Height + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +SUBMERGED_MINING_SPEED: + enabled: true + name: Submerged Mining Speed + format: '&3▸ %name%: &f%value% %condition%' + capacity: 100.0 +# Custom Stats + +MAX_MANA: + enabled: true + name: Max Mana + format: '&9▸ %name%: &f%value% %condition%' + capacity: -1 + +AOE_DAMAGE: + enabled: true + name: AoE Damage + format: '&3▸ %name%: &f%value% %condition%' + capacity: -1 + +PVP_DAMAGE: + enabled: true + name: PvP Damage + format: '&b▸ %name%: &f%value% %condition%' + capacity: 200.0 + +PVE_DAMAGE: + enabled: true + name: PvE Damage + format: '&b▸ %name%: &f%value% %condition%' + capacity: 200.0 + +DODGE_RATE: + enabled: true + name: Dodge Rate + format: '&6▸ %name%: &f%value% %condition%' + capacity: 45.0 + +ACCURACY_RATE: + enabled: true + name: Accuracy Rate + format: '&6▸ %name%: &f%value% %condition%' + capacity: 30.0 + +BLOCK_RATE: + enabled: true + name: Block Rate + format: '&6▸ %name%: &f%value% %condition%' + capacity: 100.0 + +BLOCK_DAMAGE: + enabled: true + name: Block Damage + format: '&6▸ %name%: &f%value% %condition%' + capacity: 100.0 + +CRITICAL_RATE: + enabled: true + name: Crit. Strike Rate + format: '&a▸ %name%: &f%value% %condition%' + capacity: 100.0 + +CRITICAL_DAMAGE: + enabled: true + name: Crit. Strike Dmg + format: '&a▸ %name%: &f%value% %condition%' + capacity: 3.5 + +SKILL_CRITICAL_RATE: + enabled: true + name: Skill Crit. Rate + format: '&a▸ %name%: &f%value% %condition%' + capacity: 100.0 + +SKILL_CRITICAL_DAMAGE: + enabled: true + name: Skill Crit. Dmg + format: '&a▸ %name%: &f%value% %condition%' + capacity: 3.5 + +PVP_DEFENSE: + enabled: true + name: PvP Defense + format: '&b▸ %name%: &f%value% %condition%' + capacity: 100.0 + +PVE_DEFENSE: + enabled: true + name: PvE Defense + format: '&b▸ %name%: &f%value% %condition%' + capacity: 100.0 + +LOOT_RATE: + enabled: true + name: Loot Rate + format: '&e▸ %name%: &f%value% %condition%' + capacity: 250.0 + +DURABILITY: + enabled: true + name: Durability + format: '&7▸ %name%: &f%value%' + capacity: -1 + +PENETRATION: + enabled: true + name: Armor Penetration + format: '&c▸ %name%: &f%value% %condition%' + capacity: 60.0 + +VAMPIRISM: + enabled: true + name: Vampirism + format: '&c▸ %name%: &f%value% %condition%' + capacity: 35.0 + +BURN_RATE: + enabled: true + name: Chance to Burn + format: '&c▸ %name%: &f%value% %condition%' + capacity: 100.0 + +SALE_PRICE: + enabled: true + name: Sale Price + format: '&d▸ %name%: &f%value% %condition%' + capacity: -1 + +BLEED_RATE: + enabled: true + name: Chance to Open Wounds + format: '&c▸ %name%: &f%value% %condition%' + capacity: 75.0 + settings: + damage: '%damage% * 0.5' + of-max-health: false + duration: 10.0 + +DISARM_RATE: + enabled: true + name: Chance to Disarm + format: '&c▸ %name%: &f%value% %condition%' + capacity: 25.0 + +THORNMAIL: + enabled: true + name: Thornmail + format: '&c▸ %name%: &f%value% %condition%' + capacity: 35.0 + +HEALTH_REGEN: + enabled: true + name: Health Regen + format: '&c▸ %name%: &f%value% %condition%' + capacity: -1.0 + +MANA_REGEN: + enabled: true + name: Mana Regen + format: '&9▸ %name%: &f%value% %condition%' + capacity: -1.0 + +# CC & Healing Stats +CC_RESISTANCE: + enabled: true + name: CC Resistance + format: '&e▸ %name%: &f%value% %condition%' + capacity: 60.0 + +CC_DURATION: + enabled: true + name: CC Duration + format: '&e▸ %name%: &f%value% %condition%' + capacity: -1.0 + +HEALING_CAST: + enabled: true + name: Healing Cast + format: '&a▸ %name%: &f%value% %condition%' + capacity: -1 + +HEALING_RECEIVED: + enabled: true + name: Healing Received + format: '&a▸ %name%: &f%value% %condition%' + capacity: -1 + +## PLACEHOLDERS (THEY DON'T WORK ON THEIR OWN) +SKILL_EFFECTIVNESS: + enabled: true + name: Skill Effectivness + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +SUMMON_POWER: + enabled: true + name: Summon power + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +SUMMON_HP: + enabled: true + name: Summon HP + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +SUMMON_DURATION: + enabled: true + name: Summon duration + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +BLEED_STACKS: + enabled: true + name: Bleed Stacks + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +BLEED_DURATION: + enabled: true + name: Bleed Duration + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +STUN_STACKS: + enabled: true + name: Stun Stacks + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +STUN_DURATION: + enabled: true + name: Stun Duration + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +PROJECTILE_COUNT: + enabled: true + name: Projectile Count + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +PROJECTILE_SPEED: + enabled: true + name: Projectile Speed + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 + +BLEED_DAMAGEBUFF: + enabled: true + name: Bleed base + format: '&b▸ %name%: &f%value% %condition%' + capacity: -1 From 0ec5f9c4e1f42c6ec9d209b45c16630712b881bc Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 04:10:20 +0000 Subject: [PATCH 2/3] Auto-migrate item_stats/stats.yml to the new general_stats.yml path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relocating stats.yml under item_stats/stats/ would otherwise orphan any server's customized file: on next start, JYML.loadOrExtract would find nothing at the new path and write a fresh, default-only general_stats.yml, silently discarding whatever names/formats/ capacities/enabled-toggles admins had tuned. migrateLegacyStatsFile() copies the old file to the new location before it's loaded, if the old file exists and the new one doesn't yet — a one-time, additive operation (the old file is left in place, nothing is deleted). Config then seeds the new MC 1.20.5+/1.21+ vanilla-attribute stat entries via addMissing so a migrated file still picks up the new attributes with their shipped defaults, without touching any entry the admin already customized. --- .../magemonkey/divinity/config/Config.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/main/java/studio/magemonkey/divinity/config/Config.java b/src/main/java/studio/magemonkey/divinity/config/Config.java index 8441dfef..fc46114f 100644 --- a/src/main/java/studio/magemonkey/divinity/config/Config.java +++ b/src/main/java/studio/magemonkey/divinity/config/Config.java @@ -171,7 +171,31 @@ private void setupDefense() { } } + /** + * item_stats/stats.yml was relocated to item_stats/stats/general_stats.yml. Servers upgrading + * with an already-customized stats.yml would otherwise have it orphaned and silently replaced + * by a fresh, default-only general_stats.yml. If the old file is still present and the new one + * hasn't been created yet, carry the old file's content forward so existing customizations + * (names, formats, capacities, enabled/disabled toggles) survive the upgrade. + */ + private void migrateLegacyStatsFile() { + java.io.File oldFile = new java.io.File(plugin.getDataFolder(), "item_stats/stats.yml"); + java.io.File newFile = new java.io.File(plugin.getDataFolder(), "item_stats/stats/general_stats.yml"); + if (!oldFile.exists() || newFile.exists()) return; + + try { + newFile.getParentFile().mkdirs(); + java.nio.file.Files.copy(oldFile.toPath(), newFile.toPath()); + this.plugin.info("Migrated item_stats/stats.yml to item_stats/stats/general_stats.yml"); + } catch (java.io.IOException e) { + this.plugin.error("Failed to migrate item_stats/stats.yml to the new " + + "item_stats/stats/general_stats.yml location: " + e.getMessage()); + } + } + private void setupStats() { + this.migrateLegacyStatsFile(); + JYML cfg; try { cfg = JYML.loadOrExtract(plugin, "/item_stats/stats/general_stats.yml"); @@ -182,6 +206,11 @@ private void setupStats() { return; } + // Seed the new MC 1.20.5+/1.21+ vanilla-attribute stats with their shipped defaults for + // servers carrying forward a pre-upgrade stats.yml that predates them, so upgrading + // servers get the new attributes working out of the box rather than silently disabled. + addMissingVanillaAttributeStatDefaults(cfg); + for (SimpleStat.Type statType : TypedStat.Type.values()) { String path2 = statType.name() + "."; if (!cfg.getBoolean(path2 + "enabled")) { @@ -208,6 +237,46 @@ private void setupStats() { } } + private void addMissingVanillaAttributeStatDefaults(@NotNull JYML cfg) { + addMissingStatDefault(cfg, "SCALE", "Scale", "&b▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "WATER_MOVEMENT_EFFICIENCY", "Water Movement Efficiency", + "&3▸ %name%: &f%value% %condition%", 100.0); + addMissingStatDefault(cfg, "MOVEMENT_EFFICIENCY", "Movement Efficiency", + "&3▸ %name%: &f%value% %condition%", 100.0); + addMissingStatDefault(cfg, "SNEAKING_SPEED", "Sneaking Speed", "&3▸ %name%: &f%value% %condition%", 70.0); + addMissingStatDefault(cfg, "BLOCK_BREAK_SPEED", "Block Break Speed", + "&3▸ %name%: &f%value% %condition%", 200.0); + addMissingStatDefault(cfg, "BLOCK_INTERACTION_RANGE", "Block Reach", + "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "ENTITY_INTERACTION_RANGE", "Entity Reach", + "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "EXPLOSION_KNOCKBACK_RESISTANCE", "Explosion KB Resistance", + "&3▸ %name%: &f%value% %condition%", 100.0); + addMissingStatDefault(cfg, "FALL_DAMAGE_MULTIPLIER", "Fall Damage Modifier", + "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "FLYING_SPEED", "Flying Speed", "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "GRAVITY", "Gravity", "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "JUMP_STRENGTH", "Jump Strength", "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "MAX_ABSORPTION", "Max Absorption", "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "MINING_EFFICIENCY", "Mining Efficiency", + "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "OXYGEN_BONUS", "Oxygen Bonus", "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "SAFE_FALL_DISTANCE", "Safe Fall Distance", + "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "STEP_HEIGHT", "Step Height", "&3▸ %name%: &f%value% %condition%", -1); + addMissingStatDefault(cfg, "SUBMERGED_MINING_SPEED", "Submerged Mining Speed", + "&3▸ %name%: &f%value% %condition%", 100.0); + cfg.saveChanges(); + } + + private void addMissingStatDefault( + @NotNull JYML cfg, @NotNull String path, @NotNull String name, @NotNull String format, double capacity) { + cfg.addMissing(path + ".enabled", true); + cfg.addMissing(path + ".name", name); + cfg.addMissing(path + ".format", format); + cfg.addMissing(path + ".capacity", capacity); + } + private void setupHand() { JYML cfg; try { From 726462ab482f14e08697feb9039d555074ee7f74 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Aug 2026 04:20:38 +0000 Subject: [PATCH 3/3] Trim general_stats.yml to keys this PR actually defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MAX_MANA, SKILL_CRITICAL_RATE/DAMAGE, CC_RESISTANCE/DURATION, HEALING_CAST/RECEIVED, SKILL_EFFECTIVNESS, SUMMON_*, BLEED_STACKS/ DURATION/DAMAGEBUFF, and STUN_STACKS/DURATION have no corresponding TypedStat.Type entry on this branch — those are added later by the stat-foundation and combat-formula-refactor PRs. Shipping them here was dead config with no effect; each downstream PR now owns adding its own keys via the same addMissing-seeding pattern this PR uses for the vanilla attributes. --- .../item_stats/stats/general_stats.yml | 110 ------------------ 1 file changed, 110 deletions(-) diff --git a/src/main/resources/item_stats/stats/general_stats.yml b/src/main/resources/item_stats/stats/general_stats.yml index 79c60532..ce91ebb2 100644 --- a/src/main/resources/item_stats/stats/general_stats.yml +++ b/src/main/resources/item_stats/stats/general_stats.yml @@ -155,12 +155,6 @@ SUBMERGED_MINING_SPEED: capacity: 100.0 # Custom Stats -MAX_MANA: - enabled: true - name: Max Mana - format: '&9▸ %name%: &f%value% %condition%' - capacity: -1 - AOE_DAMAGE: enabled: true name: AoE Damage @@ -215,18 +209,6 @@ CRITICAL_DAMAGE: format: '&a▸ %name%: &f%value% %condition%' capacity: 3.5 -SKILL_CRITICAL_RATE: - enabled: true - name: Skill Crit. Rate - format: '&a▸ %name%: &f%value% %condition%' - capacity: 100.0 - -SKILL_CRITICAL_DAMAGE: - enabled: true - name: Skill Crit. Dmg - format: '&a▸ %name%: &f%value% %condition%' - capacity: 3.5 - PVP_DEFENSE: enabled: true name: PvP Defense @@ -308,95 +290,3 @@ MANA_REGEN: name: Mana Regen format: '&9▸ %name%: &f%value% %condition%' capacity: -1.0 - -# CC & Healing Stats -CC_RESISTANCE: - enabled: true - name: CC Resistance - format: '&e▸ %name%: &f%value% %condition%' - capacity: 60.0 - -CC_DURATION: - enabled: true - name: CC Duration - format: '&e▸ %name%: &f%value% %condition%' - capacity: -1.0 - -HEALING_CAST: - enabled: true - name: Healing Cast - format: '&a▸ %name%: &f%value% %condition%' - capacity: -1 - -HEALING_RECEIVED: - enabled: true - name: Healing Received - format: '&a▸ %name%: &f%value% %condition%' - capacity: -1 - -## PLACEHOLDERS (THEY DON'T WORK ON THEIR OWN) -SKILL_EFFECTIVNESS: - enabled: true - name: Skill Effectivness - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -SUMMON_POWER: - enabled: true - name: Summon power - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -SUMMON_HP: - enabled: true - name: Summon HP - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -SUMMON_DURATION: - enabled: true - name: Summon duration - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -BLEED_STACKS: - enabled: true - name: Bleed Stacks - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -BLEED_DURATION: - enabled: true - name: Bleed Duration - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -STUN_STACKS: - enabled: true - name: Stun Stacks - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -STUN_DURATION: - enabled: true - name: Stun Duration - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -PROJECTILE_COUNT: - enabled: true - name: Projectile Count - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -PROJECTILE_SPEED: - enabled: true - name: Projectile Speed - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1 - -BLEED_DAMAGEBUFF: - enabled: true - name: Bleed base - format: '&b▸ %name%: &f%value% %condition%' - capacity: -1