diff --git a/README.MD b/README.MD index fcc230d..66c36b0 100644 --- a/README.MD +++ b/README.MD @@ -52,6 +52,12 @@ Reduces damage from hitting a wall while flying with elytra. **Description**: When totem of undying is activated, player is teleported to their spawn point. If spawn point is not set, player is teleported to world spawn. +### Cloaking +**Translation key**: `enchantio.enchantment.cloaking` + +**Description**: +Gives invisibility effect when player is sneaking and not moving. + ## Curses ### Curse of Panic diff --git a/src/main/java/me/youhavetrouble/enchantio/Enchantio.java b/src/main/java/me/youhavetrouble/enchantio/Enchantio.java index baced06..6c84449 100644 --- a/src/main/java/me/youhavetrouble/enchantio/Enchantio.java +++ b/src/main/java/me/youhavetrouble/enchantio/Enchantio.java @@ -40,6 +40,9 @@ public final class Enchantio extends JavaPlugin { if (EnchantioConfig.ENCHANTS.containsKey(HomecomingEnchant.KEY)) { getServer().getPluginManager().registerEvents(new HomecomingListener(), this); } + if (EnchantioConfig.ENCHANTS.containsKey(CloakingEnchant.KEY)) { + getServer().getPluginManager().registerEvents(new CloakingListener(), this); + } if (EnchantioConfig.ENCHANTS.containsKey(PanicEnchant.KEY)) { getServer().getPluginManager().registerEvents(new PanicListener(), this); diff --git a/src/main/java/me/youhavetrouble/enchantio/EnchantioConfig.java b/src/main/java/me/youhavetrouble/enchantio/EnchantioConfig.java index 15a7225..77bab7d 100644 --- a/src/main/java/me/youhavetrouble/enchantio/EnchantioConfig.java +++ b/src/main/java/me/youhavetrouble/enchantio/EnchantioConfig.java @@ -66,6 +66,9 @@ public class EnchantioConfig { ConfigurationSection homecomingSection = getConfigSection(enchantsSection, "homecoming"); HomecomingEnchant.create(homecomingSection); + ConfigurationSection cloakingSection = getConfigSection(enchantsSection, "cloaking"); + CloakingEnchant.create(cloakingSection); + ConfigurationSection cursesSection = getConfigSection(configuration, "curses"); ConfigurationSection panicSection = getConfigSection(cursesSection, "panic"); diff --git a/src/main/java/me/youhavetrouble/enchantio/enchants/CloakingEnchant.java b/src/main/java/me/youhavetrouble/enchantio/enchants/CloakingEnchant.java new file mode 100644 index 0000000..3f35f78 --- /dev/null +++ b/src/main/java/me/youhavetrouble/enchantio/enchants/CloakingEnchant.java @@ -0,0 +1,148 @@ +package me.youhavetrouble.enchantio.enchants; + +import io.papermc.paper.registry.data.EnchantmentRegistryEntry; +import io.papermc.paper.registry.keys.tags.EnchantmentTagKeys; +import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.tag.TagEntry; +import me.youhavetrouble.enchantio.EnchantioConfig; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.EquipmentSlotGroup; +import org.bukkit.inventory.ItemType; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static me.youhavetrouble.enchantio.EnchantioConfig.ENCHANTS; + +@SuppressWarnings("UnstableApiUsage") +public class CloakingEnchant implements EnchantioEnchant { + + public static final Key KEY = Key.key("enchantio:cloaking"); + + private final int anvilCost, weight, ticksToActivate; + private final EnchantmentRegistryEntry.EnchantmentCost minimumCost; + private final EnchantmentRegistryEntry.EnchantmentCost maximumCost; + private final Set> supportedItemTags; + private final Set> enchantTagKeys = new HashSet<>(); + private final Set activeSlots = new HashSet<>(); + + public CloakingEnchant( + int anvilCost, + int weight, + EnchantmentRegistryEntry.EnchantmentCost minimumCost, + EnchantmentRegistryEntry.EnchantmentCost maximumCost, + boolean canGetFromEnchantingTable, + Set> supportedItemTags, + Set activeSlots, + int ticksToActivate + ) { + this.anvilCost = anvilCost; + this.weight = weight; + this.minimumCost = minimumCost; + this.maximumCost = maximumCost; + this.supportedItemTags = supportedItemTags; + this.ticksToActivate = ticksToActivate; + this.activeSlots.addAll(activeSlots); + if (canGetFromEnchantingTable) { + enchantTagKeys.add(EnchantmentTagKeys.IN_ENCHANTING_TABLE); + } + } + + @Override + public @NotNull Key getKey() { + return KEY; + } + + @Override + public @NotNull Component getDescription() { + return Component.translatable("enchantio.enchant.cloaking", "Cloaking"); + } + + @Override + public int getAnvilCost() { + return anvilCost; + } + + @Override + public int getMaxLevel() { + return 1; + } + + @Override + public int getWeight() { + return weight; + } + + @Override + public EnchantmentRegistryEntry.@NotNull EnchantmentCost getMinimumCost() { + return minimumCost; + } + + @Override + public EnchantmentRegistryEntry.@NotNull EnchantmentCost getMaximumCost() { + return maximumCost; + } + + @Override + public @NotNull Iterable getActiveSlots() { + return activeSlots; + } + + @Override + public @NotNull Set> getSupportedItems() { + return supportedItemTags; + } + + @Override + public @NotNull Set> getEnchantTagKeys() { + return Collections.unmodifiableSet(enchantTagKeys); + } + + public int getTicksToActivate() { + return ticksToActivate; + } + + public static CloakingEnchant create(ConfigurationSection configurationSection) { + CloakingEnchant cloakingEnchant = new CloakingEnchant( + EnchantioConfig.getInt(configurationSection, "anvilCost", 1), + EnchantioConfig.getInt(configurationSection, "weight", 10), + EnchantmentRegistryEntry.EnchantmentCost.of( + EnchantioConfig.getInt(configurationSection, "minimumCost.base", 25), + EnchantioConfig.getInt(configurationSection, "minimumCost.additionalPerLevel", 1) + ), + EnchantmentRegistryEntry.EnchantmentCost.of( + EnchantioConfig.getInt(configurationSection, "maximumCost.base", 65), + EnchantioConfig.getInt(configurationSection, "maximumCost.additionalPerLevel", 1) + ), + EnchantioConfig.getBoolean(configurationSection, "canGetFromEnchantingTable", true), + EnchantioConfig.getTagsFromList(EnchantioConfig.getStringList( + configurationSection, + "supportedItemTags", + List.of( + "#minecraft:leg_armor" + ) + )), + EnchantioConfig.getEquipmentSlotGroups(EnchantioConfig.getStringList( + configurationSection, + "activeSlots", + List.of( + "LEGS" + ) + )), + EnchantioConfig.getInt(configurationSection, "ticksToActivate", 30) + ); + + if (EnchantioConfig.getBoolean(configurationSection, "enabled", true)) { + ENCHANTS.put(CloakingEnchant.KEY, cloakingEnchant); + } + + return cloakingEnchant; + } + +} diff --git a/src/main/java/me/youhavetrouble/enchantio/listeners/CloakingListener.java b/src/main/java/me/youhavetrouble/enchantio/listeners/CloakingListener.java new file mode 100644 index 0000000..6eadeea --- /dev/null +++ b/src/main/java/me/youhavetrouble/enchantio/listeners/CloakingListener.java @@ -0,0 +1,74 @@ +package me.youhavetrouble.enchantio.listeners; + +import com.destroystokyo.paper.event.player.PlayerJumpEvent; +import me.youhavetrouble.enchantio.Enchantio; +import me.youhavetrouble.enchantio.EnchantioConfig; +import me.youhavetrouble.enchantio.enchants.CloakingEnchant; +import org.bukkit.Bukkit; +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.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashMap; +import java.util.UUID; + +public class CloakingListener implements Listener { + + private final HashMap ticksSinceLastMovement = new HashMap<>(); + + private final CloakingEnchant cloakingEnchant = (CloakingEnchant) EnchantioConfig.ENCHANTS.get(CloakingEnchant.KEY); + private final PotionEffect cloakingEffect = new PotionEffect(PotionEffectType.INVISIBILITY, 3, 0, false, false, false); + + public CloakingListener() { + Enchantio enchantio = Enchantio.getPlugin(Enchantio.class); + Bukkit.getGlobalRegionScheduler().runAtFixedRate(enchantio, (task) -> { + for (Player player : Bukkit.getOnlinePlayers()) { + if (!player.isSneaking()) { + ticksSinceLastMovement.put(player.getUniqueId(), 0L); + continue; + } + ticksSinceLastMovement.computeIfPresent(player.getUniqueId(), (uuid, ticks) -> ticks + 1); + if (ticksSinceLastMovement.getOrDefault(player.getUniqueId(), 0L) < cloakingEnchant.getTicksToActivate()) continue; + player.getScheduler().execute(enchantio, () -> player.addPotionEffect(cloakingEffect), () -> {}, 1); + } + }, 1, 1); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) { + ticksSinceLastMovement.put(event.getPlayer().getUniqueId(), 0L); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) { + ticksSinceLastMovement.remove(event.getPlayer().getUniqueId()); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerMove(PlayerMoveEvent event) { + if (!event.hasChangedPosition()) return; + ticksSinceLastMovement.put(event.getPlayer().getUniqueId(), 0L); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerJump(PlayerJumpEvent event) { + ticksSinceLastMovement.put(event.getPlayer().getUniqueId(), 0L); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerInteract(PlayerInteractEvent event) { + ticksSinceLastMovement.put(event.getPlayer().getUniqueId(), 0L); + } + + + + + +}