Support for 1.21 attribute-based stats (9 new stats). Stat categories to differentiate between stats in the item browser.

This commit is contained in:
Jules 2024-10-21 16:17:42 +02:00
parent b50e6ed189
commit e3f4288fbd
58 changed files with 938 additions and 373 deletions

View File

@ -110,9 +110,9 @@ public class ItemStats {
UNBREAKABLE = new Unbreakable(), UNBREAKABLE = new Unbreakable(),
TIER = new ItemTierStat(), TIER = new ItemTierStat(),
SET = new ItemSetStat(), SET = new ItemSetStat(),
ARMOR = new DoubleStat("ARMOR", Material.GOLDEN_CHESTPLATE, "Armor", new String[]{"The armor given to the holder."}), ARMOR = new Armor(),
ARMOR_TOUGHNESS = new DoubleStat("ARMOR_TOUGHNESS", Material.DIAMOND_CHESTPLATE, "Armor Toughness", new String[]{"Armor toughness reduces damage taken."}), ARMOR_TOUGHNESS = new ArmorToughness(),
MAX_HEALTH = new DoubleStat("MAX_HEALTH", Material.GOLDEN_APPLE, "Max Health", new String[]{"The amount of health your", "item gives to the holder."}), MAX_HEALTH = new MaxHealth(),
UNSTACKABLE = new Unstackable(), UNSTACKABLE = new Unstackable(),
MAX_MANA = new DoubleStat("MAX_MANA", Material.LAPIS_LAZULI, "Max Mana", new String[]{"Adds mana to your max mana bar."}), MAX_MANA = new DoubleStat("MAX_MANA", Material.LAPIS_LAZULI, "Max Mana", new String[]{"Adds mana to your max mana bar."}),
KNOCKBACK_RESISTANCE = new KnockbackResistance(), KNOCKBACK_RESISTANCE = new KnockbackResistance(),
@ -133,6 +133,15 @@ public class ItemStats {
SAFE_FALL_DISTANCE = new SafeFallDistance(), SAFE_FALL_DISTANCE = new SafeFallDistance(),
SCALE = new Scale(), SCALE = new Scale(),
STEP_HEIGHT = new StepHeight(), STEP_HEIGHT = new StepHeight(),
BURNING_TIME = new BurningTime(),
EXPLOSION_KNOCKBACK_RESISTANCE = new ExplosionKnockbackResistance(),
MINING_EFFICIENCY = new MiningEfficiency(),
MOVEMENT_EFFICIENCY = new MovementEfficiency(),
OXYGEN_BONUS = new OxygenBonus(),
SNEAKING_SPEED = new SneakingSpeed(),
SUBMERGED_MINING_SPEED = new SubmergedMiningSpeed(),
SWEEPING_DAMAGE_RATIO = new SweepingDamageRatio(),
WATER_MOVEMENT_EFFICIENCY = new WaterMovementEfficiency(),
// Permanent Effects // Permanent Effects
PERM_EFFECTS = new PermanentEffects(), PERM_EFFECTS = new PermanentEffects(),

View File

@ -118,7 +118,7 @@ public class MMOItems extends MMOPlugin {
saveDefaultConfig(); saveDefaultConfig();
configManager = new ConfigManager(); configManager = new ConfigManager();
statManager.loadInternalStats(); statManager.loadBuiltins();
typeManager.reload(false); typeManager.reload(false);
templateManager.preloadObjects(); templateManager.preloadObjects();

View File

@ -5,6 +5,7 @@ import io.lumine.mythic.lib.api.item.NBTItem;
import io.lumine.mythic.lib.api.util.AltChar; import io.lumine.mythic.lib.api.util.AltChar;
import io.lumine.mythic.lib.api.util.ui.SilentNumbers; import io.lumine.mythic.lib.api.util.ui.SilentNumbers;
import io.lumine.mythic.lib.util.AdventureUtils; import io.lumine.mythic.lib.util.AdventureUtils;
import io.lumine.mythic.lib.version.VersionUtils;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.edition.NewItemEdition; import net.Indyuce.mmoitems.api.edition.NewItemEdition;
@ -13,6 +14,7 @@ import net.Indyuce.mmoitems.gui.edition.ItemEdition;
import net.Indyuce.mmoitems.stat.BrowserDisplayIDX; import net.Indyuce.mmoitems.stat.BrowserDisplayIDX;
import net.Indyuce.mmoitems.util.MMOUtils; import net.Indyuce.mmoitems.util.MMOUtils;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
@ -86,6 +88,7 @@ public class ItemBrowser extends PluginInventory {
final ItemStack item = currentType.getItem(); final ItemStack item = currentType.getItem();
item.setAmount(Math.max(1, Math.min(64, items))); item.setAmount(Math.max(1, Math.min(64, items)));
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
if (MythicLib.plugin.getVersion().isAbove(1, 20, 5)) VersionUtils.addEmptyAttributeModifier(meta);
AdventureUtils.setDisplayName(meta, String.format("&a%s&8 (click to browse)", currentType.getName())); AdventureUtils.setDisplayName(meta, String.format("&a%s&8 (click to browse)", currentType.getName()));
meta.addItemFlags(ItemFlag.values()); meta.addItemFlags(ItemFlag.values());
List<String> lore = new ArrayList<>(); List<String> lore = new ArrayList<>();

View File

@ -1,6 +1,7 @@
package net.Indyuce.mmoitems.gui.edition; package net.Indyuce.mmoitems.gui.edition;
import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.version.VersionUtils;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.stat.type.InternalStat; import net.Indyuce.mmoitems.stat.type.InternalStat;
@ -23,7 +24,7 @@ import java.util.stream.Collectors;
public class ItemEdition extends EditionInventory { public class ItemEdition extends EditionInventory {
private static final int[] slots = {19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43}; private static final int[] slots = {19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43};
private static final NamespacedKey STAT_ID_KEY = new NamespacedKey(MMOItems.plugin,"StatId"); private static final NamespacedKey STAT_ID_KEY = new NamespacedKey(MMOItems.plugin, "StatId");
public ItemEdition(Player player, MMOItemTemplate template) { public ItemEdition(Player player, MMOItemTemplate template) {
super(player, template); super(player, template);
@ -52,9 +53,14 @@ public class ItemEdition extends EditionInventory {
ItemStack item = new ItemStack(stat.getDisplayMaterial()); ItemStack item = new ItemStack(stat.getDisplayMaterial());
ItemMeta meta = item.getItemMeta(); ItemMeta meta = item.getItemMeta();
meta.addItemFlags(ItemFlag.values()); meta.addItemFlags(ItemFlag.values());
VersionUtils.addEmptyAttributeModifier(meta);
meta.setDisplayName(ChatColor.GREEN + stat.getName()); meta.setDisplayName(ChatColor.GREEN + stat.getName());
List<String> lore = MythicLib.plugin.parseColors(Arrays.stream(stat.getLore()).map(s -> ChatColor.GRAY + s).collect(Collectors.toList())); List<String> lore = MythicLib.plugin.parseColors(Arrays.stream(stat.getLore()).map(s -> ChatColor.GRAY + s).collect(Collectors.toList()));
lore.add(""); lore.add("");
if (stat.getCategory() != null) {
lore.add(0, "");
lore.add(0, ChatColor.BLUE + stat.getCategory().getLoreTag());
}
stat.whenDisplayed(lore, getEventualStatData(stat)); stat.whenDisplayed(lore, getEventualStatData(stat));

View File

@ -1,12 +1,15 @@
package net.Indyuce.mmoitems.manager; package net.Indyuce.mmoitems.manager;
import io.lumine.mythic.lib.MythicLib; import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.element.Element; import io.lumine.mythic.lib.element.Element;
import io.lumine.mythic.lib.util.annotation.BackwardsCompatibility; import io.lumine.mythic.lib.util.annotation.BackwardsCompatibility;
import net.Indyuce.mmoitems.ItemStats; import net.Indyuce.mmoitems.ItemStats;
import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.ConfigFile; import net.Indyuce.mmoitems.api.ConfigFile;
import net.Indyuce.mmoitems.api.Type; import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.category.StatCategory;
import net.Indyuce.mmoitems.stat.type.*; import net.Indyuce.mmoitems.stat.type.*;
import net.Indyuce.mmoitems.util.ElementStatType; import net.Indyuce.mmoitems.util.ElementStatType;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
@ -23,13 +26,14 @@ import java.util.logging.Level;
public class StatManager { public class StatManager {
private final Map<String, ItemStat<?, ?>> stats = new LinkedHashMap<>(); private final Map<String, ItemStat<?, ?>> stats = new LinkedHashMap<>();
private final Map<String, StatCategory> categories = new HashMap<>();
/** /**
* If, for whatever reason, a stat needs to change its internal * If, for whatever reason, a stat needs to change its internal
* string ID, this map keeps a reference for the deprecated old * string ID, this map keeps a reference for the deprecated old
* IDs while being separated from the main ItemStat map. * IDs while being separated from the main ItemStat map.
*/ */
@BackwardsCompatibility(version = "unknown") @BackwardsCompatibility(version = "not_specified")
private final Map<String, ItemStat<?, ?>> legacyAliases = new HashMap<>(); private final Map<String, ItemStat<?, ?>> legacyAliases = new HashMap<>();
/* /*
@ -37,8 +41,8 @@ public class StatManager {
* the first time to make their access easier. Check the classes * the first time to make their access easier. Check the classes
* individually to understand better * individually to understand better
*/ */
private final List<DoubleStat> numeric = new ArrayList<>(); private final List<DoubleStat> numericStats = new ArrayList<>();
private final List<ItemRestriction> itemRestriction = new ArrayList<>(); private final List<ItemRestriction> itemRestrictions = new ArrayList<>();
private final List<ConsumableItemInteraction> consumableActions = new ArrayList<>(); private final List<ConsumableItemInteraction> consumableActions = new ArrayList<>();
private final List<PlayerConsumable> playerConsumables = new ArrayList<>(); private final List<PlayerConsumable> playerConsumables = new ArrayList<>();
@ -46,10 +50,25 @@ public class StatManager {
* Load default stats using java reflection, get all public static final * Load default stats using java reflection, get all public static final
* fields in the ItemStat and register them as stat instances * fields in the ItemStat and register them as stat instances
*/ */
public void loadInternalStats() { public void loadBuiltins() {
// Builtin categories
for (Field field : StatCategory.class.getFields())
try {
if (Modifier.isStatic(field.getModifiers())
&& Modifier.isFinal(field.getModifiers())
&& field.get(null) instanceof StatCategory)
registerCategory((StatCategory) field.get(null));
} catch (IllegalArgumentException | IllegalAccessException exception) {
MMOItems.plugin.getLogger().log(Level.SEVERE, String.format("Couldn't register category called '%s'", field.getName()), exception.getMessage());
}
// Load builtin stats
for (Field field : ItemStats.class.getFields()) for (Field field : ItemStats.class.getFields())
try { try {
if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.get(null) instanceof ItemStat) if (Modifier.isStatic(field.getModifiers())
&& Modifier.isFinal(field.getModifiers())
&& field.get(null) instanceof ItemStat)
register((ItemStat<?, ?>) field.get(null)); register((ItemStat<?, ?>) field.get(null));
} catch (IllegalArgumentException | IllegalAccessException exception) { } catch (IllegalArgumentException | IllegalAccessException exception) {
MMOItems.plugin.getLogger().log(Level.SEVERE, String.format("Couldn't register stat called '%s'", field.getName()), exception.getMessage()); MMOItems.plugin.getLogger().log(Level.SEVERE, String.format("Couldn't register stat called '%s'", field.getName()), exception.getMessage());
@ -64,7 +83,7 @@ public class StatManager {
// Clean fictive numeric stats before // Clean fictive numeric stats before
if (cleanFirst) if (cleanFirst)
numeric.removeIf(stat -> stat instanceof FictiveNumericStat); // temporary fix, this is for elements. numericStats.removeIf(stat -> stat instanceof FakeElementalStat); // temporary fix, this is for elements TODO improve
// Register elemental stats // Register elemental stats
loadElements(); loadElements();
@ -100,7 +119,16 @@ public class StatManager {
public void loadElements() { public void loadElements() {
for (ElementStatType type : ElementStatType.values()) for (ElementStatType type : ElementStatType.values())
for (Element element : MythicLib.plugin.getElements().getAll()) for (Element element : MythicLib.plugin.getElements().getAll())
numeric.add(new FictiveNumericStat(element, type)); numericStats.add(new FakeElementalStat(element, type));
}
public void registerCategory(@NotNull StatCategory category) {
categories.put(category.getId(), category);
}
@NotNull
public StatCategory getCategory(@NotNull String id) {
return Objects.requireNonNull(categories.get(id), "No stat category found with ID '" + id + "'");
} }
@NotNull @NotNull
@ -116,7 +144,7 @@ public class StatManager {
*/ */
@NotNull @NotNull
public List<DoubleStat> getNumericStats() { public List<DoubleStat> getNumericStats() {
return numeric; return numericStats;
} }
/** /**
@ -125,7 +153,7 @@ public class StatManager {
*/ */
@NotNull @NotNull
public List<ItemRestriction> getItemRestrictionStats() { public List<ItemRestriction> getItemRestrictionStats() {
return itemRestriction; return itemRestrictions;
} }
/** /**
@ -158,7 +186,7 @@ public class StatManager {
if (stat != null) return stat; if (stat != null) return stat;
// Numeric registry (see to-do) // Numeric registry (see to-do)
stat = numeric.stream().filter(doubleStat -> doubleStat.getId().equals(id)).findFirst().orElse(null); stat = numericStats.stream().filter(doubleStat -> doubleStat.getId().equals(id)).findFirst().orElse(null);
if (stat != null) return stat; if (stat != null) return stat;
// Legacy liases // Legacy liases
@ -169,16 +197,6 @@ public class StatManager {
return null; return null;
} }
/**
* @deprecated Stat IDs are now stored in the stat instance directly.
* Please use StatManager#register(ItemStat) instead
*/
@Deprecated
@SuppressWarnings("unused")
public void register(String id, ItemStat<?, ?> stat) {
register(stat);
}
/** /**
* Registers a stat in MMOItems. It must be done right after MMOItems loads * Registers a stat in MMOItems. It must be done right after MMOItems loads
* before any manager is initialized because stats are commonly used when * before any manager is initialized because stats are commonly used when
@ -187,34 +205,30 @@ public class StatManager {
* @param stat The stat to register * @param stat The stat to register
*/ */
public void register(@NotNull ItemStat<?, ?> stat) { public void register(@NotNull ItemStat<?, ?> stat) {
register(stat, false);
}
private void register(@NotNull ItemStat<?, ?> stat, boolean customStat) {
// Skip disabled stats. // Skip disabled stats.
if (!stat.isEnabled()) return; if (!stat.isEnabled()) return;
// Safe check, this can happen with numerous extra RPG plugins // Register stat
if (stats.containsKey(stat.getId())) { stats.compute(stat.getId(), (id, current) -> {
MMOItems.plugin.getLogger().log(Level.SEVERE, "Could not register stat '" + stat.getId() + "' as a stat with the same ID already exists."); Validate.isTrue(current == null, "A stat with ID '" + id + "' already exists");
return; return stat;
} });
// Main registry // Register aliases (backwards compatibility)
stats.put(stat.getId(), stat); for (String alias : stat.getAliases()) legacyAliases.put(alias, stat);
// Register aliases
for (String alias : stat.getAliases())
legacyAliases.put(alias, stat);
// Use-case specific registries // Use-case specific registries
if (stat instanceof DoubleStat && !(stat instanceof GemStoneStat) && stat.isCompatible(Type.GEM_STONE)) if (stat instanceof DoubleStat && !(stat instanceof GemStoneStat) && stat.isCompatible(Type.GEM_STONE))
numeric.add((DoubleStat) stat); numericStats.add((DoubleStat) stat);
if (stat instanceof ItemRestriction) itemRestriction.add((ItemRestriction) stat); if (stat instanceof ItemRestriction) itemRestrictions.add((ItemRestriction) stat);
if (stat instanceof ConsumableItemInteraction) consumableActions.add((ConsumableItemInteraction) stat); if (stat instanceof ConsumableItemInteraction) consumableActions.add((ConsumableItemInteraction) stat);
if (stat instanceof PlayerConsumable) playerConsumables.add((PlayerConsumable) stat); if (stat instanceof PlayerConsumable) playerConsumables.add((PlayerConsumable) stat);
// Stat category
HasCategory statCatAnnot = stat.getClass().getAnnotation(HasCategory.class);
if (statCatAnnot != null) stat.setCategory(getCategory(UtilityMethods.enumName(statCatAnnot.cat())));
/* /*
* Cache stat for every type which may have this stat. Really important * Cache stat for every type which may have this stat. Really important
* otherwise the stat will NOT be used anywhere in the plugin. This * otherwise the stat will NOT be used anywhere in the plugin. This
@ -268,4 +282,22 @@ public class StatManager {
throw new RuntimeException("Unable to create a custom stat of type " + type, e); throw new RuntimeException("Unable to create a custom stat of type " + type, e);
} }
} }
/**
* @see #register(ItemStat)
* @deprecated Stat IDs are now stored in the stat instance directly.
*/
@Deprecated
@SuppressWarnings("unused")
public void register(@Nullable String id, @NotNull ItemStat<?, ?> stat) {
register(stat);
}
/**
* @see #loadBuiltins()
*/
@Deprecated
public void loadInternalStats() {
loadBuiltins();
}
} }

View File

@ -0,0 +1,15 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
public class Armor extends DoubleStat {
public Armor() {
super("ARMOR",
Material.GOLDEN_CHESTPLATE,
"Armor",
"The armor given to the holder.");
}
}

View File

@ -0,0 +1,15 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
public class ArmorToughness extends DoubleStat {
public ArmorToughness() {
super("ARMOR_TOUGHNESS",
Material.DIAMOND_CHESTPLATE,
"Armor Toughness",
"Armor toughness reduces damage taken.");
}
}

View File

@ -1,12 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.AttackWeaponStat; import net.Indyuce.mmoitems.stat.type.AttackWeaponStat;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
@HasCategory(cat = "vanilla_attribute")
public class AttackDamage extends AttackWeaponStat { public class AttackDamage extends AttackWeaponStat {
public AttackDamage() { public AttackDamage() {
super("ATTACK_DAMAGE", Material.IRON_SWORD, "Attack Damage", new String[] { "The amount of damage", "your weapon deals." }, super("ATTACK_DAMAGE",
Attribute.GENERIC_ATTACK_DAMAGE); Material.IRON_SWORD,
} "Attack Damage",
new String[]{"The amount of damage your weapon deals."},
Attribute.GENERIC_ATTACK_DAMAGE);
}
} }

View File

@ -1,12 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.AttackWeaponStat; import net.Indyuce.mmoitems.stat.type.AttackWeaponStat;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
@HasCategory(cat = "vanilla_attribute")
public class AttackSpeed extends AttackWeaponStat { public class AttackSpeed extends AttackWeaponStat {
public AttackSpeed() { public AttackSpeed() {
super("ATTACK_SPEED", Material.LIGHT_GRAY_DYE, "Attack Speed", super("ATTACK_SPEED",
new String[] { "The speed at which your weapon strikes.", "In attacks/sec." }, Attribute.GENERIC_ATTACK_SPEED); Material.LIGHT_GRAY_DYE,
} "Attack Speed",
new String[]{"The speed at which your weapon strikes.", "In attacks/sec."},
Attribute.GENERIC_ATTACK_SPEED);
}
} }

View File

@ -1,13 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class BlockBreakSpeed extends DoubleStat { public class BlockBreakSpeed extends DoubleStat {
public BlockBreakSpeed() { public BlockBreakSpeed() {
super("BLOCK_BREAK_SPEED", Material.IRON_PICKAXE, super("BLOCK_BREAK_SPEED",
"Mining Speed", new String[]{"Additional block breaking speed.", "Bare hands have a mining speed of 1"}); Material.IRON_PICKAXE,
"Mining Speed",
"Additional block breaking speed. Bare hands have a mining speed of 1");
} }
} }

View File

@ -1,13 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import io.lumine.mythic.lib.version.VMaterial; import io.lumine.mythic.lib.version.VMaterial;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class BlockInteractionRange extends DoubleStat { public class BlockInteractionRange extends DoubleStat {
public BlockInteractionRange() { public BlockInteractionRange() {
super("BLOCK_INTERACTION_RANGE", VMaterial.SPYGLASS.get(), super("BLOCK_INTERACTION_RANGE",
"Block Interaction Range", new String[]{"Additional range for breaking or interacting", "with blocks. Player's default is set to 5", "in creative mode, or 4.5 otherwise."}); VMaterial.SPYGLASS.get(),
"Block Interaction Range",
"Determines the maximum range the player can interact with blocks. Ranges between 0 and 64, with the default value being 4.5 in survival mode.");
} }
} }

View File

@ -5,6 +5,7 @@ import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate; import net.Indyuce.mmoitems.api.item.template.MMOItemTemplate;
import net.Indyuce.mmoitems.api.util.NumericStatFormula; import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material; import org.bukkit.Material;
@ -16,6 +17,7 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@HasCategory(cat = "template_option")
public class BrowserDisplayIDX extends DoubleStat { public class BrowserDisplayIDX extends DoubleStat {
public BrowserDisplayIDX() { public BrowserDisplayIDX() {
super("BROWSER_IDX", Material.GHAST_TEAR, "Browser Index", new String[] {"Used to display similar items together,", "neatly in the GUI \u00a7a/mmoitems browse", "", "Items with the same index are grouped."}, new String[0]); super("BROWSER_IDX", Material.GHAST_TEAR, "Browser Index", new String[] {"Used to display similar items together,", "neatly in the GUI \u00a7a/mmoitems browse", "", "Items with the same index are grouped."}, new String[0]);

View File

@ -0,0 +1,23 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.MMOUtils;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class BurningTime extends DoubleStat {
public BurningTime() {
super("BURNING_TIME",
Material.FIRE_CHARGE,
"Burning Time",
"A factor to how long the player remains on fire after being ignited. A factor of 0 removes the entire burn time, a factor of 1 lets the Entity burn the default fire time - larger values increase the amount of time the entity remains on fire. Default is 1, minimum is 0, maximum is 1024.");
}
@Override
public double multiplyWhenDisplaying() {
return 100;
}
}

View File

@ -24,7 +24,10 @@ import java.util.ArrayList;
public class DisplayName extends StringStat implements GemStoneStat { public class DisplayName extends StringStat implements GemStoneStat {
public DisplayName() { public DisplayName() {
super("NAME", Material.OAK_SIGN, "Display Name", new String[]{"The item display name."}, super("NAME",
Material.NAME_TAG,
"Display Name",
new String[]{"The item display name."},
new String[0]); new String[0]);
} }

View File

@ -1,13 +1,18 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import io.lumine.mythic.lib.version.VMaterial; import io.lumine.mythic.lib.version.VMaterial;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class EntityInteractionRange extends DoubleStat { public class EntityInteractionRange extends DoubleStat {
public EntityInteractionRange() { public EntityInteractionRange() {
super("ENTITY_INTERACTION_RANGE", VMaterial.SPYGLASS.get(), super("ENTITY_INTERACTION_RANGE",
"Entity Interaction Range", new String[]{"Additional range for damaging or interacting with entities.", "Player's default is set to 5 in creative,", "and 4.5 in survival."}); VMaterial.SPYGLASS.get(),
"Entity Interaction Range",
"Determines the maximum range the player can interact with entities. Ranges between 0 and 64, with the default value being 3."
);
} }
} }

View File

@ -0,0 +1,23 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.MMOUtils;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class ExplosionKnockbackResistance extends DoubleStat {
public ExplosionKnockbackResistance() {
super("EXPLOSION_KNOCKBACK_RESISTANCE",
Material.OBSIDIAN,
"Explosion Knockback Resistance",
"A factor to how much knockback an Entity takes from an Explosion. A factor of 1 removes the entire knockback, a factor of 0 means no knockback reduction.");
}
@Override
public double multiplyWhenDisplaying() {
return 100;
}
}

View File

@ -1,14 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class FallDamageMultiplier extends DoubleStat { public class FallDamageMultiplier extends DoubleStat {
public FallDamageMultiplier() { public FallDamageMultiplier() {
super("FALL_DAMAGE_MULTIPLIER", Material.DAMAGED_ANVIL, super("FALL_DAMAGE_MULTIPLIER",
"Fall Damage Multiplier", new String[]{"Increases fall damage by a certain %.", "Player's default is set to 100%"}); Material.DAMAGED_ANVIL,
"Fall Damage Multiplier",
"Multiply overall fall damage amount. Default is 1, and the valid range is from 0 to 100."
);
} }
@Override @Override

View File

@ -1,14 +1,18 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class Gravity extends DoubleStat { public class Gravity extends DoubleStat {
public Gravity() { public Gravity() {
super("GRAVITY", Material.STONE, super("GRAVITY",
"Gravity", new String[]{"Increases force of gravity.", "Player's default is set to 1"}); Material.STONE,
"Gravity",
"Controls blocks/tick^2 acceleration downward. Default is 0.08, and the valid range is from -1 to +1.");
} }
@Override @Override

View File

@ -6,7 +6,7 @@ import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.stat.data.BooleanData; import net.Indyuce.mmoitems.stat.data.BooleanData;
import net.Indyuce.mmoitems.stat.type.BooleanStat; import net.Indyuce.mmoitems.stat.type.BooleanStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemFlag;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@ -6,7 +6,7 @@ import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.stat.data.BooleanData; import net.Indyuce.mmoitems.stat.data.BooleanData;
import net.Indyuce.mmoitems.stat.type.BooleanStat; import net.Indyuce.mmoitems.stat.type.BooleanStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemFlag;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;

View File

@ -1,13 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class JumpStrength extends DoubleStat { public class JumpStrength extends DoubleStat {
public JumpStrength() { public JumpStrength() {
super("JUMP_STRENGTH", Material.SADDLE, super("JUMP_STRENGTH",
"Jump Strength", new String[]{"Additional jump height in blocks.", "Player's default is set to 1.25"}); Material.SADDLE,
"Jump Strength",
"This controls the base impulse from a jump (before jump boost or modifier on block). Default is 0.42, and the valid range is from 0 to 32.");
} }
} }

View File

@ -1,16 +1,20 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
public class KnockbackResistance extends DoubleStat { public class KnockbackResistance extends DoubleStat {
public KnockbackResistance() { public KnockbackResistance() {
super("KNOCKBACK_RESISTANCE", Material.CHAINMAIL_CHESTPLATE, "Knockback Resistance", new String[] { super("KNOCKBACK_RESISTANCE",
"The chance of your item to block the", "knockback from explosions, creepers...", "1.0 corresponds to 100%, 0.7 to 70%..." }); Material.CHAINMAIL_CHESTPLATE,
} "Knockback Resistance",
"The chance of your item to block the knockback from explosions, creepers... 1.0 corresponds to 100%, 0.7 to 70%...");
}
@Override @Override
public double multiplyWhenDisplaying() { public double multiplyWhenDisplaying() {
return 100; return 100;
} }
} }

View File

@ -1,13 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 2}) @VersionDependant(version = {1, 20, 2})
public class MaxAbsorption extends DoubleStat { public class MaxAbsorption extends DoubleStat {
public MaxAbsorption() { public MaxAbsorption() {
super("MAX_ABSORPTION", Material.ENCHANTED_GOLDEN_APPLE, super("MAX_ABSORPTION",
"Max Absorption", new String[]{"This does not provide permanent absorption", "but rather increases your maximum amount", "of absorption hearts you can have at any time."}); Material.ENCHANTED_GOLDEN_APPLE,
"Max Absorption",
"This does not provide permanent absorption but rather increases your maximum amount of absorption hearts you can have at any time.");
} }
} }

View File

@ -0,0 +1,15 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
public class MaxHealth extends DoubleStat {
public MaxHealth() {
super("MAX_HEALTH",
Material.GOLDEN_APPLE,
"Max Health",
"The amount of health your item gives to the holder.");
}
}

View File

@ -5,11 +5,12 @@ import io.lumine.mythic.lib.api.item.SupportedNBTTagValues;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder; import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.util.NumericStatFormula; import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.stat.type.GemStoneStat; import net.Indyuce.mmoitems.stat.type.GemStoneStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.Damageable;
@ -21,7 +22,7 @@ import java.util.ArrayList;
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class MaxItemDamage extends DoubleStat implements GemStoneStat { public class MaxItemDamage extends DoubleStat implements GemStoneStat {
public MaxItemDamage() { public MaxItemDamage() {
super("MAX_ITEM_DAMAGE", Material.DAMAGED_ANVIL, "Maximum Vanilla Durability", new String[]{"Only available in 1.20.5+", "Maximum amount of durabiliy on your item.", "This works using vanilla durability and is", "much more stable than Custom Durability."}, new String[]{"all"}); super("MAX_ITEM_DAMAGE", Material.DAMAGED_ANVIL, "Maximum Vanilla Durability", new String[]{"Only available in 1.20.5+", "Maximum amount of durability on your item.", "This works using vanilla durability and is", "much more stable than Custom Durability."}, new String[]{"all"});
} }
@Override @Override

View File

@ -5,17 +5,19 @@ import io.lumine.mythic.lib.api.item.SupportedNBTTagValues;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder; import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.util.NumericStatFormula; import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.stat.type.GemStoneStat; import net.Indyuce.mmoitems.stat.type.GemStoneStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class MaxStackSize extends DoubleStat implements GemStoneStat { public class MaxStackSize extends DoubleStat implements GemStoneStat {
public MaxStackSize() { public MaxStackSize() {

View File

@ -0,0 +1,19 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class MiningEfficiency extends DoubleStat {
public MiningEfficiency() {
super("MINING_EFFICIENCY",
Material.IRON_PICKAXE,
"Mining Efficiency",
"Mining speed factor added to the speed of mining when using a tool that efficiently mines a block. Default and minimum is 0, maximum is 1024."
);
}
}

View File

@ -0,0 +1,23 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class MovementEfficiency extends DoubleStat {
public MovementEfficiency() {
super("MOVEMENT_EFFICIENCY",
Material.LEATHER_BOOTS,
"Movement Efficiency",
"How efficiently the entity can move through impeding terrain that slows down movement. A factor of 1 removes all movement penalty, a factor of 0 applies full movement penalty. Default and minimum is 0, maximum is 1."
);
}
@Override
public double multiplyWhenDisplaying() {
return 100;
}
}

View File

@ -1,13 +1,20 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
public class MovementSpeed extends DoubleStat { public class MovementSpeed extends DoubleStat {
public MovementSpeed() { public MovementSpeed() {
super("MOVEMENT_SPEED", Material.LEATHER_BOOTS, "Movement Speed", new String[] { "Movement Speed increase walk speed.", "Default MC walk speed: 0.1" }); super("MOVEMENT_SPEED",
} Material.LEATHER_BOOTS,
"Movement Speed",
"Increases the player's walking speed. Default MC walk speed is 0.1");
}
@Override @Override
public double multiplyWhenDisplaying() { return 100; } public double multiplyWhenDisplaying() {
return 100;
}
} }

View File

@ -0,0 +1,17 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class OxygenBonus extends DoubleStat {
public OxygenBonus() {
super("OXYGEN_BONUS",
Material.CONDUIT,
"Bonus Oxygen",
"Factor to the chance an Entity has to not use up air when underwater. 0 has no effect, values over 0 are used in the following formula to determine the chance of using up air: 1 / ( bonus_oxygen + 1 ). Maximum is 1024.");
}
}

View File

@ -38,11 +38,11 @@ public class PickaxePower extends DoubleStat {
if (techMinimum < 0 && !handleNegativeStats()) { if (techMinimum < 0 && !handleNegativeStats()) {
techMinimum = 0; techMinimum = 0;
} }
if (techMinimum < ((NumericStatFormula) templateData).getBase() - ((NumericStatFormula) templateData).getMaxSpread()) { if (techMinimum < templateData.getBase() - templateData.getMaxSpread()) {
techMinimum = ((NumericStatFormula) templateData).getBase() - ((NumericStatFormula) templateData).getMaxSpread(); techMinimum = templateData.getBase() - templateData.getMaxSpread();
} }
if (techMaximum > ((NumericStatFormula) templateData).getBase() + ((NumericStatFormula) templateData).getMaxSpread()) { if (techMaximum > templateData.getBase() + templateData.getMaxSpread()) {
techMaximum = ((NumericStatFormula) templateData).getBase() + ((NumericStatFormula) templateData).getMaxSpread(); techMaximum = templateData.getBase() + templateData.getMaxSpread();
} }
// Add NBT Path // Add NBT Path

View File

@ -1,12 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class SafeFallDistance extends DoubleStat { public class SafeFallDistance extends DoubleStat {
public SafeFallDistance() { public SafeFallDistance() {
super("SAFE_FALL_DISTANCE", Material.RED_BED, "Safe Fall Distance", new String[]{"Additional blocks that you can fall", "down, without taking any fall damage.", "Player's default is set to 3"}); super("SAFE_FALL_DISTANCE",
Material.RED_BED,
"Safe Fall Distance",
"Controls the fall distance after which the player takes fall damage. Default is 3, and the valid range is from -1024 to +1024.");
} }
} }

View File

@ -1,13 +1,18 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class Scale extends DoubleStat { public class Scale extends DoubleStat {
public Scale() { public Scale() {
super("SCALE", Material.STONE, "Scale", new String[]{"Increases player size.", "Player's default is 1"}); super("SCALE",
Material.STONE,
"Scale",
"Allows changing the size of the player to anywhere between 0.0625 and 16 times their default size.");
} }
@Override @Override

View File

@ -0,0 +1,22 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class SneakingSpeed extends DoubleStat {
public SneakingSpeed() {
super("SNEAKING_SPEED",
Material.LEATHER_BOOTS,
"Sneaking Speed",
"The movement speed factor when sneaking. A factor of 1 means sneaking is as fast as walking, a factor of 0 means unable to move while sneaking. Default is 0.3, minimum is 0 and maximum is 1.");
}
@Override
public double multiplyWhenDisplaying() {
return 100;
}
}

View File

@ -10,6 +10,7 @@ import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem;
import net.Indyuce.mmoitems.api.player.PlayerData; import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.util.message.Message; import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.SoulboundData; import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.type.ConsumableItemInteraction; import net.Indyuce.mmoitems.stat.type.ConsumableItemInteraction;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
@ -24,8 +25,9 @@ import org.jetbrains.annotations.NotNull;
import java.util.Random; import java.util.Random;
@HasCategory(cat = "soulbound")
public class SoulbindingBreakChance extends DoubleStat implements ConsumableItemInteraction { public class SoulbindingBreakChance extends DoubleStat implements ConsumableItemInteraction {
private static final Random random = new Random(); private static final Random RANDOM = new Random();
public SoulbindingBreakChance() { public SoulbindingBreakChance() {
super("SOULBOUND_BREAK_CHANCE", Material.ENDER_EYE, "Soulbound Break Chance", super("SOULBOUND_BREAK_CHANCE", Material.ENDER_EYE, "Soulbound Break Chance",
@ -56,7 +58,7 @@ public class SoulbindingBreakChance extends DoubleStat implements ConsumableItem
return false; return false;
} }
if (random.nextDouble() < soulboundBreakChance / 100) { if (RANDOM.nextDouble() < soulboundBreakChance / 100) {
BreakSoulboundEvent called = new BreakSoulboundEvent(playerData, consumable.getMMOItem(), target); BreakSoulboundEvent called = new BreakSoulboundEvent(playerData, consumable.getMMOItem(), target);
Bukkit.getPluginManager().callEvent(called); Bukkit.getPluginManager().callEvent(called);
if (called.isCancelled()) if (called.isCancelled())

View File

@ -10,6 +10,7 @@ import net.Indyuce.mmoitems.api.item.mmoitem.MMOItem;
import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.VolatileMMOItem;
import net.Indyuce.mmoitems.api.player.PlayerData; import net.Indyuce.mmoitems.api.player.PlayerData;
import net.Indyuce.mmoitems.api.util.message.Message; import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.SoulboundData; import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.type.ConsumableItemInteraction; import net.Indyuce.mmoitems.stat.type.ConsumableItemInteraction;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
@ -25,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Random; import java.util.Random;
@HasCategory(cat = "soulbound")
public class SoulbindingChance extends DoubleStat implements ConsumableItemInteraction { public class SoulbindingChance extends DoubleStat implements ConsumableItemInteraction {
private static final Random random = new Random(); private static final Random random = new Random();

View File

@ -12,6 +12,7 @@ import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.player.RPGPlayer; import net.Indyuce.mmoitems.api.player.RPGPlayer;
import net.Indyuce.mmoitems.api.util.message.Message; import net.Indyuce.mmoitems.api.util.message.Message;
import net.Indyuce.mmoitems.gui.edition.EditionInventory; import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.SoulboundData; import net.Indyuce.mmoitems.stat.data.SoulboundData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData; import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
@ -33,6 +34,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@HasCategory(cat = "soulbound")
public class Soulbound extends ItemStat<RandomStatData<SoulboundData>, SoulboundData> implements InternalStat, ItemRestriction { public class Soulbound extends ItemStat<RandomStatData<SoulboundData>, SoulboundData> implements InternalStat, ItemRestriction {
public Soulbound() { public Soulbound() {
super("SOULBOUND", Material.ENDER_EYE, "Soulbound", new String[0], new String[0]); super("SOULBOUND", Material.ENDER_EYE, "Soulbound", new String[0], new String[0]);

View File

@ -3,15 +3,14 @@ package net.Indyuce.mmoitems.stat;
import io.lumine.mythic.lib.api.item.ItemTag; import io.lumine.mythic.lib.api.item.ItemTag;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder; import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.util.NumericStatFormula; import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.MMOUtils; import net.Indyuce.mmoitems.util.MMOUtils;
import org.bukkit.Material; import org.bukkit.Material;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** @HasCategory(cat = "soulbound")
* Soulbound level for consumables.
*/
public class SoulboundLevel extends DoubleStat { public class SoulboundLevel extends DoubleStat {
public SoulboundLevel() { public SoulboundLevel() {
super("SOULBOUND_LEVEL", Material.ENDER_EYE, "Soulbinding Level", new String[]{"The soulbound level defines how much", "damage players will take when trying", "to use a soulbound item. It also determines", "how hard it is to break the binding."}, new String[]{"consumable"}); super("SOULBOUND_LEVEL", Material.ENDER_EYE, "Soulbinding Level", new String[]{"The soulbound level defines how much", "damage players will take when trying", "to use a soulbound item. It also determines", "how hard it is to break the binding."}, new String[]{"consumable"});

View File

@ -1,13 +1,17 @@
package net.Indyuce.mmoitems.stat; package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.type.DoubleStat; import net.Indyuce.mmoitems.stat.type.DoubleStat;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 20, 5}) @VersionDependant(version = {1, 20, 5})
public class StepHeight extends DoubleStat { public class StepHeight extends DoubleStat {
public StepHeight() { public StepHeight() {
super("STEP_HEIGHT", Material.GOLDEN_BOOTS, super("STEP_HEIGHT",
"Step Height", new String[]{"Additional number of blocks that you can climb", "without jumping when walking or sprinting.", "Player's default is 0.6 i.e just higher than one slab."}); Material.STONE_SLAB,
"Step Height",
"Determines the max height in blocks where a mob can walk above without jumping. Default is 0.6, and the valid range is from 0 to 10.");
} }
} }

View File

@ -0,0 +1,22 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class SubmergedMiningSpeed extends DoubleStat {
public SubmergedMiningSpeed() {
super("SUBMERGED_MINING_SPEED",
Material.WATER_BUCKET,
"Submerged Mining Speed",
"The mining speed factor when submerged. A factor of 1 means mining as fast submerged as on land, a factor of 0 means unable to mine while submerged. Note that this represents only the submersion factor itself, and other factors(such as not touching the ground) also apply. Default is 0.2, minimum is 0 and maximum is 20.");
}
@Override
public double multiplyWhenDisplaying() {
return 100;
}
}

View File

@ -7,7 +7,7 @@ import net.Indyuce.mmoitems.stat.type.GemStoneStat;
public class SuccessRate extends DoubleStat implements GemStoneStat { public class SuccessRate extends DoubleStat implements GemStoneStat {
/* /**
* in a different class because Success Rate is meant to be a proper stat * in a different class because Success Rate is meant to be a proper stat
*/ */
public SuccessRate() { public SuccessRate() {

View File

@ -0,0 +1,22 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class SweepingDamageRatio extends DoubleStat {
public SweepingDamageRatio() {
super("SWEEPING_DAMAGE_RATIO",
Material.LIGHT_GRAY_DYE,
"Sweeping Damage Ratio",
"How much of the base attack damage that gets transferred transfer to secondary targets in a sweep attack. This is additive to the base attack of the sweep damage itself of 1. A value of 0 means none of the base attack damage is transferred (sweep damage is 1). A value of 1 means all of the base attack damage is transferred. Default and minimum value is 0, maximum value is 1.");
}
@Override
public double multiplyWhenDisplaying() {
return 100;
}
}

View File

@ -6,7 +6,7 @@ import net.Indyuce.mmoitems.stat.data.StringData;
import net.Indyuce.mmoitems.stat.type.ChooseStat; import net.Indyuce.mmoitems.stat.type.ChooseStat;
import net.Indyuce.mmoitems.stat.type.GemStoneStat; import net.Indyuce.mmoitems.stat.type.GemStoneStat;
import net.Indyuce.mmoitems.util.StatChoice; import net.Indyuce.mmoitems.util.StatChoice;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.Registry; import org.bukkit.Registry;

View File

@ -6,7 +6,7 @@ import net.Indyuce.mmoitems.stat.data.StringData;
import net.Indyuce.mmoitems.stat.type.ChooseStat; import net.Indyuce.mmoitems.stat.type.ChooseStat;
import net.Indyuce.mmoitems.stat.type.GemStoneStat; import net.Indyuce.mmoitems.stat.type.GemStoneStat;
import net.Indyuce.mmoitems.util.StatChoice; import net.Indyuce.mmoitems.util.StatChoice;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.Registry; import org.bukkit.Registry;

View File

@ -0,0 +1,22 @@
package net.Indyuce.mmoitems.stat;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import net.Indyuce.mmoitems.stat.type.DoubleStat;
import org.bukkit.Material;
@HasCategory(cat = "vanilla_attribute")
@VersionDependant(version = {1, 21})
public class WaterMovementEfficiency extends DoubleStat {
public WaterMovementEfficiency() {
super("WATER_MOVEMENT_EFFICIENCY",
Material.WATER_BUCKET,
"Water Movement Efficiency",
"The movement speed factor when submerged. The higher, the more of the underwater movement penalty is mitigated. Note that this represents only the submersion factor itself, and other factors (such as not touching the ground) also apply. Default and minimum value is 0, maximum value is 1.");
}
@Override
public double multiplyWhenDisplaying() {
return 100;
}
}

View File

@ -0,0 +1,13 @@
package net.Indyuce.mmoitems.stat.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Can be used to give categories to stats
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface HasCategory {
public String cat();
}

View File

@ -0,0 +1,20 @@
package net.Indyuce.mmoitems.stat.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Version string is MAJOR.MINOR.PATCH
* <p>
* This annotation indicates the LOWEST VERSION at which
* the given feature is available. Usually, it's the
* version where some non-backwards compatible feature was
* implemented into Minecraft or Spigot.
*
* @author Jules
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface VersionDependant {
public int[] version();
}

View File

@ -0,0 +1,41 @@
package net.Indyuce.mmoitems.stat.category;
import org.jetbrains.annotations.NotNull;
public class StatCategory {
private final String id, name, loreTag;
/**
* @param id Internal identifier of stat category. Must be unique
* @param name Name used to identify the category in the item browser
* @param loreTag Lore tag added to stats with the given category
*/
public StatCategory(String id, String name, String loreTag) {
this.id = id;
this.name = name;
this.loreTag = loreTag;
}
@NotNull
public String getId() {
return id;
}
@NotNull
public String getName() {
return name;
}
@NotNull
public String getLoreTag() {
return loreTag;
}
public static final StatCategory
TEMPLATE_OPTION = new StatCategory("TEMPLATE_OPTION", "Template Option, Misc", "Template Option"),
SOULBOUND = new StatCategory("SOULBOUND", "Soulbound", "Soulbound"),
ELEMENTAL = new StatCategory("ELEMENTAL", "Elements", "Elements"),
VANILLA_ATTRIBUTE = new StatCategory("VANILLA_ATTRIBUTE", "Vanilla Attributes", "Vanilla Attribute"),
REQUIREMENT = new StatCategory("REQUIREMENT", "Item Requirements", "Item Requirement"),
USE_COST = new StatCategory("USE_COST", "Item Costs", "Use Cost");
}

View File

@ -20,6 +20,7 @@ import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo; import net.Indyuce.mmoitems.stat.data.type.UpgradeInfo;
import net.Indyuce.mmoitems.util.MMOUtils;
import org.apache.commons.lang.Validate; import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
@ -44,6 +45,10 @@ public class DoubleStat extends ItemStat<NumericStatFormula, DoubleData> impleme
this(id, mat, name, lore, new String[]{"!miscellaneous", "!block", "all"}, true); this(id, mat, name, lore, new String[]{"!miscellaneous", "!block", "all"}, true);
} }
public DoubleStat(String id, Material mat, String name, String lore) {
this(id, mat, name, MMOUtils.trimString(LORE_LINE_WIDTH, lore), new String[]{"!miscellaneous", "!block", "all"}, true);
}
public DoubleStat(String id, Material mat, String name, String[] lore, String[] types, Material... materials) { public DoubleStat(String id, Material mat, String name, String[] lore, String[] types, Material... materials) {
this(id, mat, name, lore, types, true, materials); this(id, mat, name, lore, types, true, materials);
} }

View File

@ -6,6 +6,7 @@ import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.api.util.NumericStatFormula; import net.Indyuce.mmoitems.api.util.NumericStatFormula;
import net.Indyuce.mmoitems.gui.edition.EditionInventory; import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.annotation.HasCategory;
import net.Indyuce.mmoitems.stat.data.DoubleData; import net.Indyuce.mmoitems.stat.data.DoubleData;
import net.Indyuce.mmoitems.util.ElementStatType; import net.Indyuce.mmoitems.util.ElementStatType;
import org.bukkit.Material; import org.bukkit.Material;
@ -26,9 +27,10 @@ import java.util.Optional;
* *
* @deprecated Definitely not a perfect implementation * @deprecated Definitely not a perfect implementation
*/ */
@HasCategory(cat = "elemental")
@Deprecated @Deprecated
public class FictiveNumericStat extends DoubleStat implements InternalStat { public class FakeElementalStat extends DoubleStat implements InternalStat {
public FictiveNumericStat(Element el, ElementStatType type) { public FakeElementalStat(Element el, ElementStatType type) {
super(type.getConcatenatedTagPath(el), Material.BARRIER, "Fictive Stat", new String[0]); super(type.getConcatenatedTagPath(el), Material.BARRIER, "Fictive Stat", new String[0]);
} }

View File

@ -8,9 +8,10 @@ import net.Indyuce.mmoitems.api.Type;
import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder; import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem; import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.gui.edition.EditionInventory; import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.category.StatCategory;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData; import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.StatData; import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.util.VersionDependant; import net.Indyuce.mmoitems.stat.annotation.VersionDependant;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
@ -30,7 +31,7 @@ public abstract class ItemStat<R extends RandomStatData<S>, S extends StatData>
private final List<String> compatibleTypes; private final List<String> compatibleTypes;
private final List<Material> compatibleMaterials; private final List<Material> compatibleMaterials;
@Nullable @NotNull
private String[] aliases = {}; private String[] aliases = {};
/** /**
@ -39,6 +40,10 @@ public abstract class ItemStat<R extends RandomStatData<S>, S extends StatData>
*/ */
private boolean enabled = true; private boolean enabled = true;
private StatCategory category;
protected static final int LORE_LINE_WIDTH = 50;
/** /**
* Initializes an item stat * Initializes an item stat
* *
@ -66,6 +71,12 @@ public abstract class ItemStat<R extends RandomStatData<S>, S extends StatData>
final VersionDependant implVersion = getClass().getAnnotation(VersionDependant.class); final VersionDependant implVersion = getClass().getAnnotation(VersionDependant.class);
if (MythicLib.plugin.getVersion().isUnder(implVersion.version())) disable(); if (MythicLib.plugin.getVersion().isUnder(implVersion.version())) disable();
} }
// Backwards compatibility
if (getClass().isAnnotationPresent(net.Indyuce.mmoitems.util.VersionDependant.class)) {
final net.Indyuce.mmoitems.util.VersionDependant implVersion = getClass().getAnnotation(net.Indyuce.mmoitems.util.VersionDependant.class);
if (MythicLib.plugin.getVersion().isUnder(implVersion.version())) disable();
}
} }
/** /**
@ -177,6 +188,15 @@ public abstract class ItemStat<R extends RandomStatData<S>, S extends StatData>
return generalStatFormat; return generalStatFormat;
} }
@Nullable
public StatCategory getCategory() {
return category;
}
public void setCategory(@Nullable StatCategory category) {
this.category = category;
}
@NotNull @NotNull
public String getName() { public String getName() {
return name; return name;
@ -199,7 +219,7 @@ public abstract class ItemStat<R extends RandomStatData<S>, S extends StatData>
* <p> * <p>
* Aliases have to follow the UPPER_CASE stat identifier format. * Aliases have to follow the UPPER_CASE stat identifier format.
*/ */
@Nullable @NotNull
public String[] getAliases() { public String[] getAliases() {
return aliases; return aliases;
} }
@ -302,6 +322,8 @@ public abstract class ItemStat<R extends RandomStatData<S>, S extends StatData>
return id.equals(itemStat.id); return id.equals(itemStat.id);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(id); return Objects.hash(id);

View File

@ -40,6 +40,32 @@ public class MMOUtils {
return particle.getDataType() == Particle.DustOptions.class; return particle.getDataType() == Particle.DustOptions.class;
} }
public static String[] trimString(int charactersPerLine, @NotNull String... inputs) {
List<String> list = new ArrayList<>();
for (String input : inputs) {
if (input.length() <= charactersPerLine) {
list.add(input);
continue;
}
StringBuilder currentLine = new StringBuilder();
for (String word : input.split(" ")) {
if (!currentLine.isEmpty()) currentLine.append(" ");
currentLine.append(word);
if (currentLine.length() > charactersPerLine || word.endsWith("\n")) {
list.add(currentLine.toString()); // Return line
currentLine.setLength(0); // Empty current line
}
}
// Add last line (sometimes not necessary)
if (!currentLine.isEmpty()) list.add(currentLine.toString());
}
return list.toArray(new String[0]);
}
@NotNull @NotNull
public static ItemStack readIcon(@NotNull String stringInput) { public static ItemStack readIcon(@NotNull String stringInput) {

View File

@ -4,15 +4,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** /**
* Version string is MAJOR.MINOR.PATCH * @see net.Indyuce.mmoitems.stat.annotation.VersionDependant
* <p> * @deprecated Moved to another class
* This annotation indicates the LOWEST VERSION at which
* the given feature is available. Usually, it's the
* version where some non-backwards compatible feature was
* implemented into Minecraft or Spigot.
*
* @author Jules
*/ */
@Deprecated
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface VersionDependant { public @interface VersionDependant {

View File

@ -1,3 +1,4 @@
# Dragon set
DRAGON_HELMET: DRAGON_HELMET:
base: base:
material: DIAMOND_HELMET material: DIAMOND_HELMET
@ -162,6 +163,8 @@ DRAGON_BOOTS:
- Magenta - Magenta
- Black - Black
set: DRAGON set: DRAGON
# Spellcaster set
SPELLCASTER_HELMET: SPELLCASTER_HELMET:
base: base:
material: LEATHER_HELMET material: LEATHER_HELMET
@ -294,6 +297,8 @@ SPELLCASTER_BOOTS:
hide-dye: true hide-dye: true
dye-color: 125 0 255 dye-color: 125 0 255
set: SPELLCASTER set: SPELLCASTER
# Omnielemental set
OMNIELEMENTAL_HELMET: OMNIELEMENTAL_HELMET:
base: base:
material: LEATHER_HELMET material: LEATHER_HELMET
@ -482,104 +487,8 @@ OMNIELEMENTAL_BOOTS:
JUMP: 1.0 JUMP: 1.0
tier: VERY_RARE tier: VERY_RARE
max-health: 5.0 max-health: 5.0
HELMET_OF_THE_SEA:
base: # Undead slayer
material: LEATHER_HELMET
max-durability: 8000.0
attack-damage:
base: 6.0
scale: 0.1
spread: 0.2
max-spread: 0.3
required-level: 10.0
weapon-damage:
base: 10.0
scale: 0.1
spread: 0.2
max-spread: 0.25
magic-damage:
base: 10.0
scale: 0.1
spread: 0.2
max-spread: 0.25
physical-damage:
base: 15.0
scale: 0.1
spread: 0.2
max-spread: 0.25
cooldown-reduction:
base: 15.0
scale: 0.1
spread: 0.2
max-spread: 0.25
pve-damage:
base: 15.0
scale: 0.1
spread: 0.2
max-spread: 0.25
tier: RARE
dye-color: 120 120 255
name: §9Helmet of the Ocean
hide-dye: true
element:
water:
defense:
base: 25.0
scale: 0.0
spread: 0.2
max-spread: 0.4
amphibian: DAMP
perm-effects:
NIGHT_VISION: 1.0
WATER_BREATHING: 1.0
lore:
- §3Will only work when §9§lUNDERWATER§3.
- §3Gives §9Night Vision §3and §9Water Breathing§3.
UNDEADSLAYER_HELMET:
base:
material: NETHERITE_HELMET
max-durability: 10500.0
tier: EPIC
armor-toughness: 2.5
MOON_BOOTS:
base:
material: DIAMOND_BOOTS
lore:
- §bMoon boots will simulate being on the moon!
- §7Gives §3Jump Boost II§7 when worn.
- §cWill only work in "the end" biome.
max-durability: 2650.0
name: §b§lMoon Boots
tier: UNIQUE
armor-toughness: 1.5
armor: 3.0
fall-damage-reduction: 75.0
perm-effects:
JUMP: 2.0
required-biomes:
- the_end
CONTROL_DEVICES:
base:
material: LEATHER_HELMET
max-durability: 15000.0
name: §d§lControl Devices
dye-color: 255 102 204
ability: { }
set: PSYCHIC
perm-effects:
DAMAGE_RESISTANCE: 2.0
SPEED: 2.0
REGENERATION: 2.0
FIRE_RESISTANCE: 1.0
INCREASE_DAMAGE: 2.0
required-class:
- Mage
lore:
- §7To keep your powers in check...
armor-toughness: 2.0
armor: 7.0
tier: MAGICAL
unbreakable: true
UNDEAD_SLAYER_HELMET: UNDEAD_SLAYER_HELMET:
base: base:
material: NETHERITE_HELMET material: NETHERITE_HELMET
@ -696,29 +605,8 @@ UNDEAD_SLAYER_BOOTS:
enchants: enchants:
smite: 3.0 smite: 3.0
protection: 4.0 protection: 4.0
MYTHRIL_CHAINMAIL:
base: # Steel set
material: CHAINMAIL_CHESTPLATE
name: '&b--] &fMythril Chainmail &b[--'
block-power:
base: 60.0
spread: 0.048
max-spread: 0.14
block-rating:
base: 12.5
spread: 0.06
max-spread: 0.17
armor: 5
element:
ice:
defense: 8.0
earth:
defense: 6.4
gem-sockets:
- Red
- Blue
required-level: 4.0
tier: UNCOMMON
STEEL_HELMET: STEEL_HELMET:
base: base:
material: IRON_HELMET material: IRON_HELMET
@ -847,44 +735,8 @@ STEEL_BOOTS:
- MATERIAL.STEEL_INGOT AIR MATERIAL.STEEL_INGOT - MATERIAL.STEEL_INGOT AIR MATERIAL.STEEL_INGOT
- MATERIAL.STEEL_INGOT AIR MATERIAL.STEEL_INGOT - MATERIAL.STEEL_INGOT AIR MATERIAL.STEEL_INGOT
- AIR AIR AIR - AIR AIR AIR
CURSED_WITHER_SKULL:
base: # Arcane set
material: WITHER_SKELETON_SKULL
name: '&7Cursed Wither Skull'
required-level: 30.0
tier: UNIQUE
gem-sockets:
- Red
- Blue
armor: 3.0
armor-toughness: 2.0
perm-effects:
ABSORPTION: 1
lore:
- '&7Grants a permanent &62'
- '&6hearts &7absorption shield.'
durability: 1.0
element:
thunder:
defense: 67.4
fire:
defense: 53.4
crafting:
shaped:
'1':
- BONE BONE BONE
- BONE MATERIAL.UNIQUE_WEAPON_ESSENCE BONE
- BONE BONE BONE
HUGE_MOTHRON_WINGS:
base:
material: ELYTRA
max-durability: 5.0
two-handed: false
name: '&eHuge Mothron Wings'
tier: RARE
unbreakable: true
movement-speed: -0.03
will-break: false
ARCANE_HELM: ARCANE_HELM:
base: base:
material: LEATHER_HELMET material: LEATHER_HELMET
@ -961,6 +813,8 @@ ARCANE_BOOTS:
spread: 0.033 spread: 0.033
max-spread: 0.1 max-spread: 0.1
set: ARCANE set: ARCANE
# Gingerbread set
GINGERBREAD_HELM: GINGERBREAD_HELM:
base: base:
material: LEATHER_HELMET material: LEATHER_HELMET
@ -1037,93 +891,8 @@ GINGERBREAD_BOOTS:
red: 255 red: 255
green: 100 green: 100
blue: 100 blue: 100
DEAD_PHARAOH_HELMET:
base: # Shadow set
material: PLAYER_HEAD
skull-texture:
value: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU1NWRmOTc5YWI3OTc0OWY4YjU1MWI0MjM5YTQ2OWFhNzY5ZDliNDYwNTBhYWJkOWY2ZDFjZWU1M2VkMzYifX19
uuid: 852deb36-af4a-412f-ac76-2b06dc123ed2
name: '&cDead Pharaoh Helmet'
disable-interaction: true
item-particles:
type: FIREFLIES
particle: SMOKE_NORMAL
fire-damage-reduction: 30.0
armor: 4.0
undead-damage: 30.0
lore:
- '&7This powerful forgotten helmet'
- '&7will greatly increase your power'
- '&7against undead creatures.'
required-level: 9.0
MOSSY_SKELETON_SKULL:
base:
material: PLAYER_HEAD
skull-texture:
value: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWE2MzE0ZWFjMzQ0MTZjZTEwYWIyMmMyZTFjNGRjYjQ3MmEzZmViOThkNGUwNGQzZmJiYjg1YTlhNDcxYjE4In19fQ
uuid: f455a085-3f09-43ac-8be1-175204d1a6ad
name: '&8Mossy Skeleton Skull'
required-level: 6.0
armor: 3.5
knockback-resistance: 0.1
movement-speed: -0.01
disable-interaction: true
SKELETON_CROWN:
base:
material: PLAYER_HEAD
skull-texture:
value: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGM3OGQyMTAyZGI3NWYxYjM3NDRhNWU3ZTliYWNjZjg4ZmRhNGNjNDk3OWViYzBhODFiN2Q5ZWI1NzIxYzAifX19
uuid: ccb901e7-0919-463d-9bdd-ee9acae8e0e2
name: '&eSkeleton Crown'
required-level: 9.0
armor: 3.0
movement-speed: 0.01
ability:
ability1:
type: LIFE_ENDER
mode: DAMAGED
damage: 3.0
cooldown: 9.0
lore:
- '&7Every 9 seconds, summons a'
- '&cdeadly meteor&7 at your attacker,'
- '&7dealing &c4&7 damage while briefly'
- '&7knocking them away.'
disable-interaction: true
WYVERN_CAP:
base:
material: GOLDEN_HELMET
name: '&fWyvern Cap'
required-level: 10.0
hide-enchants: false
enchants:
unbreaking: 9
armor: 2.5
fall-damage-reduction: 70.0
perm-effects:
JUMP: 2
lore:
- '&7Grants permanent jump boost II'
crafting:
shaped:
'1':
- TOME.WYVERN_FEATHER TOME.WYVERN_SOUL TOME.WYVERN_FEATHER
- TOME.WYVERN_FEATHER AIR TOME.WYVERN_FEATHER
- AIR AIR AIR
'2':
- AIR AIR AIR
- TOME.WYVERN_FEATHER TOME.WYVERN_SOUL TOME.WYVERN_FEATHER
- TOME.WYVERN_FEATHER AIR TOME.WYVERN_FEATHER
TRAVEL_BOOTS:
base:
material: LEATHER_BOOTS
name: §fTravel boots
movement-speed: 0.005
dye-color: 187 118 126
will-break: true
max-durability: 100.0
armor: 2.0
fall-damage-reduction: 5.0
SHADOWVEIL: SHADOWVEIL:
base: base:
material: LEATHER_HELMET material: LEATHER_HELMET
@ -1214,6 +983,187 @@ SHADOWBOOTS:
max-health: 8.0 max-health: 8.0
movement-speed: 0.01 movement-speed: 0.01
dye-color: 0 0 0 dye-color: 0 0 0
# Misc
HELMET_OF_THE_SEA:
base:
material: LEATHER_HELMET
max-durability: 8000.0
attack-damage:
base: 6.0
scale: 0.1
spread: 0.2
max-spread: 0.3
required-level: 10.0
weapon-damage:
base: 10.0
scale: 0.1
spread: 0.2
max-spread: 0.25
magic-damage:
base: 10.0
scale: 0.1
spread: 0.2
max-spread: 0.25
physical-damage:
base: 15.0
scale: 0.1
spread: 0.2
max-spread: 0.25
cooldown-reduction:
base: 15.0
scale: 0.1
spread: 0.2
max-spread: 0.25
pve-damage:
base: 15.0
scale: 0.1
spread: 0.2
max-spread: 0.25
tier: RARE
dye-color: 120 120 255
name: §9Helmet of the Ocean
hide-dye: true
element:
water:
defense:
base: 25.0
scale: 0.0
spread: 0.2
max-spread: 0.4
amphibian: DAMP
perm-effects:
NIGHT_VISION: 1.0
WATER_BREATHING: 1.0
lore:
- §3Will only work when §9§lUNDERWATER§3.
- §3Gives §9Night Vision §3and §9Water Breathing§3.
MOON_BOOTS:
base:
material: DIAMOND_BOOTS
lore:
- §bMoon boots will simulate being on the moon!
- §7Gives §3Jump Boost II§7 when worn.
- §cWill only work in "the end" biome.
max-durability: 2650.0
name: §b§lMoon Boots
tier: UNIQUE
armor-toughness: 1.5
armor: 3.0
fall-damage-reduction: 75.0
perm-effects:
JUMP: 2.0
required-biomes:
- the_end
CONTROL_DEVICES:
base:
material: LEATHER_HELMET
max-durability: 15000.0
name: §d§lControl Devices
dye-color: 255 102 204
ability: {}
set: PSYCHIC
perm-effects:
DAMAGE_RESISTANCE: 2.0
SPEED: 2.0
REGENERATION: 2.0
FIRE_RESISTANCE: 1.0
INCREASE_DAMAGE: 2.0
required-class:
- Mage
lore:
- §7To keep your powers in check...
armor-toughness: 2.0
armor: 7.0
tier: MAGICAL
unbreakable: true
DEAD_PHARAOH_HELMET:
base:
material: PLAYER_HEAD
skull-texture:
value: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU1NWRmOTc5YWI3OTc0OWY4YjU1MWI0MjM5YTQ2OWFhNzY5ZDliNDYwNTBhYWJkOWY2ZDFjZWU1M2VkMzYifX19
uuid: 852deb36-af4a-412f-ac76-2b06dc123ed2
name: '&cDead Pharaoh Helmet'
disable-interaction: true
item-particles:
type: FIREFLIES
particle: SMOKE_NORMAL
fire-damage-reduction: 30.0
armor: 4.0
undead-damage: 30.0
lore:
- '&7This powerful forgotten helmet'
- '&7will greatly increase your power'
- '&7against undead creatures.'
required-level: 9.0
MOSSY_SKELETON_SKULL:
base:
material: PLAYER_HEAD
skull-texture:
value: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWE2MzE0ZWFjMzQ0MTZjZTEwYWIyMmMyZTFjNGRjYjQ3MmEzZmViOThkNGUwNGQzZmJiYjg1YTlhNDcxYjE4In19fQ
uuid: f455a085-3f09-43ac-8be1-175204d1a6ad
name: '&8Mossy Skeleton Skull'
required-level: 6.0
armor: 3.5
knockback-resistance: 0.1
movement-speed: -0.01
disable-interaction: true
SKELETON_CROWN:
base:
material: PLAYER_HEAD
skull-texture:
value: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGM3OGQyMTAyZGI3NWYxYjM3NDRhNWU3ZTliYWNjZjg4ZmRhNGNjNDk3OWViYzBhODFiN2Q5ZWI1NzIxYzAifX19
uuid: ccb901e7-0919-463d-9bdd-ee9acae8e0e2
name: '&eSkeleton Crown'
required-level: 9.0
armor: 3.0
movement-speed: 0.01
ability:
ability1:
type: LIFE_ENDER
mode: DAMAGED
damage: 3.0
cooldown: 9.0
lore:
- '&7Every 9 seconds, summons a'
- '&cdeadly meteor&7 at your attacker,'
- '&7dealing &c4&7 damage while briefly'
- '&7knocking them away.'
disable-interaction: true
WYVERN_CAP:
base:
material: GOLDEN_HELMET
name: '&fWyvern Cap'
required-level: 10.0
hide-enchants: false
enchants:
unbreaking: 9
armor: 2.5
fall-damage-reduction: 70.0
perm-effects:
JUMP: 2
lore:
- '&7Grants permanent jump boost II'
crafting:
shaped:
'1':
- TOME.WYVERN_FEATHER TOME.WYVERN_SOUL TOME.WYVERN_FEATHER
- TOME.WYVERN_FEATHER AIR TOME.WYVERN_FEATHER
- AIR AIR AIR
'2':
- AIR AIR AIR
- TOME.WYVERN_FEATHER TOME.WYVERN_SOUL TOME.WYVERN_FEATHER
- TOME.WYVERN_FEATHER AIR TOME.WYVERN_FEATHER
TRAVEL_BOOTS:
base:
material: LEATHER_BOOTS
name: §fTravel boots
movement-speed: 0.005
dye-color: 187 118 126
will-break: true
max-durability: 100.0
armor: 2.0
fall-damage-reduction: 5.0
GARGOYLE_CHESTPLATE: GARGOYLE_CHESTPLATE:
base: base:
material: IRON_CHESTPLATE material: IRON_CHESTPLATE
@ -1241,3 +1191,103 @@ SHRINKING_BOOTS:
lore: lore:
- '&7&oMay this item help you get' - '&7&oMay this item help you get'
- '&7&othrough the narrowest paths...' - '&7&othrough the narrowest paths...'
MYTHRIL_CHAINMAIL:
base:
material: CHAINMAIL_CHESTPLATE
name: '&b--] &fMythril Chainmail &b[--'
block-power:
base: 60.0
spread: 0.048
max-spread: 0.14
block-rating:
base: 12.5
spread: 0.06
max-spread: 0.17
armor: 5
element:
ice:
defense: 8.0
earth:
defense: 6.4
gem-sockets:
- Red
- Blue
required-level: 4.0
tier: UNCOMMON
CURSED_WITHER_SKULL:
base:
material: WITHER_SKELETON_SKULL
name: '&7Cursed Wither Skull'
required-level: 30.0
tier: UNIQUE
gem-sockets:
- Red
- Blue
armor: 3.0
armor-toughness: 2.0
perm-effects:
ABSORPTION: 1
lore:
- '&7Grants a permanent &62'
- '&6hearts &7absorption shield.'
durability: 1.0
element:
thunder:
defense: 67.4
fire:
defense: 53.4
crafting:
shaped:
'1':
- BONE BONE BONE
- BONE MATERIAL.UNIQUE_WEAPON_ESSENCE BONE
- BONE BONE BONE
LARGE_MOTHRON_WINGS:
base:
material: ELYTRA
max-durability: 5.0
two-handed: false
name: '&eLarge Mothron Wings'
tier: RARE
gravity: -0.02
unbreakable: true
movement-speed: -0.03
will-break: false
FIREPROOF_JACKET:
base:
material: LEATHER_CHESTPLATE
burning-time: -0.9
name: Fireproof Jacket
enchants:
unbreaking: 4.0
trim-pattern: vex
trim-material: redstone
BEE_WINGS:
base:
material: ELYTRA
name: Bee Wings
movement-speed: 0.02
fall-damage-multiplier: -0.5
jump-strength: 0.2
gravity: -0.04
safe-fall-distance: 7.0
SOULWALKER:
base:
material: GOLDEN_BOOTS
name: Soulwalker
movement-efficiency: 0.9
armor: 3.0
step-height: 0.4
lore:
- '&7Negates soulsand movement impairing.'
DIVING_BOOTS:
base:
material: GOLDEN_BOOTS
name: Diving Boots
trim-material: copper
trim-pattern: wild
water-movement-efficiency: 1.0
gravity: 2.0
step-height: 0.5

View File

@ -175,11 +175,10 @@ SNEAKY_DAGGER:
material: STONE_SWORD material: STONE_SWORD
name: §8§lSneaky Dagger name: §8§lSneaky Dagger
lore: lore:
- §7This dagger is very sneaky. - '&7&oUse it to sneak behind your target''s back!'
- §8§oWhat dagger?
tier: UNCOMMON tier: UNCOMMON
attack-damage: 5.0 attack-damage: 11.0
attack-speed: 2.2 attack-speed: 0.9
critical-strike-chance: critical-strike-chance:
base: 9.0 base: 9.0
scale: 0.1 scale: 0.1
@ -197,3 +196,4 @@ SNEAKY_DAGGER:
mode: ATTACK mode: ATTACK
cooldown: 3.0 cooldown: 3.0
extra: 50.0 extra: 50.0
sneaking-speed: 2.0

View File

@ -53,7 +53,7 @@ AUTOSMELT_PICKAXE:
- v magma_cream - 1.0..|v netherite_block - 1.0..|v magma_cream - 1.0.. - v magma_cream - 1.0..|v netherite_block - 1.0..|v magma_cream - 1.0..
- v AIR 0 1..|v stick - 1.0..|v AIR 0 1.. - v AIR 0 1..|v stick - 1.0..|v AIR 0 1..
- v AIR 0 1..|v stick - 1.0..|v AIR 0 1.. - v AIR 0 1..|v stick - 1.0..|v AIR 0 1..
UNBREAKABLE_SHEARS: SILK_SHEARS:
base: base:
material: SHEARS material: SHEARS
enchants: enchants:
@ -64,3 +64,12 @@ UNBREAKABLE_SHEARS:
- §7pick up §9glass§7 or §2grass§7! - §7pick up §9glass§7 or §2grass§7!
tier: UNCOMMON tier: UNCOMMON
required-level: 5.0 required-level: 5.0
FAST_PICK:
base:
material: WOODEN_PICKAXE
mining-efficiency: 100.0
max-item-damage: 100.0
lore:
- '&7&oInsanely fast, but won''t loot'

View File

@ -92,6 +92,15 @@ lore-format:
- '#safe-fall-distance#' - '#safe-fall-distance#'
- '#scale#' - '#scale#'
- '#step-height#' - '#step-height#'
- '#burning-time#'
- '#explosion-knockback-resistance#'
- '#mining-efficiency#'
- '#movement-efficiency#'
- '#oxygen-bonus#'
- '#sneaking-speed#'
- '#submerged-mining-speed#'
- '#sweeping-damage-ratio#'
- '#water-movement-efficiency#'
- '#lute-attack-effect#' - '#lute-attack-effect#'
- '#two-handed#' - '#two-handed#'
- '#handworn#' - '#handworn#'

View File

@ -94,6 +94,15 @@ max-absorption: '&3 &7■ Max Absorption: &f<plus>{value}'
safe-fall-distance: '&3 &7■ Safe Fall Distance: &f<plus>{value}' safe-fall-distance: '&3 &7■ Safe Fall Distance: &f<plus>{value}'
scale: '&3 &7■ Size: &f<plus>{value}%' scale: '&3 &7■ Size: &f<plus>{value}%'
step-height: '&3 &7■ Smooth Walking: &f<plus>{value}' step-height: '&3 &7■ Smooth Walking: &f<plus>{value}'
burning-time: '&3 &7■ Burning Time: &f<plus>{value}%'
explosion-knockback-resistance: '&3 &7■ Explosion Knockback Resistance: &f<plus>{value}%'
mining-efficiency: '&3 &7■ Mining Efficiency: &f<plus>{value}'
movement-efficiency: '&3 &7■ Movement Efficiency: &f<plus>{value}%'
oxygen-bonus: '&3 &7■ Waterbreathing: &f<plus>{value}'
sneaking-speed: '&3 &7■ Sneaking Speed: &f<plus>{value}%'
submerged-mining-speed: '&3 &7■ Underwater Mining Speed: &f<plus>{value}%'
sweeping-damage-ratio: '&3 &7■ Sweeping Damage: &f<plus>{value}%'
water-movement-efficiency: '&3 &7■ Underwater Movement Speed: &f<plus>{value}%'
# Extra Options # Extra Options
perm-effects: '&3 &7■ Permanent &f{effect}' perm-effects: '&3 &7■ Permanent &f{effect}'