From b364c3d64e1fed1c6a1c08f2eaf561897f10e09e Mon Sep 17 00:00:00 2001 From: Archy-X Date: Mon, 14 Aug 2023 00:54:09 -0700 Subject: [PATCH] Implement modifiers, multipliers, and requirements --- .../auraskills/api/registry/NamespacedId.java | 2 +- .../auraskills/bukkit/AuraSkills.java | 28 ++ .../bukkit/commands/CommandRegistrar.java | 2 +- .../bukkit/item/ItemRegistryMenuProvider.java | 2 +- .../auraskills/bukkit/loot/ItemKeyParser.java | 2 +- .../bukkit/loot/LootTableManager.java | 2 +- .../bukkit/loot/SourceContextManager.java | 2 +- .../bukkit/menus/contexts/AbilityContext.java | 2 +- .../menus/contexts/ManaAbilityContext.java | 2 +- .../bukkit/menus/contexts/SkillContext.java | 2 +- .../bukkit/menus/contexts/SourceContext.java | 4 +- .../bukkit/menus/contexts/StatContext.java | 2 +- .../bukkit/menus/stats/SkullItem.java | 2 +- .../modifier/ArmorModifierListener.java | 171 ++++++++++ .../bukkit/modifier/ItemListener.java | 308 +++++++++++++++++ .../bukkit/modifier/ModifierManager.java | 88 ++++- .../bukkit/modifier/ModifierType.java | 8 + .../auraskills/bukkit/modifier/Modifiers.java | 158 +++++++++ .../bukkit/modifier/Multipliers.java | 157 +++++++++ .../bukkit/requirement/GlobalRequirement.java | 33 ++ .../requirement/RequirementListener.java | 161 +++++++++ .../requirement/RequirementManager.java | 95 ++++++ .../bukkit/requirement/Requirements.java | 170 ++++++++++ .../skills/foraging/ForagingAbilities.java | 5 +- .../bukkit/skills/foraging/Treecapitator.java | 4 +- .../auraskills/bukkit/ui/BossBarManager.java | 2 +- .../bukkit/util/ConfigurateItemParser.java | 2 +- .../auraskills/bukkit/util/ItemUtils.java | 43 ++- .../bukkit/util/armor/ArmorEquipEvent.java | 143 ++++++++ .../bukkit/util/armor/ArmorListener.java | 319 ++++++++++++++++++ .../bukkit/util/armor/ArmorType.java | 54 +++ .../reward/parser/ItemRewardParser.java | 2 +- .../reward/parser/StatRewardParser.java | 2 +- .../auraskills/common/skill/SkillLoader.java | 4 +- .../common/source/SourceLoader.java | 4 +- .../aurelium/auraskills/common/user/User.java | 2 +- .../auramc/auraskills/NamespacedIdTest.java | 4 +- 37 files changed, 1958 insertions(+), 35 deletions(-) create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ArmorModifierListener.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ItemListener.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierType.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Modifiers.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Multipliers.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/GlobalRequirement.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementListener.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementManager.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/Requirements.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorEquipEvent.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorListener.java create mode 100644 bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorType.java diff --git a/api/src/main/java/dev/aurelium/auraskills/api/registry/NamespacedId.java b/api/src/main/java/dev/aurelium/auraskills/api/registry/NamespacedId.java index 4e7243458..f8da41822 100644 --- a/api/src/main/java/dev/aurelium/auraskills/api/registry/NamespacedId.java +++ b/api/src/main/java/dev/aurelium/auraskills/api/registry/NamespacedId.java @@ -39,7 +39,7 @@ public static NamespacedId fromString(String string) { return new NamespacedId(split[0], split[1]); } - public static NamespacedId fromStringOrDefault(String string) { + public static NamespacedId fromDefault(String string) { String[] split = string.split("/"); if (split.length == 1) { return new NamespacedId(NamespacedId.AURASKILLS, split[0]); // Use default namespace if not specified diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/AuraSkills.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/AuraSkills.java index be24680c3..a32db557b 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/AuraSkills.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/AuraSkills.java @@ -3,6 +3,7 @@ import co.aikar.commands.PaperCommandManager; import com.archyx.slate.Slate; import com.archyx.slate.menu.MenuManager; +import de.tr7zw.changeme.nbtapi.utils.MinecraftVersion; import dev.aurelium.auraskills.api.AuraSkillsApi; import dev.aurelium.auraskills.bukkit.ability.BukkitAbilityManager; import dev.aurelium.auraskills.bukkit.commands.CommandRegistrar; @@ -18,9 +19,13 @@ import dev.aurelium.auraskills.bukkit.mana.BukkitManaAbilityManager; import dev.aurelium.auraskills.bukkit.menus.MenuFileManager; import dev.aurelium.auraskills.bukkit.menus.MenuRegistrar; +import dev.aurelium.auraskills.bukkit.modifier.ArmorModifierListener; +import dev.aurelium.auraskills.bukkit.modifier.ItemListener; import dev.aurelium.auraskills.bukkit.modifier.ModifierManager; import dev.aurelium.auraskills.bukkit.region.BukkitWorldManager; import dev.aurelium.auraskills.bukkit.region.RegionManager; +import dev.aurelium.auraskills.bukkit.requirement.RequirementListener; +import dev.aurelium.auraskills.bukkit.requirement.RequirementManager; import dev.aurelium.auraskills.bukkit.reward.BukkitRewardManager; import dev.aurelium.auraskills.bukkit.scheduler.BukkitScheduler; import dev.aurelium.auraskills.bukkit.skills.agility.AgilityAbilities; @@ -29,6 +34,7 @@ import dev.aurelium.auraskills.bukkit.ui.BukkitUiProvider; import dev.aurelium.auraskills.bukkit.user.BukkitUser; import dev.aurelium.auraskills.bukkit.user.BukkitUserManager; +import dev.aurelium.auraskills.bukkit.util.armor.ArmorListener; import dev.aurelium.auraskills.common.AuraSkillsPlugin; import dev.aurelium.auraskills.common.ability.AbilityRegistry; import dev.aurelium.auraskills.common.api.ApiAuraSkills; @@ -110,6 +116,8 @@ public class AuraSkills extends JavaPlugin implements AuraSkillsPlugin { private BukkitWorldManager worldManager; private LootTableManager lootTableManager; private ModifierManager modifierManager; + private RequirementManager requirementManager; + private boolean nbtApiEnabled; @Override public void onEnable() { @@ -135,6 +143,7 @@ public void onEnable() { traitManager = new BukkitTraitManager(this); regionManager = new RegionManager(this); modifierManager = new ModifierManager(this); + requirementManager = new RequirementManager(this); // Init registries skillRegistry = new SkillRegistry(this); @@ -173,6 +182,14 @@ public void onEnable() { // Update leaderboards leaderboardManager.updateLeaderboards(); + + // Check if NBT API is supported for the version + if (MinecraftVersion.getVersion() == MinecraftVersion.UNKNOWN) { + getLogger().warning("NBT API is not yet supported for your Minecraft version, item modifier, requirement, and some other functionality is disabled!"); + nbtApiEnabled = false; + } else { + nbtApiEnabled = true; + } } @Override @@ -243,6 +260,10 @@ private void registerEvents() { pm.registerEvents(new DamageListener(this), this); pm.registerEvents(new BlockLootHandler(this), this); pm.registerEvents(new FishingLootHandler(this), this); + pm.registerEvents(new RequirementListener(this), this); + pm.registerEvents(new ItemListener(this), this); + pm.registerEvents(new ArmorListener(configStringList(Option.MODIFIER_ARMOR_EQUIP_BLOCKED_MATERIALS)), this); + pm.registerEvents(new ArmorModifierListener(this), this); } public BukkitAudiences getAudiences() { @@ -277,6 +298,10 @@ public ModifierManager getModifierManager() { return modifierManager; } + public RequirementManager getRequirementManager() { + return requirementManager; + } + @Override public AuraSkillsApi getApi() { return api; @@ -455,4 +480,7 @@ public Locale getLocale(CommandSender sender) { } } + public boolean isNbtApiEnabled() { + return nbtApiEnabled; + } } diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/commands/CommandRegistrar.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/commands/CommandRegistrar.java index 44ce7d7bd..4fca8a173 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/commands/CommandRegistrar.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/commands/CommandRegistrar.java @@ -49,7 +49,7 @@ private void registerContexts(PaperCommandManager manager) { }); contexts.registerContext(Skill.class, c -> { String arg = c.popFirstArg(); - Skill skill = plugin.getSkillRegistry().get(NamespacedId.fromStringOrDefault(arg)); + Skill skill = plugin.getSkillRegistry().get(NamespacedId.fromDefault(arg)); if (!skill.isEnabled()) { Locale locale = plugin.getLocale(c.getSender()); c.getIssuer().sendMessage(plugin.getPrefix(locale) + plugin.getMsg(CommandMessage.UNKNOWN_SKILL, locale)); diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/item/ItemRegistryMenuProvider.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/item/ItemRegistryMenuProvider.java index 2c8394a76..d47fbfcd6 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/item/ItemRegistryMenuProvider.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/item/ItemRegistryMenuProvider.java @@ -15,6 +15,6 @@ public ItemRegistryMenuProvider(BukkitItemRegistry itemRegistry) { @Override public @Nullable ItemStack getItem(String key) { - return itemRegistry.getItem(NamespacedId.fromStringOrDefault(key)); + return itemRegistry.getItem(NamespacedId.fromDefault(key)); } } diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/ItemKeyParser.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/ItemKeyParser.java index 785868ac1..328c68305 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/ItemKeyParser.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/ItemKeyParser.java @@ -23,7 +23,7 @@ public boolean shouldUseParser(Map map) { @Override public ItemStack parseCustomItem(Map map) { - NamespacedId itemKey = NamespacedId.fromStringOrDefault(DataUtil.getString(map, "key")); + NamespacedId itemKey = NamespacedId.fromDefault(DataUtil.getString(map, "key")); ItemStack item = plugin.getItemRegistry().getItem(itemKey); if (item != null) { return item; diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/LootTableManager.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/LootTableManager.java index 7ccdd0c2f..ea37a1309 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/LootTableManager.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/LootTableManager.java @@ -69,7 +69,7 @@ public void loadLootTables() { } // Parse skill from file name String skillName = lootTableFile.getName().replace(".yml", ""); - Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromStringOrDefault(skillName)); + Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromDefault(skillName)); if (skill == null) return; FileConfiguration config = YamlConfiguration.loadConfiguration(lootTableFile); diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/SourceContextManager.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/SourceContextManager.java index 06e6ff285..085a382f7 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/SourceContextManager.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/loot/SourceContextManager.java @@ -29,7 +29,7 @@ public Set parseContext(Map parentMap) { if (parentMap.containsKey("sources")) { List sourcesList = DataUtil.getStringList(parentMap, "sources"); for (String name : sourcesList) { - NamespacedId sourceId = NamespacedId.fromStringOrDefault(name); + NamespacedId sourceId = NamespacedId.fromDefault(name); XpSource source = plugin.getSkillManager().getSourceById(sourceId); if (source != null) { contexts.add(new SourceContextWrapper(source)); diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/AbilityContext.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/AbilityContext.java index c86faec81..9d27c0661 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/AbilityContext.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/AbilityContext.java @@ -17,7 +17,7 @@ public AbilityContext(AuraSkills plugin) { @Override @Nullable public Ability parse(String menuName, String input) { - Ability ability = plugin.getAbilityRegistry().getOrNull(NamespacedId.fromStringOrDefault(input)); + Ability ability = plugin.getAbilityRegistry().getOrNull(NamespacedId.fromDefault(input)); if (ability != null && ability.isEnabled()) { return ability; } else { diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/ManaAbilityContext.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/ManaAbilityContext.java index 49acec502..55ebf497e 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/ManaAbilityContext.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/ManaAbilityContext.java @@ -17,7 +17,7 @@ public ManaAbilityContext(AuraSkills plugin) { @Override @Nullable public ManaAbility parse(String menuName, String input) { - ManaAbility manaAbility = plugin.getManaAbilityRegistry().getOrNull(NamespacedId.fromStringOrDefault(input)); + ManaAbility manaAbility = plugin.getManaAbilityRegistry().getOrNull(NamespacedId.fromDefault(input)); if (manaAbility != null && manaAbility.isEnabled()) { return manaAbility; } else { diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SkillContext.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SkillContext.java index 8713bf08e..7318c0834 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SkillContext.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SkillContext.java @@ -17,7 +17,7 @@ public SkillContext(AuraSkills plugin) { @Nullable @Override public Skill parse(String menuName, String input) { - Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromStringOrDefault(input)); + Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromDefault(input)); if (skill != null && skill.isEnabled()) { return skill; } else { diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SourceContext.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SourceContext.java index 8a79d8832..f3bf436a9 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SourceContext.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/SourceContext.java @@ -20,12 +20,12 @@ public SourceContext(AuraSkills plugin) { public XpSource parse(String menuName, String input) { String[] split = input.split(" "); // Parse Skill from first part of input - Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromStringOrDefault(split[0])); + Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromDefault(split[0])); if (skill == null || !skill.isEnabled()) { return null; } // Parse Source from second part of input - NamespacedId sourceId = NamespacedId.fromStringOrDefault(split[1]); + NamespacedId sourceId = NamespacedId.fromDefault(split[1]); // Find source from skill that matches id return skill.getSources().stream().filter(source -> source.getId().equals(sourceId)).findFirst().orElse(null); } diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/StatContext.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/StatContext.java index 7c4d426b5..97d2f40af 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/StatContext.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/contexts/StatContext.java @@ -17,7 +17,7 @@ public StatContext(AuraSkills plugin) { @Nullable @Override public Stat parse(String menuName, String input) { - Stat stat = plugin.getStatRegistry().getOrNull(NamespacedId.fromStringOrDefault(input)); + Stat stat = plugin.getStatRegistry().getOrNull(NamespacedId.fromDefault(input)); if (stat != null && stat.isEnabled()) { return stat; } else { diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/stats/SkullItem.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/stats/SkullItem.java index 193614b51..97dcf23af 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/stats/SkullItem.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/menus/stats/SkullItem.java @@ -32,7 +32,7 @@ public String onPlaceholderReplace(String placeholder, Player player, ActiveMenu } User user = plugin.getUser(player); // Handle each stat entry - Stat stat = plugin.getStatRegistry().getOrNull(NamespacedId.fromStringOrDefault(placeholder)); + Stat stat = plugin.getStatRegistry().getOrNull(NamespacedId.fromDefault(placeholder)); if (stat != null) { return TextUtil.replace(plugin.getMsg(MenuMessage.PLAYER_STAT_ENTRY, locale), "{color}", stat.getColor(locale), diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ArmorModifierListener.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ArmorModifierListener.java new file mode 100644 index 000000000..459081070 --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ArmorModifierListener.java @@ -0,0 +1,171 @@ +package dev.aurelium.auraskills.bukkit.modifier; + +import dev.aurelium.auraskills.api.event.AuraSkillsEventHandler; +import dev.aurelium.auraskills.api.event.user.UserLoadEvent; +import dev.aurelium.auraskills.api.stat.Stat; +import dev.aurelium.auraskills.api.stat.StatModifier; +import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.requirement.Requirements; +import dev.aurelium.auraskills.bukkit.user.BukkitUser; +import dev.aurelium.auraskills.bukkit.util.armor.ArmorEquipEvent; +import dev.aurelium.auraskills.bukkit.util.armor.ArmorType; +import dev.aurelium.auraskills.common.config.Option; +import dev.aurelium.auraskills.common.modifier.Multiplier; +import dev.aurelium.auraskills.common.scheduler.TaskRunnable; +import dev.aurelium.auraskills.common.stat.StatManager; +import dev.aurelium.auraskills.common.user.User; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class ArmorModifierListener implements Listener { + + private final AuraSkills plugin; + private final Modifiers modifiers; + private final Requirements requirements; + private final Multipliers multipliers; + private final StatManager statManager; + private final Map> storedArmor; + + public ArmorModifierListener(AuraSkills plugin) { + this.plugin = plugin; + this.modifiers = new Modifiers(plugin); + this.requirements = new Requirements(plugin); + this.multipliers = new Multipliers(plugin); + this.statManager = plugin.getStatManager(); + this.storedArmor = new HashMap<>(); + if (plugin.configBoolean(Option.MODIFIER_ARMOR_TIMER_ENABLED)) { + startTimer(); + } + } + + @AuraSkillsEventHandler + public void onJoin(UserLoadEvent event) { + Player player = BukkitUser.getPlayer(event.getUser()); + User user = BukkitUser.getUser(event.getUser()); + for (ItemStack armor : player.getInventory().getArmorContents()) { + if (armor == null) { + continue; + } + if (plugin.configBoolean(Option.MODIFIER_ARMOR_TIMER_ENABLED)) { + storedArmor.computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()).put(ArmorType.matchType(armor), armor.clone()); + } + if (armor.getType().equals(Material.AIR)) { + continue; + } + if (requirements.meetsRequirements(ModifierType.ARMOR, armor, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ARMOR, armor)) { + user.addStatModifier(modifier, false); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ARMOR, armor)) { + user.addMultiplier(multiplier); + } + } + } + } + + @EventHandler + public void onEquip(ArmorEquipEvent event) { + if (plugin.configBoolean(Option.MODIFIER_ARMOR_TIMER_ENABLED)) return; // Don't use if timer is enabled + Player player = event.getPlayer(); + User user = plugin.getUser(player); + // Equip + if (event.getNewArmorPiece() != null && event.getNewArmorPiece().getType() != Material.AIR) { + ItemStack item = event.getNewArmorPiece(); + if (requirements.meetsRequirements(ModifierType.ARMOR, item, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ARMOR, item)) { + user.addStatModifier(modifier); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ARMOR, item)) { + user.addMultiplier(multiplier); + } + } + } + // Un-equip + if (event.getOldArmorPiece() != null && event.getOldArmorPiece().getType() != Material.AIR) { + ItemStack item = event.getOldArmorPiece(); + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ARMOR, item)) { + user.removeStatModifier(modifier.name()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ARMOR, item)) { + user.removeMultiplier(multiplier.name()); + } + } + } + + // Timer based detection + private void startTimer() { + var task = new TaskRunnable() { + @Override + public void run() { + for (Player player : Bukkit.getOnlinePlayers()) { + UUID uuid = player.getUniqueId(); + // Get the stored armor player has or create if it doesn't exist + Map playerStoredArmor = storedArmor.computeIfAbsent(uuid, k -> new HashMap<>()); + for (ArmorType armorType : ArmorType.values()) { // Go through each armor slot + ItemStack stored = playerStoredArmor.get(armorType); // Get the stored item in the slot + ItemStack wearing = player.getInventory().getItem(armorType.getEquipmentSlot()); // Get the armor player is currently wearing + + boolean remove = true; + if (stored == null) { + remove = false; + } else if (stored.equals(wearing)) { // Don't check if stored and wearing are the same item + continue; + } + + Set statsToReload = new HashSet<>(); + // Remove modifiers and multipliers that are on stored item from player + if (remove && stored.getType() != Material.AIR) { + User user = plugin.getUser(player); + + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ARMOR, stored)) { + user.removeStatModifier(modifier.name(), false); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ARMOR, stored)) { + user.removeMultiplier(multiplier.name()); + } + } + // Add modifiers and multipliers that are on worn item to the player + if (wearing != null && wearing.getType() != Material.AIR) { + User user = plugin.getUser(player); + + if (requirements.meetsRequirements(ModifierType.ARMOR, wearing, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ARMOR, wearing)) { + user.addStatModifier(modifier, false); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ARMOR, wearing)) { + user.addMultiplier(multiplier); + } + } + } + for (Stat stat : statsToReload) { + statManager.reloadStat(plugin.getUser(player), stat); + } + // Set stored item to worn item + if (wearing != null) { + playerStoredArmor.put(armorType, wearing.clone()); + } else { + playerStoredArmor.put(armorType, new ItemStack(Material.AIR)); + } + } + } + } + }; + plugin.getScheduler().timerSync(task, 0L, plugin.configInt(Option.MODIFIER_ARMOR_TIMER_CHECK_PERIOD) * 50L, TimeUnit.MILLISECONDS); + } + + @EventHandler + public void onLeave(PlayerQuitEvent event) { + storedArmor.remove(event.getPlayer().getUniqueId()); + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ItemListener.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ItemListener.java new file mode 100644 index 000000000..69790b81f --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ItemListener.java @@ -0,0 +1,308 @@ +package dev.aurelium.auraskills.bukkit.modifier; + +import dev.aurelium.auraskills.api.event.AuraSkillsEventHandler; +import dev.aurelium.auraskills.api.event.user.UserLoadEvent; +import dev.aurelium.auraskills.api.stat.Stat; +import dev.aurelium.auraskills.api.stat.StatModifier; +import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.requirement.Requirements; +import dev.aurelium.auraskills.bukkit.skills.foraging.ForagingAbilities; +import dev.aurelium.auraskills.bukkit.skills.mining.MiningAbilities; +import dev.aurelium.auraskills.bukkit.user.BukkitUser; +import dev.aurelium.auraskills.bukkit.util.ItemUtils; +import dev.aurelium.auraskills.common.config.Option; +import dev.aurelium.auraskills.common.modifier.Multiplier; +import dev.aurelium.auraskills.common.scheduler.TaskRunnable; +import dev.aurelium.auraskills.common.stat.StatManager; +import dev.aurelium.auraskills.common.user.User; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerSwapHandItemsEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class ItemListener implements Listener { + + private final AuraSkills plugin; + private final Map heldItems; + private final Map offHandItems; + private final StatManager statManager; + private final Modifiers modifiers; + private final Requirements requirements; + private final Multipliers multipliers; + private final ForagingAbilities foragingAbilities; + private final MiningAbilities miningAbilities; + + public ItemListener(AuraSkills plugin) { + this.plugin = plugin; + heldItems = new HashMap<>(); + offHandItems = new HashMap<>(); + this.statManager = plugin.getStatManager(); + this.modifiers = new Modifiers(plugin); + this.requirements = new Requirements(plugin); + this.multipliers = new Multipliers(plugin); + this.foragingAbilities = plugin.getAbilityManager().getAbilityImpl(ForagingAbilities.class); + this.miningAbilities = plugin.getAbilityManager().getAbilityImpl(MiningAbilities.class); + scheduleTask(); + } + + @AuraSkillsEventHandler + public void onJoin(UserLoadEvent event) { + Player player = BukkitUser.getPlayer(event.getUser()); + User user = BukkitUser.getUser(event.getUser()); + + ItemStack held = player.getInventory().getItemInMainHand(); + heldItems.put(player.getUniqueId(), held.clone()); + + if (!held.getType().equals(Material.AIR)) { + if (plugin.configBoolean(Option.MODIFIER_AUTO_CONVERT_FROM_LEGACY)) { + held = requirements.convertFromLegacy(modifiers.convertFromLegacy(held)); + if (!held.equals(player.getInventory().getItemInMainHand())) { + player.getInventory().setItemInMainHand(held); + } + } + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, held)) { + user.addStatModifier(modifier, false); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, held)) { + user.addMultiplier(multiplier); + } + } + if (plugin.configBoolean(Option.MODIFIER_ITEM_ENABLE_OFF_HAND)) { + ItemStack offHandItem = player.getInventory().getItemInOffHand(); + offHandItems.put(player.getUniqueId(), offHandItem.clone()); + if (!offHandItem.getType().equals(Material.AIR)) { + if (plugin.configBoolean(Option.MODIFIER_AUTO_CONVERT_FROM_LEGACY)) { + offHandItem = requirements.convertFromLegacy(modifiers.convertFromLegacy(offHandItem)); + if (!offHandItem.equals(player.getInventory().getItemInOffHand())) { + player.getInventory().setItemInOffHand(offHandItem); + } + } + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, offHandItem)) { + StatModifier offHandModifier = new StatModifier(modifier.name() + ".Offhand", modifier.stat(), modifier.value()); + user.addStatModifier(offHandModifier); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, offHandItem)) { + Multiplier offHandMultiplier = new Multiplier(multiplier.name() + ".Offhand", multiplier.skill(), multiplier.value()); + user.addMultiplier(offHandMultiplier); + } + } + } + + } + + @EventHandler + public void onLeave(PlayerQuitEvent event) { + Player player = event.getPlayer(); + heldItems.remove(player.getUniqueId()); + offHandItems.remove(player.getUniqueId()); + } + + public void scheduleTask() { + var task = new TaskRunnable() { + @Override + public void run() { + for (Player player : Bukkit.getOnlinePlayers()) { + // Gets stored and held items + ItemStack held = player.getInventory().getItemInMainHand(); + final ItemStack finalHeld = held; + ItemStack stored = heldItems.computeIfAbsent(player.getUniqueId(), id -> finalHeld.clone()); + // If stored item is different than held + if (stored.equals(held)) { + continue; + } + Set statsToReload = new HashSet<>(); + // Remove modifiers from stored item + if (!stored.getType().equals(Material.AIR)) { + User user = plugin.getUser(player); + + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, stored)) { + user.removeStatModifier(modifier.name(), false); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, stored)) { + user.removeMultiplier(multiplier.name()); + } + // Remove valor + if (ItemUtils.isAxe(stored.getType())) { + plugin.getAbilityManager().getAbilityImpl(ForagingAbilities.class).removeValor(user); + } + // Remove stamina + if (ItemUtils.isPickaxe(stored.getType())) { + plugin.getAbilityManager().getAbilityImpl(MiningAbilities.class).removeStamina(user); + } + } + // Add modifiers from held item + if (!held.getType().equals(Material.AIR)) { + if (plugin.configBoolean(Option.MODIFIER_AUTO_CONVERT_FROM_LEGACY)) { + held = requirements.convertFromLegacy(modifiers.convertFromLegacy(held)); + if (!held.equals(player.getInventory().getItemInMainHand())) { + player.getInventory().setItemInMainHand(held); + } + } + User user = plugin.getUser(player); + if (requirements.meetsRequirements(ModifierType.ITEM, held, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, held)) { + user.addStatModifier(modifier, false); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, held)) { + user.addMultiplier(multiplier); + } + } + // Apply valor + if (ItemUtils.isAxe(held.getType())) { + foragingAbilities.removeValor(user); + } + // Apply stamina + if (ItemUtils.isPickaxe(held.getType())) { + miningAbilities.removeStamina(user); + } + } + for (Stat stat : statsToReload) { + statManager.reloadStat(plugin.getUser(player), stat); + } + // Set stored item to held item + heldItems.put(player.getUniqueId(), held.clone()); + } + } + }; + plugin.getScheduler().timerSync(task, 0L, plugin.configInt(Option.MODIFIER_ITEM_CHECK_PERIOD) * 50L, TimeUnit.MILLISECONDS); + scheduleOffHandTask(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onSwap(PlayerSwapHandItemsEvent event) { + if (event.isCancelled()) { + return; + } + if (!plugin.configBoolean(Option.MODIFIER_ITEM_ENABLE_OFF_HAND)) { + return; + } + Player player = event.getPlayer(); + User playerData = plugin.getUser(player); + + // Get items switched + ItemStack itemOffHand = event.getOffHandItem(); + ItemStack itemMainHand = event.getMainHandItem(); + // Update items + offHandItems.put(player.getUniqueId(), itemOffHand); + heldItems.put(player.getUniqueId(), itemMainHand); + // Things to prevent double reloads + Set offHandModifiers = new HashSet<>(); + Set statsToReload = new HashSet<>(); + Set offHandMultipliers = new HashSet<>(); + // Check off hand item + if (itemOffHand != null) { + if (itemOffHand.getType() != Material.AIR) { + boolean meetsRequirements = requirements.meetsRequirements(ModifierType.ITEM, itemOffHand, player); // Get whether player meets requirements + // For each modifier on the item + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, itemOffHand)) { + // Removes the old modifier from main hand + StatModifier offHandModifier = new StatModifier(modifier.name() + ".Offhand", modifier.stat(), modifier.value()); + playerData.removeStatModifier(modifier.name(), false); + // Add new one if meets requirements + if (meetsRequirements) { + playerData.addStatModifier(offHandModifier, false); + } + // Reload check stuff + offHandModifiers.add(offHandModifier.name()); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, itemOffHand)) { + Multiplier offHandMultiplier = new Multiplier(multiplier.name() + ".Offhand", multiplier.skill(), multiplier.value()); + playerData.removeMultiplier(multiplier.name()); + if (meetsRequirements) { + playerData.addMultiplier(offHandMultiplier); + } + offHandMultipliers.add(offHandMultiplier.name()); + } + } + } + // Check main hand item + if (itemMainHand != null) { + if (itemMainHand.getType() != Material.AIR) { + boolean meetsRequirements = requirements.meetsRequirements(ModifierType.ITEM, itemMainHand, player); // Get whether player meets requirements + // For each modifier on the item + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, itemMainHand)) { + // Removes the offhand modifier if wasn't already added + if (!offHandModifiers.contains(modifier.name() + ".Offhand")) { + playerData.removeStatModifier(modifier.name() + ".Offhand", false); + } + // Add if meets requirements + if (meetsRequirements) { + playerData.addStatModifier(modifier, false); + } + // Reload check stuff + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, itemMainHand)) { + if (!offHandMultipliers.contains(multiplier.name() + ".Offhand")) { + playerData.removeMultiplier(multiplier.name() + ".Offhand"); + } + if (meetsRequirements) { + playerData.addMultiplier(multiplier); + } + } + } + } + // Reload stats + for (Stat stat : statsToReload) { + statManager.reloadStat(plugin.getUser(player), stat); + } + } + + public void scheduleOffHandTask() { + var task = new TaskRunnable() { + @Override + public void run() { + if (!plugin.configBoolean(Option.MODIFIER_ITEM_ENABLE_OFF_HAND)) { + return; + } + for (Player player : Bukkit.getOnlinePlayers()) { + // Gets stored and held items + ItemStack held = player.getInventory().getItemInOffHand(); + ItemStack stored = offHandItems.computeIfAbsent(player.getUniqueId(), id -> held.clone()); + // If stored item is different than held + if (!stored.equals(held)) { + //Remove modifiers from stored item + if (!stored.getType().equals(Material.AIR)) { + User playerData = plugin.getUser(player); + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, stored)) { + playerData.removeStatModifier(modifier.name() + ".Offhand"); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, stored)) { + playerData.removeMultiplier(multiplier.name() + ".Offhand"); + } + } + // Add modifiers from held item + if (!held.getType().equals(Material.AIR)) { + User playerData = plugin.getUser(player); + if (requirements.meetsRequirements(ModifierType.ITEM, held, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, held)) { + StatModifier offHandModifier = new StatModifier(modifier.name() + ".Offhand", modifier.stat(), modifier.value()); + playerData.addStatModifier(offHandModifier); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, held)) { + Multiplier offHandMultiplier = new Multiplier(multiplier.name() + ".Offhand", multiplier.skill(), multiplier.value()); + playerData.addMultiplier(offHandMultiplier); + } + } + } + // Set stored item to held item + offHandItems.put(player.getUniqueId(), held.clone()); + } + } + } + }; + plugin.getScheduler().timerSync(task, 0L, plugin.configInt(Option.MODIFIER_ITEM_CHECK_PERIOD) * 50L, TimeUnit.MILLISECONDS); + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierManager.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierManager.java index 77f84600f..94d4c483d 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierManager.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierManager.java @@ -1,7 +1,18 @@ package dev.aurelium.auraskills.bukkit.modifier; +import dev.aurelium.auraskills.api.stat.Stat; +import dev.aurelium.auraskills.api.stat.StatModifier; import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.requirement.Requirements; +import dev.aurelium.auraskills.common.modifier.Multiplier; +import dev.aurelium.auraskills.common.user.User; +import org.bukkit.Material; import org.bukkit.entity.Player; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; + +import java.util.HashSet; +import java.util.Set; public class ModifierManager { @@ -12,7 +23,82 @@ public ModifierManager(AuraSkills plugin) { } public void reloadPlayer(Player player) { - + User user = plugin.getUser(player); + Requirements requirements = new Requirements(plugin); + Modifiers modifiers = new Modifiers(plugin); + Multipliers multipliers = new Multipliers(plugin); + + Set statsToReload = new HashSet<>(); + ItemStack item = player.getInventory().getItemInMainHand(); + if (!(item.getType() == Material.AIR)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, item)) { + user.removeStatModifier(modifier.name()); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, item)) { + user.removeMultiplier(multiplier.name()); + } + if (requirements.meetsRequirements(ModifierType.ITEM, item, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, item)) { + user.addStatModifier(modifier, false); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, item)) { + user.addMultiplier(multiplier); + } + } + } + ItemStack itemOffHand = player.getInventory().getItemInOffHand(); + if (!(itemOffHand.getType() == Material.AIR)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, itemOffHand)) { + user.removeStatModifier(modifier.name() + ".Offhand"); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, itemOffHand)) { + user.removeMultiplier(multiplier.name() + ".Offhand"); + } + if (requirements.meetsRequirements(ModifierType.ITEM, itemOffHand, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ITEM, itemOffHand)) { + StatModifier offHandModifier = new StatModifier(modifier.name() + ".Offhand", modifier.stat(), modifier.value()); + user.addStatModifier(offHandModifier, false); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ITEM, itemOffHand)) { + Multiplier offHandMultiplier = new Multiplier(multiplier.name() + ".Offhand", multiplier.skill(), multiplier.value()); + user.addMultiplier(offHandMultiplier); + } + } + } + EntityEquipment equipment = player.getEquipment(); + if (equipment != null) { + for (ItemStack armor : equipment.getArmorContents()) { + if (armor == null) { + continue; + } + if (armor.getType() == Material.AIR) { + continue; + } + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ARMOR, armor)) { + user.removeStatModifier(modifier.name()); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ARMOR, armor)) { + user.removeMultiplier(multiplier.name()); + } + if (requirements.meetsRequirements(ModifierType.ARMOR, armor, player)) { + for (StatModifier modifier : modifiers.getModifiers(ModifierType.ARMOR, armor)) { + user.addStatModifier(modifier, false); + statsToReload.add(modifier.stat()); + } + for (Multiplier multiplier : multipliers.getMultipliers(ModifierType.ARMOR, armor)) { + user.addMultiplier(multiplier); + } + } + } + } + for (Stat stat : statsToReload) { + plugin.getStatManager().reloadStat(user, stat); + } } } diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierType.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierType.java new file mode 100644 index 000000000..101935b7d --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/ModifierType.java @@ -0,0 +1,8 @@ +package dev.aurelium.auraskills.bukkit.modifier; + +public enum ModifierType { + + ITEM, + ARMOR + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Modifiers.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Modifiers.java new file mode 100644 index 000000000..e8116de78 --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Modifiers.java @@ -0,0 +1,158 @@ +package dev.aurelium.auraskills.bukkit.modifier; + +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import de.tr7zw.changeme.nbtapi.NBTType; +import dev.aurelium.auraskills.api.registry.NamespacedId; +import dev.aurelium.auraskills.api.stat.Stat; +import dev.aurelium.auraskills.api.stat.StatModifier; +import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.util.ItemUtils; +import dev.aurelium.auraskills.common.message.type.CommandMessage; +import dev.aurelium.auraskills.common.util.math.NumberUtil; +import dev.aurelium.auraskills.common.util.text.TextUtil; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +public class Modifiers { + + private final AuraSkills plugin; + + public Modifiers(AuraSkills plugin) { + this.plugin = plugin; + } + + public ItemStack addModifier(ModifierType type, ItemStack item, Stat stat, double value) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getModifiersTypeCompound(nbtItem, type); + compound.setDouble(getName(stat), value); + return nbtItem.getItem(); + } + + public ItemStack convertFromLegacy(ItemStack item) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + for (ModifierType type : ModifierType.values()) { + List legacyModifiers = getLegacyModifiers(type, nbtItem); + if (!legacyModifiers.isEmpty()) { + NBTCompound compound = ItemUtils.getModifiersTypeCompound(nbtItem, type); + for (StatModifier modifier : legacyModifiers) { + compound.setDouble(getName(modifier.stat()), modifier.value()); + } + } + } + if (nbtItem.hasTag("AureliumSkills", NBTType.NBTTagCompound)) { + nbtItem.removeKey("AureliumSkills"); + } + return nbtItem.getItem(); + } + + public ItemStack removeModifier(ModifierType type, ItemStack item, Stat stat) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getModifiersTypeCompound(nbtItem, type); + compound.removeKey(getName(stat)); + ItemUtils.removeParentCompounds(compound); + return nbtItem.getItem(); + } + + public ItemStack removeAllModifiers(ModifierType type, ItemStack item) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getModifiersTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + compound.removeKey(key); + } + ItemUtils.removeParentCompounds(compound); + return nbtItem.getItem(); + } + + public List getLegacyModifiers(ModifierType type, NBTItem nbtItem) { + if (!plugin.isNbtApiEnabled()) return new ArrayList<>(); + List modifiers = new ArrayList<>(); + NBTCompound compound = ItemUtils.getLegacyModifiersTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + Stat stat = plugin.getStatRegistry().getOrNull(NamespacedId.fromDefault(key)); + if (stat != null) { + int value = nbtItem.getInteger(key); + modifiers.add(new StatModifier(key, stat, value)); + } + } + return modifiers; + } + + public List getModifiers(ModifierType type, ItemStack item) { + if (!plugin.isNbtApiEnabled()) return new ArrayList<>(); + NBTItem nbtItem = new NBTItem(item); + List modifiers = new ArrayList<>(); + NBTCompound compound = ItemUtils.getModifiersTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + Stat stat = plugin.getStatRegistry().getOrNull(NamespacedId.fromDefault(key)); + if (stat != null) { + double value = compound.getDouble(key); + if (type == ModifierType.ITEM) { + modifiers.add(new StatModifier("AuraSkills.Modifiers.Item." + getName(stat), stat, value)); + } else if (type == ModifierType.ARMOR) { + String slot = "Helmet"; + String mat = item.getType().toString(); + if (mat.contains("CHESTPLATE")) { + slot = "Chestplate"; + } else if (mat.contains("LEGGINGS")) { + slot = "Leggings"; + } else if (mat.contains("BOOTS")) { + slot = "Boots"; + } + modifiers.add(new StatModifier("AuraSkills.Modifiers.Armor." + slot + "." + getName(stat), stat, value)); + } + } + } + return modifiers; + } + + public void addLore(ModifierType type, ItemStack item, Stat stat, double value, Locale locale) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + List lore; + if (meta.getLore() != null) { + if (!meta.getLore().isEmpty()) lore = meta.getLore(); + else lore = new LinkedList<>(); + } + else { + lore = new LinkedList<>(); + } + CommandMessage message; + if (value >= 0) { + message = CommandMessage.valueOf(type.name() + "_MODIFIER_ADD_LORE"); + } else { + message = CommandMessage.valueOf(type.name() + "_MODIFIER_ADD_LORE_SUBTRACT"); + } + lore.add(0, TextUtil.replace(plugin.getMsg(message, locale), + "{stat}", stat.getDisplayName(locale), + "{value}", NumberUtil.format1(Math.abs(value)), + "{color}", stat.getColor(locale))); + meta.setLore(lore); + } + item.setItemMeta(meta); + } + + public void removeLore(ItemStack item, Stat stat, Locale locale) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + List lore = meta.getLore(); + if (lore != null && !lore.isEmpty()) lore.removeIf(line -> line.contains(stat.getDisplayName(locale))); + meta.setLore(lore); + } + item.setItemMeta(meta); + } + + private String getName(Stat stat) { + return TextUtil.capitalize(stat.name().toLowerCase(Locale.ROOT)); + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Multipliers.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Multipliers.java new file mode 100644 index 000000000..4b6843dcf --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/modifier/Multipliers.java @@ -0,0 +1,157 @@ +package dev.aurelium.auraskills.bukkit.modifier; + +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import dev.aurelium.auraskills.api.registry.NamespacedId; +import dev.aurelium.auraskills.api.skill.Skill; +import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.util.ItemUtils; +import dev.aurelium.auraskills.common.config.Option; +import dev.aurelium.auraskills.common.message.type.CommandMessage; +import dev.aurelium.auraskills.common.modifier.Multiplier; +import dev.aurelium.auraskills.common.util.math.NumberUtil; +import dev.aurelium.auraskills.common.util.text.TextUtil; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +public class Multipliers { + + private final AuraSkills plugin; + + public Multipliers(AuraSkills plugin) { + this.plugin = plugin; + } + + public List getMultipliers(ModifierType type, ItemStack item) { + if (!plugin.configBoolean(Option.MODIFIER_MULTIPLIER_ENABLED) || !plugin.isNbtApiEnabled()) { // Return empty list if disabled + return new ArrayList<>(); + } + NBTItem nbtItem = new NBTItem(item); + List multipliers = new ArrayList<>(); + NBTCompound compound = ItemUtils.getMultipliersTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + double value = compound.getDouble(key); + Skill skill = plugin.getSkillRegistry().get(NamespacedId.fromDefault(key)); // Null if Global + String skillName = getNBTName(skill); + + if (type == ModifierType.ITEM) { + multipliers.add(new Multiplier("AuraSkills.Multipliers.Item." + skillName, skill, value)); + } else if (type == ModifierType.ARMOR) { + String slot = "Helmet"; + String mat = item.getType().toString(); + if (mat.contains("CHESTPLATE")) { + slot = "Chestplate"; + } else if (mat.contains("LEGGINGS")) { + slot = "Leggings"; + } else if (mat.contains("BOOTS")) { + slot = "Boots"; + } + multipliers.add(new Multiplier("AuraSkills.Multipliers.Armor." + slot + "." + skillName, skill, value)); + } + } + return multipliers; + } + + public ItemStack convertFromLegacy(ItemStack item) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + for (ModifierType type : ModifierType.values()) { + NBTCompound legacyCompound = ItemUtils.getLegacyMultipliersTypeCompound(nbtItem, type); + NBTCompound compound = ItemUtils.getMultipliersTypeCompound(nbtItem, type); + for (String key : legacyCompound.getKeys()) { + compound.setInteger(key, legacyCompound.getInteger(key)); + } + } + if (nbtItem.hasTag("AureliumSkills")) { + nbtItem.removeKey("AureliumSkills"); + } + return nbtItem.getItem(); + } + + public ItemStack addMultiplier(ModifierType type, ItemStack item, @Nullable Skill skill, double value) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getMultipliersTypeCompound(nbtItem, type); + compound.setDouble(getNBTName(skill), value); + return nbtItem.getItem(); + } + + public ItemStack removeMultiplier(ModifierType type, ItemStack item, @Nullable Skill skill) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getMultipliersTypeCompound(nbtItem, type); + compound.removeKey(getNBTName(skill)); + ItemUtils.removeParentCompounds(compound); + return nbtItem.getItem(); + } + + public ItemStack removeAllMultipliers(ModifierType type, ItemStack item) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getMultipliersTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + compound.removeKey(key); + } + ItemUtils.removeParentCompounds(compound); + return nbtItem.getItem(); + } + + public void addLore(ModifierType type, ItemStack item, Skill skill, double value, Locale locale) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + List lore; + if (meta.getLore() != null) { + if (!meta.getLore().isEmpty()) { + lore = meta.getLore(); + } else { + lore = new LinkedList<>(); + } + } else { + lore = new LinkedList<>(); + } + if (skill != null) { // Skill multiplier + CommandMessage message; + if (value >= 0) { + message = CommandMessage.valueOf(type.name() + "_MULTIPLIER_ADD_SKILL_LORE"); + } else { + message = CommandMessage.valueOf(type.name() + "_MULTIPLIER_ADD_SKILL_LORE_SUBTRACT"); + } + if (!lore.isEmpty()) { + lore.add(" "); + } + lore.add(TextUtil.replace(plugin.getMsg(message, locale), + "{skill}", skill.getDisplayName(locale), + "{value}", NumberUtil.format1(Math.abs(value)))); + } else { // Global multiplier + CommandMessage message; + if (value >= 0) { + message = CommandMessage.valueOf(type.name() + "_MULTIPLIER_ADD_GLOBAL_LORE"); + } else { + message = CommandMessage.valueOf(type.name() + "_MULTIPLIER_ADD_GLOBAL_LORE_SUBTRACT"); + } + if (!lore.isEmpty()) { + lore.add(" "); + } + lore.add(TextUtil.replace(plugin.getMsg(message, locale), + "{value}", NumberUtil.format1(Math.abs(value)))); + } + meta.setLore(lore); + } + item.setItemMeta(meta); + } + + private String getNBTName(@Nullable Skill skill) { + if (skill != null) { + return TextUtil.capitalize(skill.toString().toLowerCase(Locale.ROOT)); + } else { + return "Global"; + } + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/GlobalRequirement.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/GlobalRequirement.java new file mode 100644 index 000000000..fea7cdfb5 --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/GlobalRequirement.java @@ -0,0 +1,33 @@ +package dev.aurelium.auraskills.bukkit.requirement; + +import dev.aurelium.auraskills.api.skill.Skill; +import dev.aurelium.auraskills.bukkit.modifier.ModifierType; +import org.bukkit.Material; + +import java.util.Map; + +public class GlobalRequirement { + + private final ModifierType type; + private final Material material; + private final Map requirements; + + public GlobalRequirement(ModifierType type, Material material, Map requirements) { + this.type = type; + this.material = material; + this.requirements = requirements; + } + + public ModifierType getType() { + return type; + } + + public Material getMaterial() { + return material; + } + + public Map getRequirements() { + return requirements; + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementListener.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementListener.java new file mode 100644 index 000000000..a4bd00936 --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementListener.java @@ -0,0 +1,161 @@ +package dev.aurelium.auraskills.bukkit.requirement; + +import dev.aurelium.auraskills.api.skill.Skill; +import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.modifier.ModifierType; +import dev.aurelium.auraskills.bukkit.util.armor.ArmorEquipEvent; +import dev.aurelium.auraskills.common.config.Option; +import dev.aurelium.auraskills.common.message.MessageKey; +import dev.aurelium.auraskills.common.message.type.CommandMessage; +import dev.aurelium.auraskills.common.util.math.RomanNumber; +import dev.aurelium.auraskills.common.util.text.TextUtil; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.*; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.Locale; +import java.util.Map; + +public class RequirementListener implements Listener { + + private final AuraSkills plugin; + private final RequirementManager manager; + private final Requirements requirements; + + public RequirementListener(AuraSkills plugin) { + this.plugin = plugin; + this.manager = plugin.getRequirementManager(); + this.requirements = new Requirements(plugin); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onEquip(ArmorEquipEvent event) { + if (event.isCancelled()) return; + Player player = event.getPlayer(); + ItemStack item = event.getNewArmorPiece(); + if (item == null) { + return; + } + if (item.getType() == Material.AIR) { + return; + } + if (!requirements.meetsRequirements(ModifierType.ARMOR, item, player)) { + Locale locale = plugin.getUser(player).getLocale(); + event.setCancelled(true); + Integer timer = manager.getErrorMessageTimer().get(player.getUniqueId()); + if (timer != null) { + if (timer.equals(0)) { + sendMessage(CommandMessage.ARMOR_REQUIREMENT_EQUIP, CommandMessage.ARMOR_REQUIREMENT_ENTRY, ModifierType.ARMOR, player, locale, item); + manager.getErrorMessageTimer().put(player.getUniqueId(), 8); + } + } + else { + sendMessage(CommandMessage.ARMOR_REQUIREMENT_EQUIP, CommandMessage.ARMOR_REQUIREMENT_ENTRY, ModifierType.ARMOR, player, locale, item); + manager.getErrorMessageTimer().put(player.getUniqueId(), 8); + } + } + } + + private void sendMessage(MessageKey baseMessage, MessageKey entryMessage, ModifierType modifierType, Player player, Locale locale, ItemStack item) { + // Build requirements message that shows skills and levels + StringBuilder requirementsString = new StringBuilder(); + Map requirementMap = requirements.getRequirements(modifierType, item); + for (Map.Entry entry : requirementMap.entrySet()) { + requirementsString.append(TextUtil.replace(plugin.getMsg(entryMessage, locale), + "{skill}", entry.getKey().getDisplayName(locale), "{level}", RomanNumber.toRoman(entry.getValue(), plugin))); + } + Map globalRequirementMap = requirements.getGlobalRequirements(modifierType, item); + for (Map.Entry entry : globalRequirementMap.entrySet()) { + requirementsString.append(TextUtil.replace(plugin.getMsg(entryMessage, locale), + "{skill}", entry.getKey().getDisplayName(locale), "{level}", RomanNumber.toRoman(entry.getValue(), plugin))); + } + if (requirementsString.length() >= 2) { + requirementsString.delete(requirementsString.length() - 2, requirementsString.length()); + } + + player.sendMessage(plugin.getPrefix(locale) + TextUtil.replace(plugin.getMsg(baseMessage, locale) + , "{requirements}", requirementsString.toString())); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onBlockBreak(BlockBreakEvent event) { + if (event.isCancelled()) return; + if (plugin.configBoolean(Option.REQUIREMENT_ITEM_PREVENT_TOOL_USE)) { + Player player = event.getPlayer(); + ItemStack item = player.getInventory().getItemInMainHand(); + if (item.getType() == Material.AIR) return; + checkItemRequirements(player, item, event); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onPlace(BlockPlaceEvent event) { + if (event.isCancelled()) return; + if (plugin.configBoolean(Option.REQUIREMENT_ITEM_PREVENT_BLOCK_PLACE)) { + Player player = event.getPlayer(); + ItemStack item = event.getItemInHand(); + if (item.getType() == Material.AIR) return; + checkItemRequirements(player, item, event); + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onAttack(EntityDamageByEntityEvent event) { + if (event.isCancelled()) return; + if (plugin.configBoolean(Option.REQUIREMENT_ITEM_PREVENT_WEAPON_USE)) { + if (event.getDamager() instanceof Player player) { + ItemStack item = player.getInventory().getItemInMainHand(); + if (item.getType() == Material.AIR) return; + checkItemRequirements(player, item, event); + } + } + } + + @EventHandler(priority = EventPriority.HIGH) + public void onShoot(EntityShootBowEvent event) { + if (event.isCancelled()) return; + if (!plugin.configBoolean(Option.REQUIREMENT_ITEM_PREVENT_WEAPON_USE)) return; + if (!(event.getEntity() instanceof Player player)) return; + + ItemStack item = event.getBow(); + if (item == null) return; + if (item.getType() == Material.AIR) return; + + checkItemRequirements(player, item, event); + } + + @EventHandler(priority = EventPriority.HIGH) + public void onInteract(PlayerInteractEvent event) { + if (event.useItemInHand() == Event.Result.DENY) return; + if (!plugin.configBoolean(Option.REQUIREMENT_ITEM_PREVENT_INTERACT)) return; + + ItemStack item = event.getItem(); + if (item == null) return; + if (item.getType() == Material.AIR) return; + checkItemRequirements(event.getPlayer(), event.getItem(), event); + } + + private void checkItemRequirements(Player player, ItemStack item, Cancellable event) { + if (!requirements.meetsRequirements(ModifierType.ITEM, item, player)) { + Locale locale = plugin.getUser(player).getLocale(); + event.setCancelled(true); + Integer timer = manager.getErrorMessageTimer().get(player.getUniqueId()); + if (timer != null) { + if (timer.equals(0)) { + sendMessage(CommandMessage.ITEM_REQUIREMENT_USE, CommandMessage.ITEM_REQUIREMENT_ENTRY, ModifierType.ITEM, player, locale, item); + manager.getErrorMessageTimer().put(player.getUniqueId(), 8); + } + } else { + sendMessage(CommandMessage.ITEM_REQUIREMENT_USE, CommandMessage.ITEM_REQUIREMENT_ENTRY, ModifierType.ITEM, player, locale, item); + manager.getErrorMessageTimer().put(player.getUniqueId(), 8); + } + } + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementManager.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementManager.java new file mode 100644 index 000000000..153e906ca --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/RequirementManager.java @@ -0,0 +1,95 @@ +package dev.aurelium.auraskills.bukkit.requirement; + +import dev.aurelium.auraskills.api.registry.NamespacedId; +import dev.aurelium.auraskills.api.skill.Skill; +import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.modifier.ModifierType; +import dev.aurelium.auraskills.common.scheduler.TaskRunnable; +import org.bukkit.Material; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class RequirementManager implements Listener { + + private Set globalRequirements; + private final Map errorMessageTimer; + private final AuraSkills plugin; + + public RequirementManager(AuraSkills plugin) { + errorMessageTimer = new HashMap<>(); + this.plugin = plugin; + tickTimer(); + } + + public void load() { + FileConfiguration config = plugin.getConfig(); + this.globalRequirements = new HashSet<>(); + for (ModifierType type : ModifierType.values()) { + List list = config.getStringList("requirement." + type.name().toLowerCase(Locale.ROOT) + ".global"); + for (String text : list) { + String[] splitText = text.split(" "); + Material material = Material.valueOf(splitText[0].toUpperCase(Locale.ROOT)); + Map requirements = new HashMap<>(); + for (int i = 1; i < splitText.length; i++) { + String requirementText = splitText[i]; + try { + Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromDefault(requirementText.split(":")[0])); + if (skill != null) { + int level = Integer.parseInt(requirementText.split(":")[1]); + requirements.put(skill, level); + } + } catch (Exception e) { + plugin.logger().warn("Error parsing global skill " + type.name().toLowerCase(Locale.ROOT) + " requirement skill level pair with text " + requirementText); + } + } + GlobalRequirement globalRequirement = new GlobalRequirement(type, material, requirements); + globalRequirements.add(globalRequirement); + + } + } + } + + public Set getGlobalRequirements() { + return globalRequirements; + } + + public Set getGlobalRequirementsType(ModifierType type) { + Set matched = new HashSet<>(); + for (GlobalRequirement requirement : globalRequirements) { + if (requirement.getType() == type) { + matched.add(requirement); + } + } + return matched; + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + errorMessageTimer.remove(event.getPlayer().getUniqueId()); + } + + public void tickTimer() { + var task = new TaskRunnable() { + @Override + public void run() { + for (UUID id : errorMessageTimer.keySet()) { + int timer = errorMessageTimer.get(id); + if (timer != 0) { + errorMessageTimer.put(id, errorMessageTimer.get(id) - 1); + } + } + } + }; + plugin.getScheduler().timerSync(task, 0, 5 * 50, TimeUnit.MILLISECONDS); + } + + public Map getErrorMessageTimer() { + return errorMessageTimer; + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/Requirements.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/Requirements.java new file mode 100644 index 000000000..4ca116785 --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/requirement/Requirements.java @@ -0,0 +1,170 @@ +package dev.aurelium.auraskills.bukkit.requirement; + +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import dev.aurelium.auraskills.api.registry.NamespacedId; +import dev.aurelium.auraskills.api.skill.Skill; +import dev.aurelium.auraskills.bukkit.AuraSkills; +import dev.aurelium.auraskills.bukkit.modifier.ModifierType; +import dev.aurelium.auraskills.bukkit.util.ItemUtils; +import dev.aurelium.auraskills.common.config.Option; +import dev.aurelium.auraskills.common.message.type.CommandMessage; +import dev.aurelium.auraskills.common.user.User; +import dev.aurelium.auraskills.common.util.text.TextUtil; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; + +public class Requirements { + + private final AuraSkills plugin; + private final RequirementManager manager; + + public Requirements(AuraSkills plugin) { + this.plugin = plugin; + this.manager = plugin.getRequirementManager(); + } + + public Map getRequirements(ModifierType type, ItemStack item) { + if (!plugin.isNbtApiEnabled()) return new HashMap<>(); + NBTItem nbtItem = new NBTItem(item); + Map requirements = new HashMap<>(); + NBTCompound compound = ItemUtils.getRequirementsTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + try { + Skill skill = plugin.getSkillRegistry().getOrNull(NamespacedId.fromDefault(key)); + if (skill != null) { + Integer value = compound.getInteger(key); + requirements.put(skill, value); + } + } + catch (Exception ignored) { } + } + return requirements; + } + + public Map getGlobalRequirements(ModifierType type, ItemStack item) { + Map requirements = new HashMap<>(); + for (GlobalRequirement global : manager.getGlobalRequirementsType(type)) { + if (global.getMaterial() == item.getType()) { + requirements.putAll(global.getRequirements()); + } + } + return requirements; + } + + + public ItemStack addRequirement(ModifierType type, ItemStack item, Skill skill, int level) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getRequirementsTypeCompound(nbtItem, type); + compound.setInteger(getName(skill), level); + return nbtItem.getItem(); + } + + public ItemStack removeRequirement(ModifierType type, ItemStack item, Skill skill) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getRequirementsTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + if (key.equals(getName(skill))) { + compound.removeKey(key); + } + } + ItemUtils.removeParentCompounds(compound); + return nbtItem.getItem(); + } + + public ItemStack removeAllRequirements(ModifierType type, ItemStack item) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getRequirementsTypeCompound(nbtItem, type); + compound.getKeys().forEach(compound::removeKey); + ItemUtils.removeParentCompounds(compound); + return nbtItem.getItem(); + } + + public boolean hasRequirement(ModifierType type, ItemStack item, Skill skill) { + if (!plugin.isNbtApiEnabled()) return false; + NBTItem nbtItem = new NBTItem(item); + NBTCompound compound = ItemUtils.getRequirementsTypeCompound(nbtItem, type); + for (String key : compound.getKeys()) { + if (key.equals(getName(skill))) { + return true; + } + } + return false; + } + + public ItemStack convertFromLegacy(ItemStack item) { + if (!plugin.isNbtApiEnabled()) return item; + NBTItem nbtItem = new NBTItem(item); + for (ModifierType type : ModifierType.values()) { + NBTCompound legacyCompound = ItemUtils.getLegacyModifiersTypeCompound(nbtItem, type); + NBTCompound compound = ItemUtils.getRequirementsTypeCompound(nbtItem, type); + for (String key : legacyCompound.getKeys()) { + compound.setInteger(key, legacyCompound.getInteger(key)); + } + } + if (nbtItem.hasTag("AureliumSkills")) { + nbtItem.removeKey("AureliumSkills"); + } + return nbtItem.getItem(); + } + + public void addLore(ModifierType type, ItemStack item, Skill skill, int level, Locale locale) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + String text = TextUtil.replace(plugin.getMsg(CommandMessage.valueOf(type.name() + "_REQUIREMENT_ADD_LORE"), locale), "{skill}", skill.getDisplayName(locale), "{level}", String.valueOf(level)); + List lore; + if (meta.hasLore()) lore = meta.getLore(); + else lore = new ArrayList<>(); + if (lore != null) { + lore.add(text); + meta.setLore(lore); + } + item.setItemMeta(meta); + } + } + + public void removeLore(ItemStack item, Skill skill) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + List lore = meta.getLore(); + if (lore != null) { + for (int i = 0; i < lore.size(); i++) { + String line = lore.get(i); + if (line.contains("Requires") && line.contains(TextUtil.capitalize(skill.name().toLowerCase(Locale.ROOT)))) { + lore.remove(line); + } + } + meta.setLore(lore); + } + item.setItemMeta(meta); + } + } + + public boolean meetsRequirements(ModifierType type, ItemStack item, Player player) { + if (!plugin.configBoolean(Option.REQUIREMENT_ENABLED)) return true; + User user = plugin.getUser(player); + // Check global requirements + for (Map.Entry entry : getGlobalRequirements(type, item).entrySet()) { + if (user.getSkillLevel(entry.getKey()) < entry.getValue()) { + return false; + } + } + for (Map.Entry entry : getRequirements(type, item).entrySet()) { + if (user.getSkillLevel(entry.getKey()) < entry.getValue()) { + return false; + } + } + return true; + } + + private String getName(Skill skill) { + return TextUtil.capitalize(skill.name().toLowerCase(Locale.ROOT)); + } + +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/ForagingAbilities.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/ForagingAbilities.java index a32ff23f9..5ccdc2f8f 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/ForagingAbilities.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/ForagingAbilities.java @@ -1,6 +1,5 @@ package dev.aurelium.auraskills.bukkit.skills.foraging; -import com.archyx.aureliumskills.data.PlayerData; import com.cryptomorin.xseries.XMaterial; import dev.aurelium.auraskills.api.ability.Abilities; import dev.aurelium.auraskills.api.event.loot.LootDropEvent; @@ -109,8 +108,8 @@ public void applyValor(User user) { user.addStatModifier(new StatModifier("foraging-valor", Stats.STRENGTH, (int) getValue(ability, user))); } - public void removeValor(PlayerData playerData) { - playerData.removeStatModifier("foraging-valor"); + public void removeValor(User user) { + user.removeStatModifier("foraging-valor"); } } diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/Treecapitator.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/Treecapitator.java index 775295047..16d68263f 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/Treecapitator.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/skills/foraging/Treecapitator.java @@ -180,9 +180,9 @@ private void setMaxBlocks() { for (String woodName : woodNames) { if (matName.contains(woodName)) { if (woodName.equals("CRIMSON") || woodName.equals("WARPED")) { - source = plugin.getSkillManager().getSourceById(NamespacedId.fromStringOrDefault(woodName.toLowerCase(Locale.ROOT) + "_stem")); + source = plugin.getSkillManager().getSourceById(NamespacedId.fromDefault(woodName.toLowerCase(Locale.ROOT) + "_stem")); } else { - source = plugin.getSkillManager().getSourceById(NamespacedId.fromStringOrDefault(woodName.toLowerCase(Locale.ROOT) + "_log")); + source = plugin.getSkillManager().getSourceById(NamespacedId.fromDefault(woodName.toLowerCase(Locale.ROOT) + "_log")); } break; } diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/ui/BossBarManager.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/ui/BossBarManager.java index b10e8cca3..038f61989 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/ui/BossBarManager.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/ui/BossBarManager.java @@ -65,7 +65,7 @@ public void loadOptions() { BossBarColor color = BossBarColor.GREEN; BossBarStyle style = BossBarStyle.SOLID; try { - skill = plugin.getSkillRegistry().get(NamespacedId.fromStringOrDefault(splitEntry[0].toUpperCase(Locale.ROOT))); + skill = plugin.getSkillRegistry().get(NamespacedId.fromDefault(splitEntry[0].toUpperCase(Locale.ROOT))); } catch (IllegalArgumentException e) { plugin.logger().warn("Error loading boss bar format in config.yml: " + splitEntry[0] + " is not a valid Skill"); skill = Skills.FARMING; diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ConfigurateItemParser.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ConfigurateItemParser.java index 546d71fe9..6af5b3ba8 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ConfigurateItemParser.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ConfigurateItemParser.java @@ -118,7 +118,7 @@ public ItemStack parseBaseItem(ConfigurationNode config) { @Nullable private ItemStack parseItemKey(String key) { - return plugin.getItemRegistry().getItem(NamespacedId.fromStringOrDefault(key)); + return plugin.getItemRegistry().getItem(NamespacedId.fromDefault(key)); } @SuppressWarnings("deprecation") diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ItemUtils.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ItemUtils.java index 08ee622fe..f45995857 100644 --- a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ItemUtils.java +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/ItemUtils.java @@ -1,10 +1,10 @@ package dev.aurelium.auraskills.bukkit.util; -import com.archyx.aureliumskills.modifier.ModifierType; -import com.archyx.aureliumskills.util.text.TextUtil; import com.cryptomorin.xseries.XMaterial; import de.tr7zw.changeme.nbtapi.NBTCompound; import de.tr7zw.changeme.nbtapi.NBTItem; +import dev.aurelium.auraskills.bukkit.modifier.ModifierType; +import dev.aurelium.auraskills.common.util.text.TextUtil; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -62,6 +62,14 @@ public static NBTCompound getCompound(NBTCompound root, String name) { } public static NBTCompound getRootCompound(NBTItem item) { + NBTCompound compound = item.getCompound("AuraSkills"); + if (compound == null) { + compound = item.addCompound("AuraSkills"); + } + return compound; + } + + public static NBTCompound getLegacyRootCompound(NBTItem item) { NBTCompound compound = item.getCompound("AureliumSkills"); if (compound == null) { compound = item.addCompound("AureliumSkills"); @@ -73,10 +81,18 @@ public static NBTCompound getModifiersCompound(NBTItem item) { return getCompound(getRootCompound(item), "Modifiers"); } + public static NBTCompound getLegacyModifiersCompound(NBTItem item) { + return getCompound(getLegacyRootCompound(item), "Modifiers"); + } + public static NBTCompound getModifiersTypeCompound(NBTItem item, ModifierType type) { return getCompound(getModifiersCompound(item), TextUtil.capitalize(type.name().toLowerCase(Locale.ROOT))); } + public static NBTCompound getLegacyModifiersTypeCompound(NBTItem item, ModifierType type) { + return getCompound(getLegacyModifiersCompound(item), TextUtil.capitalize(type.name().toLowerCase(Locale.ROOT))); + } + public static NBTCompound getRequirementsCompound(NBTItem item) { return getCompound(getRootCompound(item), "Requirements"); } @@ -85,6 +101,15 @@ public static NBTCompound getRequirementsTypeCompound(NBTItem item, ModifierType return getCompound(getRequirementsCompound(item), TextUtil.capitalize(type.name().toLowerCase(Locale.ROOT))); } + public static NBTCompound getLegacyRequirementsCompound(NBTItem item) { + return getCompound(getLegacyRootCompound(item), "Requirements"); + } + + + public static NBTCompound getLegacyRequirementsTypeCompound(NBTItem item, ModifierType type) { + return getCompound(getLegacyRequirementsCompound(item), TextUtil.capitalize(type.name().toLowerCase(Locale.ROOT))); + } + public static NBTCompound getMultipliersCompound(NBTItem item) { return getCompound(getRootCompound(item), "Multipliers"); } @@ -93,13 +118,21 @@ public static NBTCompound getMultipliersTypeCompound(NBTItem item, ModifierType return getCompound(getMultipliersCompound(item), TextUtil.capitalize(type.name().toLowerCase(Locale.ROOT))); } + public static NBTCompound getLegacyMultipliersCompound(NBTItem item) { + return getCompound(getLegacyRootCompound(item), "Multipliers"); + } + + public static NBTCompound getLegacyMultipliersTypeCompound(NBTItem item, ModifierType type) { + return getCompound(getLegacyMultipliersCompound(item), TextUtil.capitalize(type.name().toLowerCase(Locale.ROOT))); + } + public static void removeParentCompounds(NBTCompound compound) { - if (compound.getKeys().size() == 0) { + if (compound.getKeys().isEmpty()) { NBTCompound parent = compound.getParent(); parent.removeKey(compound.getName()); - if (parent.getKeys().size() == 0) { + if (parent.getKeys().isEmpty()) { parent.getParent().removeKey(parent.getName()); - if (parent.getParent().getKeys().size() == 0) { + if (parent.getParent().getKeys().isEmpty()) { parent.getParent().getParent().removeKey(parent.getParent().getName()); } } diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorEquipEvent.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorEquipEvent.java new file mode 100644 index 000000000..3b9f9e2a0 --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorEquipEvent.java @@ -0,0 +1,143 @@ +package dev.aurelium.auraskills.bukkit.util.armor; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.HandlerList; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.inventory.ItemStack; + +/** + * @author Arnah + * @since Jul 30, 2015 + */ +public final class ArmorEquipEvent extends PlayerEvent implements Cancellable{ + + private static final HandlerList handlers = new HandlerList(); + private boolean cancel = false; + private final EquipMethod equipType; + private final ArmorType type; + private ItemStack oldArmorPiece, newArmorPiece; + + /** + * @param player The player who put on / removed the armor. + * @param type The ArmorType of the armor added + * @param oldArmorPiece The ItemStack of the armor removed. + * @param newArmorPiece The ItemStack of the armor added. + */ + public ArmorEquipEvent(final Player player, final EquipMethod equipType, final ArmorType type, final ItemStack oldArmorPiece, final ItemStack newArmorPiece){ + super(player); + this.equipType = equipType; + this.type = type; + this.oldArmorPiece = oldArmorPiece; + this.newArmorPiece = newArmorPiece; + } + + /** + * Gets a list of handlers handling this event. + * + * @return A list of handlers handling this event. + */ + public static HandlerList getHandlerList(){ + return handlers; + } + + /** + * Gets a list of handlers handling this event. + * + * @return A list of handlers handling this event. + */ + @Override + public final HandlerList getHandlers(){ + return handlers; + } + + /** + * Sets if this event should be cancelled. + * + * @param cancel If this event should be cancelled. + */ + @Override + public final void setCancelled(final boolean cancel){ + this.cancel = cancel; + } + + /** + * Gets if this event is cancelled. + * + * @return If this event is cancelled + */ + @Override + public final boolean isCancelled(){ + return cancel; + } + + public final ArmorType getType(){ + return type; + } + + /** + * Returns the last equipped armor piece, could be a piece of armor, or null + */ + public final ItemStack getOldArmorPiece(){ + return oldArmorPiece; + } + + public final void setOldArmorPiece(final ItemStack oldArmorPiece){ + this.oldArmorPiece = oldArmorPiece; + } + + /** + * Returns the newly equipped armor, could be a piece of armor, or null + */ + public final ItemStack getNewArmorPiece(){ + return newArmorPiece; + } + + public final void setNewArmorPiece(final ItemStack newArmorPiece){ + this.newArmorPiece = newArmorPiece; + } + + /** + * Gets the method used to either equip or unequip an armor piece. + */ + public EquipMethod getMethod(){ + return equipType; + } + + public enum EquipMethod{// These have got to be the worst documentations ever. + /** + * When you shift click an armor piece to equip or unequip + */ + SHIFT_CLICK, + /** + * When you drag and drop the item to equip or unequip + */ + DRAG, + /** + * When you manually equip or unequip the item. Use to be DRAG + */ + PICK_DROP, + /** + * When you right click an armor piece in the hotbar without the inventory open to equip. + */ + HOTBAR, + /** + * When you press the hotbar slot number while hovering over the armor slot to equip or unequip + */ + HOTBAR_SWAP, + /** + * When in range of a dispenser that shoots an armor piece to equip.
+ * Requires the spigot version to have {@link org.bukkit.event.block.BlockDispenseArmorEvent} implemented. + */ + DISPENSER, + /** + * When an armor piece is removed due to it losing all durability. + */ + BROKE, + /** + * When you die causing all armor to unequip + */ + DEATH, + ; + } +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorListener.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorListener.java new file mode 100644 index 000000000..f1c9ec95e --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorListener.java @@ -0,0 +1,319 @@ +package dev.aurelium.auraskills.bukkit.util.armor; + +import dev.aurelium.auraskills.bukkit.util.VersionUtils; +import dev.aurelium.auraskills.bukkit.util.armor.ArmorEquipEvent.EquipMethod; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Event.Result; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockDispenseArmorEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.inventory.*; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.*; + +/** + * @author Arnah + * @since Jul 30, 2015 + */ +public class ArmorListener implements Listener{ + + private final List blockedMaterials; + + private static final String[] defBlocked = new String[] { + "CHEST", "TRAPPED_CHEST", "ENDER_CHEST", + "FURNACE", "WORKBENCH", "CRAFTING_TABLE", + "ANVIL", "ENCHANTING_TABLE", "ENCHANTMENT_TABLE", + "*SHULKER_BOX", "*BED", "BED_BLOCK", + "NOTE_BLOCK", "BREWING_STAND", "HOPPER", + "DISPENSER", "DROPPER", "*BUTTON", + "REPEATER", "?DIODE", "LEVER", + "?COMPARATOR", "*DOOR!?IRON_DOOR", "*FENCE_GATE", + "?DAYLIGHT_DETECTOR", "BEACON", "?COMMAND", + "CARTOGRAPHY_TABLE", "LECTERN", "GRINDSTONE", + "SMITHING_TABLE", "STONECUTTER", "BLAST_FURNACE", + "BELL", "SMOKER", "BARREL", "LOOM", + "CHIPPED_ANVIL", "DAMAGED_ANVIL", "FLOWER_POT", + "*SIGN" + }; + + public ArmorListener(List blockedMaterials){ + this.blockedMaterials = new LinkedList<>(); + this.blockedMaterials.addAll(Arrays.asList(defBlocked)); + this.blockedMaterials.addAll(blockedMaterials); + } + //Event Priority is highest because other plugins might cancel the events before we check. + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public final void inventoryClick(final InventoryClickEvent e) { + boolean shift = false, numberkey = false; + if (e.getAction() == InventoryAction.NOTHING) return;// Why does this get called if nothing happens?? + if (e.getClick().equals(ClickType.SHIFT_LEFT) || e.getClick().equals(ClickType.SHIFT_RIGHT)){ + shift = true; + } + if (e.getClick().equals(ClickType.NUMBER_KEY)){ + numberkey = true; + } + if (e.getSlotType() != SlotType.ARMOR && e.getSlotType() != SlotType.QUICKBAR && e.getSlotType() != SlotType.CONTAINER) return; + if (e.getClickedInventory() != null && !e.getClickedInventory().getType().equals(InventoryType.PLAYER)) return; + if (!e.getInventory().getType().equals(InventoryType.CRAFTING) && !e.getInventory().getType().equals(InventoryType.PLAYER)) return; + if (!(e.getWhoClicked() instanceof Player)) return; + ArmorType newArmorType = ArmorType.matchType(shift ? e.getCurrentItem() : e.getCursor()); + if (!shift && newArmorType != null && e.getRawSlot() != newArmorType.getSlot()) { + // Used for drag and drop checking to make sure you aren't trying to place a helmet in the boots slot. + return; + } + if (shift){ + newArmorType = ArmorType.matchType(e.getCurrentItem()); + if (newArmorType != null) { + boolean equipping = e.getRawSlot() != newArmorType.getSlot(); + if (newArmorType.equals(ArmorType.HELMET) && (equipping == isAirOrNull(e.getWhoClicked().getInventory().getHelmet())) || newArmorType.equals(ArmorType.CHESTPLATE) && (equipping == isAirOrNull(e.getWhoClicked().getInventory().getChestplate())) || newArmorType.equals(ArmorType.LEGGINGS) && (equipping == isAirOrNull(e.getWhoClicked().getInventory().getLeggings())) || newArmorType.equals(ArmorType.BOOTS) && (equipping == isAirOrNull(e.getWhoClicked().getInventory().getBoots()))){ + ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent((Player) e.getWhoClicked(), EquipMethod.SHIFT_CLICK, newArmorType, equipping ? null : e.getCurrentItem(), equipping ? e.getCurrentItem() : null); + Bukkit.getServer().getPluginManager().callEvent(armorEquipEvent); + if (armorEquipEvent.isCancelled()) { + e.setCancelled(true); + } + } + } + } else { + ItemStack newArmorPiece = e.getCursor(); + ItemStack oldArmorPiece = e.getCurrentItem(); + if (numberkey) { + if (e.getClickedInventory().getType().equals(InventoryType.PLAYER)) {// Prevents shit in the 2by2 crafting + // e.getClickedInventory() == The players inventory + // e.getHotBarButton() == key people are pressing to equip or unequip the item to or from. + // e.getRawSlot() == The slot the item is going to. + // e.getSlot() == Armor slot, can't use e.getRawSlot() as that gives a hotbar slot ;-; + ItemStack hotbarItem = e.getClickedInventory().getItem(e.getHotbarButton()); + if (!isAirOrNull(hotbarItem)) {// Equipping + newArmorType = ArmorType.matchType(hotbarItem); + newArmorPiece = hotbarItem; + oldArmorPiece = e.getClickedInventory().getItem(e.getSlot()); + } else {// Unequipping + newArmorType = ArmorType.matchType(!isAirOrNull(e.getCurrentItem()) ? e.getCurrentItem() : e.getCursor()); + } + } + } else { + if (isAirOrNull(e.getCursor()) && !isAirOrNull(e.getCurrentItem())) {// unequip with no new item going into the slot. + newArmorType = ArmorType.matchType(e.getCurrentItem()); + } + // e.getCurrentItem() == Unequip + // e.getCursor() == Equip + // newArmorType = ArmorType.matchType(!isAirOrNull(e.getCurrentItem()) ? e.getCurrentItem() : e.getCursor()); + } + if (newArmorType != null && e.getRawSlot() == newArmorType.getSlot()) { + EquipMethod method = EquipMethod.PICK_DROP; + if (e.getAction().equals(InventoryAction.HOTBAR_SWAP) || numberkey) method = EquipMethod.HOTBAR_SWAP; + ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent((Player) e.getWhoClicked(), method, newArmorType, oldArmorPiece, newArmorPiece); + Bukkit.getServer().getPluginManager().callEvent(armorEquipEvent); + if (armorEquipEvent.isCancelled()) { + e.setCancelled(true); + } + } + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void playerInteractEvent(PlayerInteractEvent e){ + if (e.useItemInHand().equals(Result.DENY)) return; + // + if (e.getAction() == Action.PHYSICAL) return; + if (e.getAction() == Action.RIGHT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_BLOCK) { + Player player = e.getPlayer(); + if (e.getItem() != null) { + Material mat = e.getItem().getType(); + if (mat.name().endsWith("_HEAD") || mat.name().endsWith("_SKULL") || mat.name().endsWith("SKULL_ITEM")) { + return; + } + } + if (!e.useInteractedBlock().equals(Result.DENY)) { + if (e.getClickedBlock() != null && e.getAction() == Action.RIGHT_CLICK_BLOCK && !player.isSneaking()) { // Having both of these checks is useless, might as well do it though. + // Some blocks have actions when you right-click them which stops the client from equipping the armor in hand. + Material mat = e.getClickedBlock().getType(); + for (String s : blockedMaterials) { + String[] split = s.split("!"); + String checked = split[0]; + List excluded = new ArrayList<>(); + if (split.length > 1) { + excluded.addAll(Arrays.asList(split[1].split("&"))); + } + if (checked.startsWith("*")) { + //Check material + if (mat.name().endsWith(checked.replace("*", "").toUpperCase(Locale.ROOT)) && isIncluded(mat, excluded)) { + return; + } + } else if (checked.startsWith("?")) { + if (mat.name().contains(s.replace("?", "").toUpperCase(Locale.ROOT)) && isIncluded(mat, excluded)) { + return; + } + } else if (mat.name().equalsIgnoreCase(s) && isIncluded(mat, excluded)) return; + } + } + } + ArmorType newArmorType = ArmorType.matchType(e.getItem()); + if (newArmorType != null) { + if (newArmorType.equals(ArmorType.HELMET) && isAirOrNull(e.getPlayer().getInventory().getHelmet()) || newArmorType.equals(ArmorType.CHESTPLATE) + && isAirOrNull(e.getPlayer().getInventory().getChestplate()) || newArmorType.equals(ArmorType.LEGGINGS) && isAirOrNull(e.getPlayer().getInventory().getLeggings()) + || newArmorType.equals(ArmorType.BOOTS) && isAirOrNull(e.getPlayer().getInventory().getBoots())) { + ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(e.getPlayer(), EquipMethod.HOTBAR, ArmorType.matchType(e.getItem()), null, e.getItem()); + Bukkit.getServer().getPluginManager().callEvent(armorEquipEvent); + if (armorEquipEvent.isCancelled()) { + e.setCancelled(true); + player.updateInventory(); + } + return; + } + // Check for armor swap mechanics + if (VersionUtils.isAtLeastVersion(19, 4)) { + ArmorType type = ArmorType.matchType(e.getItem()); + if (type == null) return; + ItemStack oldArmorPiece = getArmorInSlot(e.getPlayer(), type); + if (hasCurseOfBinding(oldArmorPiece)) return; // Ignore if the old armor piece has Curse of Binding + + ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(e.getPlayer(), EquipMethod.HOTBAR, type, oldArmorPiece, e.getItem()); + Bukkit.getServer().getPluginManager().callEvent(armorEquipEvent); + if (armorEquipEvent.isCancelled()) { + e.setCancelled(true); + player.updateInventory(); + } + } + } + } + } + + private boolean hasCurseOfBinding(ItemStack item) { + if (item == null) return false; + if (item.hasItemMeta()) { + ItemMeta meta = item.getItemMeta(); + if (meta == null) return false; + return meta.hasEnchant(Enchantment.BINDING_CURSE); + } + return false; + } + + private ItemStack getArmorInSlot(Player player, ArmorType type) { + switch (type) { + case HELMET: + return player.getInventory().getHelmet(); + case CHESTPLATE: + return player.getInventory().getChestplate(); + case LEGGINGS: + return player.getInventory().getLeggings(); + case BOOTS: + return player.getInventory().getBoots(); + default: + return null; + } + } + + private boolean isIncluded(Material mat, List excluded) { + boolean isExcluded = false; + // Check exclusions + for (String exclusion : excluded) { + if (exclusion.startsWith("*")) { + if (mat.name().endsWith(exclusion.replace("*", "").toUpperCase(Locale.ROOT))) { + isExcluded = true; + break; + } + } else if (exclusion.startsWith("?")) { + if (mat.name().contains(exclusion.replace("?", "").toUpperCase(Locale.ROOT))) { + isExcluded = true; + break; + } + } + } + return !isExcluded; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void inventoryDrag(InventoryDragEvent event){ + // getType() seems to always be even. + // Old Cursor gives the item you are equipping + // Raw slot is the ArmorType slot + // Can't replace armor using this method making getCursor() useless. + ArmorType type = ArmorType.matchType(event.getOldCursor()); + if (event.getRawSlots().isEmpty()) return;// Idk if this will ever happen + if (type != null && type.getSlot() == event.getRawSlots().stream().findFirst().orElse(0)) { + ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent((Player) event.getWhoClicked(), EquipMethod.DRAG, type, null, event.getOldCursor()); + Bukkit.getServer().getPluginManager().callEvent(armorEquipEvent); + if (armorEquipEvent.isCancelled()) { + event.setResult(Result.DENY); + event.setCancelled(true); + } + } + } + + @EventHandler + @SuppressWarnings("deprecation") + public void itemBreakEvent(PlayerItemBreakEvent e){ + ArmorType type = ArmorType.matchType(e.getBrokenItem()); + if (type != null) { + Player p = e.getPlayer(); + ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(p, EquipMethod.BROKE, type, e.getBrokenItem(), null); + Bukkit.getServer().getPluginManager().callEvent(armorEquipEvent); + if (armorEquipEvent.isCancelled()) { + ItemStack i = e.getBrokenItem().clone(); + i.setAmount(1); + i.setDurability((short) (i.getDurability() - 1)); + if (type.equals(ArmorType.HELMET)) { + p.getInventory().setHelmet(i); + } else if(type.equals(ArmorType.CHESTPLATE)) { + p.getInventory().setChestplate(i); + } else if(type.equals(ArmorType.LEGGINGS)) { + p.getInventory().setLeggings(i); + } else if(type.equals(ArmorType.BOOTS)) { + p.getInventory().setBoots(i); + } + } + } + } + + @EventHandler + public void playerDeathEvent(PlayerDeathEvent e){ + Player p = e.getEntity(); + if (e.getKeepInventory()) return; + for (ItemStack i : p.getInventory().getArmorContents()) { + if (!isAirOrNull(i)) { + Bukkit.getServer().getPluginManager().callEvent(new ArmorEquipEvent(p, EquipMethod.DEATH, ArmorType.matchType(i), i, null)); + // No way to cancel a death event. + } + } + } + + @EventHandler(ignoreCancelled = true) + public void dispenser(BlockDispenseArmorEvent event) { + LivingEntity livingEntity = event.getTargetEntity(); + if (!(livingEntity instanceof Player)) { // Ensure the target is a player + return; + } + Player player = (Player) livingEntity; + ItemStack item = event.getItem(); + ArmorType type = ArmorType.matchType(item); + + if (type != null) { + ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(player, EquipMethod.DISPENSER, type, null, item); + Bukkit.getServer().getPluginManager().callEvent(armorEquipEvent); + if (armorEquipEvent.isCancelled()) { + event.setCancelled(true); + } + } + } + + /** + * A utility method to support versions that use null or air ItemStacks. + */ + public static boolean isAirOrNull(ItemStack item) { + return item == null || item.getType().equals(Material.AIR); + } +} diff --git a/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorType.java b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorType.java new file mode 100644 index 000000000..14f9c319c --- /dev/null +++ b/bukkit/src/main/java/dev/aurelium/auraskills/bukkit/util/armor/ArmorType.java @@ -0,0 +1,54 @@ +package dev.aurelium.auraskills.bukkit.util.armor; + +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; + +/** + * @author Arnah + * @since Jul 30, 2015 + */ +public enum ArmorType { + + HELMET(5), CHESTPLATE(6), LEGGINGS(7), BOOTS(8); + + private final int slot; + + ArmorType(int slot){ + this.slot = slot; + } + + /** + * Attempts to match the ArmorType for the specified ItemStack. + * + * @param itemStack The ItemStack to parse the type of. + * @return The parsed ArmorType, or null if not found. + */ + public static ArmorType matchType(final ItemStack itemStack){ + if(ArmorListener.isAirOrNull(itemStack)) return null; + String type = itemStack.getType().name(); + if(type.endsWith("_HELMET") || type.endsWith("_SKULL") || type.endsWith("_HEAD") || type.endsWith("SKULL_ITEM")) return HELMET; + else if(type.endsWith("_CHESTPLATE") || type.equals("ELYTRA")) return CHESTPLATE; + else if(type.endsWith("_LEGGINGS")) return LEGGINGS; + else if(type.endsWith("_BOOTS")) return BOOTS; + else return null; + } + + public int getSlot(){ + return slot; + } + + public EquipmentSlot getEquipmentSlot() { + switch (this) { + case HELMET: + return EquipmentSlot.HEAD; + case CHESTPLATE: + return EquipmentSlot.CHEST; + case LEGGINGS: + return EquipmentSlot.LEGS; + case BOOTS: + default: + return EquipmentSlot.FEET; + } + } + +} diff --git a/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/ItemRewardParser.java b/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/ItemRewardParser.java index e79d6950b..402873b3b 100644 --- a/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/ItemRewardParser.java +++ b/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/ItemRewardParser.java @@ -18,7 +18,7 @@ public ItemRewardParser(AuraSkillsPlugin plugin) { @Override public SkillReward parse(Map map) { String key = getString(map, "key"); - NamespacedId itemKey = NamespacedId.fromStringOrDefault(key); + NamespacedId itemKey = NamespacedId.fromDefault(key); ItemRewardBuilder builder = new ItemRewardBuilder(plugin).itemKey(itemKey); int amount = 1; diff --git a/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/StatRewardParser.java b/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/StatRewardParser.java index 9ee075ad6..0bb1ac1fc 100644 --- a/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/StatRewardParser.java +++ b/common/src/main/java/dev/aurelium/auraskills/common/reward/parser/StatRewardParser.java @@ -19,7 +19,7 @@ public SkillReward parse(Map map) { StatRewardBuilder builder = new StatRewardBuilder(plugin); String statName = getString(map, "stat"); - Stat stat = plugin.getStatRegistry().get(NamespacedId.fromStringOrDefault(statName)); + Stat stat = plugin.getStatRegistry().get(NamespacedId.fromDefault(statName)); if (stat == null) { throw new IllegalArgumentException("Unknown stat with name: " + statName); } diff --git a/common/src/main/java/dev/aurelium/auraskills/common/skill/SkillLoader.java b/common/src/main/java/dev/aurelium/auraskills/common/skill/SkillLoader.java index 65abd2929..85eedd3a5 100644 --- a/common/src/main/java/dev/aurelium/auraskills/common/skill/SkillLoader.java +++ b/common/src/main/java/dev/aurelium/auraskills/common/skill/SkillLoader.java @@ -88,7 +88,7 @@ public LoadedSkill loadSkill(Skill skill, ConfigurationNode config) throws Seria private ImmutableList loadAbilities(Skill skill, ConfigurationNode config) throws SerializationException { List abilitiesStr = config.node("abilities").getList(String.class, new ArrayList<>()); // Parse ability names to ability instances in registry - return abilitiesStr.stream().map(NamespacedId::fromStringOrDefault) + return abilitiesStr.stream().map(NamespacedId::fromDefault) .map(id -> { try { Ability ability = plugin.getAbilityRegistry().get(id); @@ -117,7 +117,7 @@ private ManaAbility loadManaAbility(Skill skill, ConfigurationNode config) { ManaAbility manaAbility = null; if (manaAbilityStr != null) { try { - manaAbility = plugin.getManaAbilityRegistry().get(NamespacedId.fromStringOrDefault(manaAbilityStr)); + manaAbility = plugin.getManaAbilityRegistry().get(NamespacedId.fromDefault(manaAbilityStr)); createLoadedManaAbility(manaAbility, skill); } catch (IllegalArgumentException e) { plugin.logger().severe("Could not find mana ability " + manaAbilityStr + " while loading " + skill.getId()); diff --git a/common/src/main/java/dev/aurelium/auraskills/common/source/SourceLoader.java b/common/src/main/java/dev/aurelium/auraskills/common/source/SourceLoader.java index 812f8926c..65861cbab 100644 --- a/common/src/main/java/dev/aurelium/auraskills/common/source/SourceLoader.java +++ b/common/src/main/java/dev/aurelium/auraskills/common/source/SourceLoader.java @@ -114,13 +114,13 @@ public Map> loadTags(Skills skill) { if (sourceString.equals("*")) { // Add all sources in skill sourceList.addAll(skill.getSources()); } else if (sourceString.startsWith("!")) { // Remove source if starts with ! - NamespacedId id = NamespacedId.fromStringOrDefault(sourceString.substring(1)); + NamespacedId id = NamespacedId.fromDefault(sourceString.substring(1)); XpSource source = plugin.getSkillManager().getSourceById(id); if (source != null) { sourceList.remove(source); } } else { // Add raw source name - XpSource source = plugin.getSkillManager().getSourceById(NamespacedId.fromStringOrDefault(sourceString)); + XpSource source = plugin.getSkillManager().getSourceById(NamespacedId.fromDefault(sourceString)); if (source != null) { sourceList.add(source); } diff --git a/common/src/main/java/dev/aurelium/auraskills/common/user/User.java b/common/src/main/java/dev/aurelium/auraskills/common/user/User.java index 382b14c60..289cc581b 100644 --- a/common/src/main/java/dev/aurelium/auraskills/common/user/User.java +++ b/common/src/main/java/dev/aurelium/auraskills/common/user/User.java @@ -362,7 +362,7 @@ public void clearInvalidItems() { // Find items that are not registered List toRemove = new ArrayList<>(); for (KeyIntPair unclaimedItem : unclaimedItems) { - if (!plugin.getItemRegistry().containsItem(NamespacedId.fromStringOrDefault(unclaimedItem.getKey()))) { + if (!plugin.getItemRegistry().containsItem(NamespacedId.fromDefault(unclaimedItem.getKey()))) { toRemove.add(unclaimedItem); } } diff --git a/common/src/test/java/dev/auramc/auraskills/NamespacedIdTest.java b/common/src/test/java/dev/auramc/auraskills/NamespacedIdTest.java index 63948bedd..1e1588330 100644 --- a/common/src/test/java/dev/auramc/auraskills/NamespacedIdTest.java +++ b/common/src/test/java/dev/auramc/auraskills/NamespacedIdTest.java @@ -9,8 +9,8 @@ public class NamespacedIdTest { public void testFromString() { assert NamespacedId.from("namespace", "key").toString().equals("namespace/key"); assert NamespacedId.fromString("namespace/key").toString().equals("namespace/key"); - assert NamespacedId.fromStringOrDefault("namespace/key").toString().equals("namespace/key"); - assert NamespacedId.fromStringOrDefault("key").toString().equals(NamespacedId.AURASKILLS + "/key"); + assert NamespacedId.fromDefault("namespace/key").toString().equals("namespace/key"); + assert NamespacedId.fromDefault("key").toString().equals(NamespacedId.AURASKILLS + "/key"); } }