From 87fa2f7c643f168459fa71e78479e5f1b8b2f8eb Mon Sep 17 00:00:00 2001 From: EpicKnarvik97 Date: Sat, 3 Aug 2024 17:32:36 +0200 Subject: [PATCH] Adds customization and automatic movement to crafting stations --- pom.xml | 4 +- .../blacksmithvisuals/BlacksmithListener.java | 149 ---------- .../blacksmithvisuals/BlacksmithVisuals.java | 106 ++++++- .../command/ReloadCommand.java | 22 ++ .../command/SetNPCPositionCommand.java | 83 ++++++ .../container/AnimationData.java | 57 ++++ .../blacksmithvisuals/container/NPCData.java | 16 + .../container/SoundData.java | 108 +++++++ .../listener/BlacksmithListener.java | 278 ++++++++++++++++++ .../manager/ConfigurationManager.java | 78 +++++ .../manager/NPCDataManager.java | 115 ++++++++ .../property/NPCPosition.java | 82 ++++++ .../property/SoundIdentifier.java | 61 ++++ src/main/resources/config.yml | 98 ++++++ src/main/resources/plugin.yml | 33 ++- 15 files changed, 1133 insertions(+), 157 deletions(-) delete mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithListener.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/command/ReloadCommand.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/command/SetNPCPositionCommand.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/container/AnimationData.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/container/NPCData.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/container/SoundData.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/listener/BlacksmithListener.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/manager/ConfigurationManager.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/manager/NPCDataManager.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/property/NPCPosition.java create mode 100644 src/main/java/net/knarcraft/blacksmithvisuals/property/SoundIdentifier.java create mode 100644 src/main/resources/config.yml diff --git a/pom.xml b/pom.xml index 746819d..8611844 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ BlacksmithVisuals - 1.8 + 16 UTF-8 @@ -84,7 +84,7 @@ net.knarcraft blacksmith - 1.1.1-SNAPSHOT + 1.1.2-SNAPSHOT provided diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithListener.java b/src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithListener.java deleted file mode 100644 index 2652fb2..0000000 --- a/src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithListener.java +++ /dev/null @@ -1,149 +0,0 @@ -package net.knarcraft.blacksmithvisuals; - -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.ProtocolLibrary; -import com.comphenix.protocol.ProtocolManager; -import com.comphenix.protocol.events.PacketContainer; -import net.knarcraft.blacksmith.BlacksmithPlugin; -import net.knarcraft.blacksmith.event.ActionStartEvent; -import net.knarcraft.blacksmith.event.BlacksmithReforgeFailEvent; -import net.knarcraft.blacksmith.event.BlacksmithReforgeStartEvent; -import net.knarcraft.blacksmith.event.BlacksmithReforgeSucceedEvent; -import net.knarcraft.blacksmith.event.NPCSoundEvent; -import net.knarcraft.blacksmith.event.ScrapperSalvageFailEvent; -import net.knarcraft.blacksmith.event.ScrapperSalvageStartEvent; -import net.knarcraft.blacksmith.event.ScrapperSalvageSucceedEvent; -import org.bukkit.Bukkit; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; -import org.bukkit.World; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.scheduler.BukkitScheduler; -import org.jetbrains.annotations.NotNull; - -import java.util.Random; - -/** - * A listener for blacksmith-related events - */ -public class BlacksmithListener implements Listener { - - private static final Random random = new Random(); - - @EventHandler - public void onReforgeStart(@NotNull BlacksmithReforgeStartEvent event) { - onActionStart(event); - } - - @EventHandler - public void onSalvageStart(@NotNull ScrapperSalvageStartEvent event) { - onActionStart(event); - } - - @EventHandler - public void onDefaultSound(@NotNull NPCSoundEvent event) { - event.setCancelled(true); - } - - @EventHandler - public void onReforgeSuccess(@NotNull BlacksmithReforgeSucceedEvent event) { - playSuccessSound(event.getNpc().getEntity()); - } - - @EventHandler - public void onSalvageSuccess(@NotNull ScrapperSalvageSucceedEvent event) { - playSuccessSound(event.getNpc().getEntity()); - } - - @EventHandler - public void onReforgeFail(@NotNull BlacksmithReforgeFailEvent event) { - playFailSound(event.getNpc().getEntity()); - } - - @EventHandler - public void onSalvageFail(@NotNull ScrapperSalvageFailEvent event) { - playFailSound(event.getNpc().getEntity()); - } - - /** - * A method performing actions required when a blacksmith or scrapper action starts - * - * @param event

The event that's starting

- */ - private void onActionStart(@NotNull ActionStartEvent event) { - BukkitScheduler scheduler = Bukkit.getScheduler(); - int playWorkSound = scheduler.scheduleSyncRepeatingTask(BlacksmithPlugin.getInstance(), - () -> displayWorkAnimation(event), 20, 5); - - scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), () -> scheduler.cancelTask(playWorkSound), - event.getActionDurationTicks()); - } - - /** - * Displays the animation of a working NPC - * - * @param event

The action start event to display for

- */ - private void displayWorkAnimation(@NotNull ActionStartEvent event) { - if (random.nextInt(100) >= 20) { - return; - } - - this.playWorkSound(event.getNpc().getEntity()); - ProtocolManager manager = ProtocolLibrary.getProtocolManager(); - PacketContainer packet = manager.createPacket(PacketType.Play.Server.ANIMATION); - packet.getIntegers().write(0, event.getNpc().getEntity().getEntityId()); - packet.getIntegers().write(1, 3); - - for (Player player : Bukkit.getOnlinePlayers()) { - if (player.getWorld().equals(event.getNpc().getEntity().getWorld())) { - manager.sendServerPacket(player, packet); - } - } - } - - /** - * Plays the fail action sound - * - * @param entity

The entity to play the sound at

- */ - private void playFailSound(@NotNull Entity entity) { - playSound(entity, Sound.ENTITY_VILLAGER_NO); - } - - /** - * Plays the succeed action sound - * - * @param entity

The entity to play the sound at

- */ - private void playSuccessSound(@NotNull Entity entity) { - playSound(entity, Sound.BLOCK_ANVIL_USE); - } - - /** - * Plays a npc sound using a cancellable event - * - * @param entity

The entity that should play the sound

- */ - private void playWorkSound(@NotNull Entity entity) { - playSound(entity, Sound.ITEM_ARMOR_EQUIP_NETHERITE); - } - - /** - * Plays a npc sound using a cancellable event - * - * @param entity

The entity that should play the sound

- */ - private void playSound(@NotNull Entity entity, Sound sound) { - World world = entity.getLocation().getWorld(); - if (world == null) { - return; - } - - world.playSound(entity, sound, SoundCategory.AMBIENT, 0.5f, 1.0f); - } - -} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithVisuals.java b/src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithVisuals.java index 35b23cf..931494a 100644 --- a/src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithVisuals.java +++ b/src/main/java/net/knarcraft/blacksmithvisuals/BlacksmithVisuals.java @@ -1,6 +1,18 @@ package net.knarcraft.blacksmithvisuals; +import net.knarcraft.blacksmithvisuals.command.ReloadCommand; +import net.knarcraft.blacksmithvisuals.command.SetNPCPositionCommand; +import net.knarcraft.blacksmithvisuals.listener.BlacksmithListener; +import net.knarcraft.blacksmithvisuals.manager.ConfigurationManager; +import net.knarcraft.blacksmithvisuals.manager.NPCDataManager; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.PluginCommand; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.logging.Level; /** * The blacksmith visual main class @@ -8,18 +20,102 @@ import org.bukkit.plugin.java.JavaPlugin; @SuppressWarnings("unused") public final class BlacksmithVisuals extends JavaPlugin { + private static BlacksmithVisuals instance; + private ConfigurationManager configurationManager; + private NPCDataManager npcDataManager; + @Override public void onEnable() { - // Plugin startup logic - getServer().getPluginManager().registerEvents(new BlacksmithListener(), this); + BlacksmithVisuals.instance = this; - //TODO: Allow customization of sounds, volumes and pitches - // Allow setting an idle position and a working position, and move the NPC to the right position at the right time. - // Use the distance between the two locations to decide how much sooner before the success/fail sounds are played the NPC should start walking + //Copy default config to disk, and add missing configuration values + this.saveDefaultConfig(); + this.getConfig().options().copyDefaults(true); + this.reloadConfig(); + this.saveConfig(); + + try { + this.configurationManager = new ConfigurationManager(this.getConfig()); + } catch (InvalidConfigurationException exception) { + this.getLogger().log(Level.SEVERE, "Could not properly load the configuration file. " + + "Please check it for errors!"); + this.onDisable(); + return; + } + try { + this.npcDataManager = NPCDataManager.load(); + } catch (IOException e) { + this.getLogger().log(Level.SEVERE, "Could not properly load the data.yml file. " + + "Please check it for errors!"); + this.onDisable(); + return; + } + getServer().getPluginManager().registerEvents(new BlacksmithListener(this.configurationManager), this); + registerCommand("reload", new ReloadCommand()); + registerCommand("setNPCPosition", new SetNPCPositionCommand()); } @Override public void onDisable() { // Plugin shutdown logic } + + /** + * Reloads the configuration file + */ + public void reload() { + reloadConfig(); + saveConfig(); + try { + this.configurationManager.load(getConfig()); + } catch (InvalidConfigurationException exception) { + this.getLogger().log(Level.SEVERE, "Could not properly load the configuration file. " + + "Please check it for errors!"); + this.onDisable(); + } + } + + /** + * Registers a command + * + * @param commandName

The name of the command

+ * @param executor

The executor to bind to the command

+ */ + private void registerCommand(@NotNull String commandName, @NotNull CommandExecutor executor) { + PluginCommand command = this.getCommand(commandName); + if (command != null) { + command.setExecutor(executor); + } + } + + /** + * Gets the NPC data manager + * + * @return

The NPC data manager

+ */ + @NotNull + public NPCDataManager getNpcDataManager() { + return this.npcDataManager; + } + + /** + * Gets the configuration manager + * + * @return

The configuration manager

+ */ + @NotNull + public ConfigurationManager getConfigurationManager() { + return this.configurationManager; + } + + /** + * Gets an instance of this plugin + * + * @return

An instance of this plugin

+ */ + @NotNull + public static BlacksmithVisuals getInstance() { + return instance; + } + } diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/command/ReloadCommand.java b/src/main/java/net/knarcraft/blacksmithvisuals/command/ReloadCommand.java new file mode 100644 index 0000000..fb1221c --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/command/ReloadCommand.java @@ -0,0 +1,22 @@ +package net.knarcraft.blacksmithvisuals.command; + +import net.knarcraft.blacksmithvisuals.BlacksmithVisuals; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +/** + * The reload command + */ +public class ReloadCommand implements CommandExecutor { + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] strings) { + BlacksmithVisuals.getInstance().reload(); + commandSender.sendMessage("BlacksmithVisuals has been reloaded!"); + return true; + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/command/SetNPCPositionCommand.java b/src/main/java/net/knarcraft/blacksmithvisuals/command/SetNPCPositionCommand.java new file mode 100644 index 0000000..57c89c7 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/command/SetNPCPositionCommand.java @@ -0,0 +1,83 @@ +package net.knarcraft.blacksmithvisuals.command; + +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.npc.NPC; +import net.knarcraft.blacksmith.trait.BlacksmithTrait; +import net.knarcraft.blacksmith.trait.ScrapperTrait; +import net.knarcraft.blacksmithvisuals.BlacksmithVisuals; +import net.knarcraft.blacksmithvisuals.container.NPCData; +import net.knarcraft.blacksmithvisuals.manager.NPCDataManager; +import net.knarcraft.blacksmithvisuals.property.NPCPosition; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * The commands for setting an NPC's positions + */ +public class SetNPCPositionCommand implements TabExecutor { + + private static List npcPositionNames = null; + + + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] arguments) { + NPC npc = CitizensAPI.getDefaultNPCSelector().getSelected(commandSender); + if (npc == null || (!npc.hasTrait(BlacksmithTrait.class) && !npc.hasTrait(ScrapperTrait.class))) { + commandSender.sendMessage("You must select an NPC before executing this command"); + return true; + } + + if (!(commandSender instanceof Player player)) { + commandSender.sendMessage("This command must be executed by a player"); + return false; + } + + if (arguments.length < 1) { + return false; + } + + NPCPosition position = NPCPosition.fromInput(arguments[0]); + if (position == null) { + commandSender.sendMessage("Invalid position specified!"); + return false; + } + + NPCDataManager manager = BlacksmithVisuals.getInstance().getNpcDataManager(); + NPCData data = manager.getData(npc.getUniqueId()); + if (data == null) { + data = new NPCData(new HashMap<>()); + } + data.positions().put(position, player.getLocation()); + manager.putData(npc.getUniqueId(), data); + commandSender.sendMessage("Position " + position.getPositionName() + " set!"); + return true; + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, + @NotNull String[] arguments) { + if (arguments.length == 1) { + if (npcPositionNames == null) { + List output = new ArrayList<>(); + for (NPCPosition position : NPCPosition.values()) { + output.add(position.getPositionName()); + } + npcPositionNames = output; + } + return npcPositionNames; + } else { + return List.of(); + } + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/container/AnimationData.java b/src/main/java/net/knarcraft/blacksmithvisuals/container/AnimationData.java new file mode 100644 index 0000000..58cdff3 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/container/AnimationData.java @@ -0,0 +1,57 @@ +package net.knarcraft.blacksmithvisuals.container; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +/** + * An animation data container + * + * @param animateOffHand

Whether to animate the off-hand at all

+ * @param animationDelay

The delay between each time the animation should be attempted (max swing rate)

+ * @param animationChance

The probability of the animation triggering when the animation delay has passed

+ */ +public record AnimationData(boolean animateOffHand, int animationDelay, int animationChance) { + + /** + * Instantiates a new animation data object with sanity checks + * + * @param animateOffHand

Whether to animate the off-hand at all

+ * @param animationDelay

The delay between each time the animation should be attempted (max swing rate)

+ * @param animationChance

The probability of the animation triggering when the animation delay has passed

+ */ + public AnimationData { + if (animationDelay < 1) { + animationDelay = 1; + } + if (animationChance < 1) { + animationChance = 1; + } + if (animationChance > 100) { + animationChance = 100; + } + } + + /** + * Loads sound data from the given configuration key + * + * @param configuration

The configuration to load values from

+ * @param rootKey

The key to load the data from

+ * @return

The loaded sound data

+ */ + @NotNull + public static AnimationData load(@NotNull FileConfiguration configuration, + @NotNull String rootKey) throws InvalidConfigurationException { + ConfigurationSection section = configuration.getConfigurationSection(rootKey); + if (section == null) { + throw new InvalidConfigurationException("Could not find the configuration section " + rootKey); + } + boolean animateOffHand = section.getBoolean("animateOffhand", true); + int animationDelay = section.getInt("animationDelay", 5); + int animationChance = section.getInt("animationChance", 20); + + return new AnimationData(animateOffHand, animationDelay, animationChance); + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/container/NPCData.java b/src/main/java/net/knarcraft/blacksmithvisuals/container/NPCData.java new file mode 100644 index 0000000..14800f2 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/container/NPCData.java @@ -0,0 +1,16 @@ +package net.knarcraft.blacksmithvisuals.container; + +import net.knarcraft.blacksmithvisuals.property.NPCPosition; +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * Data for an NPC + * + * @param positions

A map storing all NPC positions

+ */ +public record NPCData(@NotNull Map positions) { + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/container/SoundData.java b/src/main/java/net/knarcraft/blacksmithvisuals/container/SoundData.java new file mode 100644 index 0000000..78852e5 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/container/SoundData.java @@ -0,0 +1,108 @@ +package net.knarcraft.blacksmithvisuals.container; + +import net.knarcraft.blacksmithvisuals.BlacksmithVisuals; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; +import java.util.logging.Level; + +/** + * A sound data container + * + * @param soundCategory

The category the sound should be played in

+ * @param sound

The sound to be played

+ * @param volume

The volume to play the sound at (> 0)

+ * @param pitch

The pitch to play the sound at

+ * @param offsetTicks

The amount of ticks to offset the sound by

+ * @param enabled

Whether this sound is enabled

+ */ +public record SoundData(@NotNull SoundCategory soundCategory, @NotNull Sound sound, float volume, float pitch, + int offsetTicks, boolean enabled) { + + /** + * Instantiates a new sound data object with sanity checks + * + * @param soundCategory

The category the sound should be played in

+ * @param sound

The sound to be played

+ * @param volume

The volume to play the sound at (> 0)

+ * @param pitch

The pitch to play the sound at

+ * @param offsetTicks

The amount of ticks to offset the sound by

+ * @param enabled

Whether this sound is enabled

+ */ + public SoundData { + if (volume < 0) { + volume = 1; + } + if (pitch < 0) { + pitch = 0; + } else if (pitch > 1) { + pitch = 1; + } + Objects.requireNonNull(soundCategory); + Objects.requireNonNull(sound); + } + + /** + * Loads sound data from the given configuration key + * + * @param configuration

The configuration to load values from

+ * @param rootKey

The key to load the data from

+ * @return

The loaded sound data

+ */ + @NotNull + public static SoundData load(@NotNull FileConfiguration configuration, + @NotNull String rootKey) throws InvalidConfigurationException { + ConfigurationSection section = configuration.getConfigurationSection(rootKey); + if (section == null) { + throw new InvalidConfigurationException("Could not find the configuration section " + rootKey); + } + boolean enabled = section.getBoolean("enabled", true); + SoundCategory soundCategory = parseCategory(section.getString("soundCategory", "AMBIENT")); + Sound sound = parseSound(section.getString("sound", "ENTITY_PIG_DEATH")); + float pitch = (float) section.getDouble("pitch", 1); + float volume = (float) section.getDouble("volume", 1); + int offsetTicks = section.getInt("offset", 0); + + return new SoundData(soundCategory, sound, volume, pitch, offsetTicks, enabled); + } + + /** + * Safely parses a sound + * + * @param soundName

The name of the sound to parse

+ * @return

The parsed sound

+ */ + @NotNull + private static Sound parseSound(@NotNull String soundName) { + try { + return Sound.valueOf(soundName); + } catch (IllegalArgumentException exception) { + BlacksmithVisuals.getInstance().getLogger().log(Level.WARNING, "Invalid sound in configuration: " + + soundName); + return Sound.ENTITY_PIG_DEATH; + } + } + + /** + * Safely parses a sound category + * + * @param categoryName

The name of the category to parse

+ * @return

The parsed category

+ */ + @NotNull + private static SoundCategory parseCategory(@NotNull String categoryName) { + try { + return SoundCategory.valueOf(categoryName); + } catch (IllegalArgumentException exception) { + BlacksmithVisuals.getInstance().getLogger().log(Level.WARNING, "Invalid sound category in " + + "configuration: " + categoryName); + return SoundCategory.AMBIENT; + } + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/listener/BlacksmithListener.java b/src/main/java/net/knarcraft/blacksmithvisuals/listener/BlacksmithListener.java new file mode 100644 index 0000000..e709134 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/listener/BlacksmithListener.java @@ -0,0 +1,278 @@ +package net.knarcraft.blacksmithvisuals.listener; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; +import net.citizensnpcs.api.npc.NPC; +import net.knarcraft.blacksmith.event.ActionStartEvent; +import net.knarcraft.blacksmith.event.BlacksmithReforgeFailEvent; +import net.knarcraft.blacksmith.event.BlacksmithReforgeStartEvent; +import net.knarcraft.blacksmith.event.BlacksmithReforgeSucceedEvent; +import net.knarcraft.blacksmith.event.NPCSoundEvent; +import net.knarcraft.blacksmith.event.ScrapperSalvageFailEvent; +import net.knarcraft.blacksmith.event.ScrapperSalvageStartEvent; +import net.knarcraft.blacksmith.event.ScrapperSalvageSucceedEvent; +import net.knarcraft.blacksmithvisuals.BlacksmithVisuals; +import net.knarcraft.blacksmithvisuals.container.AnimationData; +import net.knarcraft.blacksmithvisuals.container.NPCData; +import net.knarcraft.blacksmithvisuals.container.SoundData; +import net.knarcraft.blacksmithvisuals.manager.ConfigurationManager; +import net.knarcraft.blacksmithvisuals.property.NPCPosition; +import net.knarcraft.blacksmithvisuals.property.SoundIdentifier; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.scheduler.BukkitScheduler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Random; +import java.util.logging.Level; + +/** + * A listener for blacksmith-related events + */ +public class BlacksmithListener implements Listener { + + private final Random random; + private final ConfigurationManager configurationManager; + + /** + * Instantiates a new blacksmith listener + * + * @param configurationManager

The configuration manager to get configuration values from

+ */ + public BlacksmithListener(@NotNull ConfigurationManager configurationManager) { + this.random = new Random(); + this.configurationManager = configurationManager; + } + + @EventHandler + public void onDefaultSound(@NotNull NPCSoundEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onReforgeStart(@NotNull BlacksmithReforgeStartEvent event) { + runWorkingAnimation(event, SoundIdentifier.REFORGING_WORKING, this.configurationManager.getBlacksmithAnimationData()); + } + + @EventHandler + public void onSalvageStart(@NotNull ScrapperSalvageStartEvent event) { + runWorkingAnimation(event, SoundIdentifier.SALVAGING_WORKING, this.configurationManager.getScrapperAnimationData()); + } + + @EventHandler + public void onReforgeSuccess(@NotNull BlacksmithReforgeSucceedEvent event) { + playSound(event.getNpc().getEntity(), this.configurationManager.getSoundData(SoundIdentifier.REFORGING_SUCCESS)); + } + + @EventHandler + public void onSalvageSuccess(@NotNull ScrapperSalvageSucceedEvent event) { + playSound(event.getNpc().getEntity(), this.configurationManager.getSoundData(SoundIdentifier.SALVAGING_SUCCESS)); + } + + @EventHandler + public void onReforgeFail(@NotNull BlacksmithReforgeFailEvent event) { + playSound(event.getNpc().getEntity(), this.configurationManager.getSoundData(SoundIdentifier.REFORGING_FAILURE)); + } + + @EventHandler + public void onSalvageFail(@NotNull ScrapperSalvageFailEvent event) { + playSound(event.getNpc().getEntity(), this.configurationManager.getSoundData(SoundIdentifier.SALVAGING_FAILURE)); + } + + /** + * Runs the working animation for an NPC + * + * @param event

The action that started

+ * @param soundIdentifier

The identifier for the sound to play

+ * @param animationData

The animation data for the animation to play

+ */ + private void runWorkingAnimation(@NotNull ActionStartEvent event, @NotNull SoundIdentifier soundIdentifier, + @NotNull AnimationData animationData) { + BlacksmithVisuals instance = BlacksmithVisuals.getInstance(); + BukkitScheduler scheduler = Bukkit.getScheduler(); + NPC npc = event.getNpc(); + long delay = moveToWorkingPosition(npc, NPCPosition.getFromMaterial(event.getCraftingStation())); + long finishTime = event.getActionDurationTicks() - (2 * delay); + + scheduler.scheduleSyncDelayedTask(instance, () -> startWorkAnimation(npc.getEntity(), + this.configurationManager.getSoundData(soundIdentifier), animationData, finishTime - 20), delay); + scheduler.scheduleSyncDelayedTask(instance, () -> moveBack(event.getNpc()), finishTime); + } + + /** + * Moves an NPC back to its idle position + * + * @param npc

The NPC to move

+ */ + private void moveBack(@NotNull NPC npc) { + if (!npc.isSpawned()) { + return; + } + + NPCData npcData = BlacksmithVisuals.getInstance().getNpcDataManager().getData(npc.getUniqueId()); + if (npcData == null) { + return; + } + + Location targetLocation = npcData.positions().get(NPCPosition.IDLE); + if (!npc.getNavigator().canNavigateTo(targetLocation)) { + BlacksmithVisuals.getInstance().getLogger().log(Level.WARNING, "Idle position for NPC " + + npc.getName() + " is unreachable!"); + return; + } + npc.getNavigator().setTarget(targetLocation); + + Bukkit.getScheduler().scheduleSyncDelayedTask(BlacksmithVisuals.getInstance(), () -> + npc.getEntity().teleport(targetLocation), getWalkTime(npc.getEntity().getLocation(), targetLocation)); + } + + /** + * Moves a npc to its working position + * + * @param npc

The NPC to move

+ * @param npcPosition

The npc position to move to

+ * @return

The time the move will take

+ */ + private long moveToWorkingPosition(@NotNull NPC npc, @Nullable NPCPosition npcPosition) { + if (!npc.isSpawned() || npcPosition == null) { + return 0; + } + + NPCData npcData = BlacksmithVisuals.getInstance().getNpcDataManager().getData(npc.getUniqueId()); + if (npcData == null) { + return 0; + } + + Location targetLocation = npcData.positions().get(npcPosition); + if (targetLocation == null && npcPosition != NPCPosition.WORKING_REPAIRABLE) { + targetLocation = npcData.positions().get(NPCPosition.WORKING_REPAIRABLE); + } + if (targetLocation == null) { + return 0; + } + + if (!npc.getNavigator().canNavigateTo(targetLocation)) { + BlacksmithVisuals.getInstance().getLogger().log(Level.WARNING, "Working position for NPC " + + npc.getName() + " is unreachable!"); + return 0; + } + + // Move NPC using Citizens path-finding + npc.getNavigator().setTarget(targetLocation); + + // Teleport the NPC tp get it in the exact final location + long walkTime = getWalkTime(npc.getEntity().getLocation(), targetLocation); + Location finalTargetLocation = targetLocation; + Bukkit.getScheduler().scheduleSyncDelayedTask(BlacksmithVisuals.getInstance(), () -> + npc.getEntity().teleport(finalTargetLocation), walkTime); + return walkTime; + } + + /** + * Starts the working animation and sound + * + * @param entity

The entity to play the sound at

+ * @param soundData

The sound data for the sound to play

+ * @param animationData

The animation data for the animation to play

+ * @param durationTicks

The duration of the work animation

+ */ + private void startWorkAnimation(@NotNull Entity entity, @NotNull SoundData soundData, + @NotNull AnimationData animationData, long durationTicks) { + BlacksmithVisuals instance = BlacksmithVisuals.getInstance(); + BukkitScheduler scheduler = Bukkit.getScheduler(); + + int playWorkSound = scheduler.scheduleSyncRepeatingTask(instance, + () -> animateNPC(entity, soundData, animationData), 20, animationData.animationDelay()); + scheduler.scheduleSyncDelayedTask(instance, () -> scheduler.cancelTask(playWorkSound), durationTicks); + } + + /** + * Gets the time it would take to go from one location to another in ticks + * + * @param startLocation

The location to start from

+ * @param targetLocation

The target location

+ * @return

The time in ticks

+ */ + private long getWalkTime(@NotNull Location startLocation, @NotNull Location targetLocation) { + double distance = startLocation.distance(targetLocation); + double WALK_SPEED = 4.317; + return Math.round((distance / WALK_SPEED) * 20); + } + + /** + * Animates a npc's hand, and plays a sound + * + * @param entity

The entity to animate and play the sound at

+ * @param soundData

The sound to be played

+ * @param animationData

The animation to be played

+ */ + private void animateNPC(@NotNull Entity entity, @NotNull SoundData soundData, @NotNull AnimationData animationData) { + if (random.nextInt(100) >= animationData.animationChance()) { + return; + } + + playSound(entity, soundData); + + // Don't play disabled animations + if (!animationData.animateOffHand()) { + return; + } + + if (soundData.offsetTicks() < 0) { + Bukkit.getScheduler().scheduleSyncDelayedTask(BlacksmithVisuals.getInstance(), + () -> animateOffhand(entity), -soundData.offsetTicks()); + } else { + animateOffhand(entity); + } + } + + /** + * Animates an NPC's offhand + * + * @param entity

The entity to animate

+ */ + private void animateOffhand(@NotNull Entity entity) { + ProtocolManager manager = ProtocolLibrary.getProtocolManager(); + PacketContainer packet = manager.createPacket(PacketType.Play.Server.ANIMATION); + packet.getIntegers().write(0, entity.getEntityId()); + packet.getIntegers().write(1, 3); + + for (Player player : Bukkit.getOnlinePlayers()) { + if (player.getWorld().equals(entity.getWorld())) { + manager.sendServerPacket(player, packet); + } + } + } + + /** + * Plays a sound according to the given sound data + * + * @param entity

The entity to play the sound from

+ * @param soundData

The data describing the sound to play

+ */ + private void playSound(@NotNull Entity entity, @NotNull SoundData soundData) { + // Don't play disabled sounds + if (!soundData.enabled()) { + return; + } + + World world = entity.getLocation().getWorld(); + if (world == null) { + return; + } + + int delay = Math.max(soundData.offsetTicks(), 0); + Bukkit.getScheduler().scheduleSyncDelayedTask(BlacksmithVisuals.getInstance(), () -> + world.playSound(entity, soundData.sound(), soundData.soundCategory(), + soundData.volume(), soundData.pitch()), delay); + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/manager/ConfigurationManager.java b/src/main/java/net/knarcraft/blacksmithvisuals/manager/ConfigurationManager.java new file mode 100644 index 0000000..31858a0 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/manager/ConfigurationManager.java @@ -0,0 +1,78 @@ +package net.knarcraft.blacksmithvisuals.manager; + +import net.knarcraft.blacksmithvisuals.container.AnimationData; +import net.knarcraft.blacksmithvisuals.container.SoundData; +import net.knarcraft.blacksmithvisuals.property.SoundIdentifier; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * A manager keeping track of configuration options + */ +public class ConfigurationManager { + + private Map soundSettings; + private AnimationData scrapperAnimationData; + private AnimationData blacksmithAnimationData; + + /** + * Instantiates a new configuration manager + * + * @param fileConfiguration

The file configuration to load values from

+ * @throws InvalidConfigurationException

If the configuration file has missing or invalid values

+ */ + public ConfigurationManager(@NotNull FileConfiguration fileConfiguration) throws InvalidConfigurationException { + load(fileConfiguration); + } + + /** + * Loads the configuration from disk + * + * @param fileConfiguration

The file configuration to get values from

+ * @throws InvalidConfigurationException

If the configuration file has missing or invalid values

+ */ + public void load(@NotNull FileConfiguration fileConfiguration) throws InvalidConfigurationException { + soundSettings = new HashMap<>(); + for (SoundIdentifier identifier : SoundIdentifier.values()) { + soundSettings.put(identifier, SoundData.load(fileConfiguration, identifier.getConfigNode())); + } + scrapperAnimationData = AnimationData.load(fileConfiguration, "scrapper.animation"); + blacksmithAnimationData = AnimationData.load(fileConfiguration, "blacksmith.animation"); + } + + /** + * Gets the animation data for the scrapper's working animation + * + * @return

Scrapper animation data

+ */ + @NotNull + public AnimationData getScrapperAnimationData() { + return this.scrapperAnimationData; + } + + /** + * Gets the animation data for the blacksmith's working animation + * + * @return

Blacksmith animation data

+ */ + @NotNull + public AnimationData getBlacksmithAnimationData() { + return this.blacksmithAnimationData; + } + + /** + * Gets the sound data for the given identifier + * + * @param identifier

The identifier of the sound

+ * @return

The sound's sound data

+ */ + @NotNull + public SoundData getSoundData(@NotNull SoundIdentifier identifier) { + return soundSettings.get(identifier); + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/manager/NPCDataManager.java b/src/main/java/net/knarcraft/blacksmithvisuals/manager/NPCDataManager.java new file mode 100644 index 0000000..e45bb3a --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/manager/NPCDataManager.java @@ -0,0 +1,115 @@ +package net.knarcraft.blacksmithvisuals.manager; + +import net.knarcraft.blacksmithvisuals.BlacksmithVisuals; +import net.knarcraft.blacksmithvisuals.container.NPCData; +import net.knarcraft.blacksmithvisuals.property.NPCPosition; +import org.bukkit.Location; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Level; + +/** + * A manager for NPC data + */ +public class NPCDataManager { + + private final Map npcDataMap; + private static final File configurationFile = new File(BlacksmithVisuals.getInstance().getDataFolder(), "data.yml"); + + /** + * Instantiates a new NPC data manager + * + * @param npcDataMap

The map containing existing NPC data

+ */ + public NPCDataManager(@NotNull Map npcDataMap) { + this.npcDataMap = npcDataMap; + } + + /** + * Gets the NPC data for the NPC with the given id + * + * @param npcId

The id of the NPC to get data for

+ * @return

The NPC data, or null if not set

+ */ + @Nullable + public NPCData getData(@NotNull UUID npcId) { + return this.npcDataMap.get(npcId); + } + + /** + * Adds new NPC data to an NPC + * + * @param npcId

The id of the NPC to add data to

+ * @param npcData

The data to set

+ */ + public void putData(@NotNull UUID npcId, @NotNull NPCData npcData) { + this.npcDataMap.put(npcId, npcData); + try { + this.save(); + } catch (IOException exception) { + BlacksmithVisuals.getInstance().getLogger().log(Level.SEVERE, "Unable to save NPC data. Error was: " + + exception); + } + } + + /** + * Saves this data manager's data to disk + * + * @throws IOException

If unable to write the configuration file

+ */ + public void save() throws IOException { + if (!configurationFile.exists() && !configurationFile.createNewFile()) { + throw new FileNotFoundException("data.yml could not be found or created. Please create it manually."); + } + FileConfiguration configuration = new YamlConfiguration(); + for (Map.Entry entry : npcDataMap.entrySet()) { + ConfigurationSection npcSection = configuration.createSection(entry.getKey().toString()); + npcSection.set("workingPositionNetherite", entry.getValue().positions().get(NPCPosition.WORKING_NETHERITE)); + npcSection.set("workingPositionCrafting", entry.getValue().positions().get(NPCPosition.WORKING_CRAFTING)); + npcSection.set("workingPositionRepairable", entry.getValue().positions().get(NPCPosition.WORKING_REPAIRABLE)); + npcSection.set("idlePosition", entry.getValue().positions().get(NPCPosition.IDLE)); + } + configuration.save(configurationFile); + } + + /** + * Loads a data manager from disk + * + * @return

The loaded data manager

+ * @throws IOException

If unable to load the configuration file

+ */ + @NotNull + public static NPCDataManager load() throws IOException { + if (!configurationFile.exists() && !configurationFile.createNewFile()) { + throw new FileNotFoundException("data.yml could not be found or created. Please create it manually."); + } + Map npcDataMap = new HashMap<>(); + + FileConfiguration configuration = YamlConfiguration.loadConfiguration(configurationFile); + for (String configurationSectionKey : configuration.getKeys(false)) { + ConfigurationSection section = configuration.getConfigurationSection(configurationSectionKey); + if (section == null) { + continue; + } + Map locationMap = new HashMap<>(); + + locationMap.put(NPCPosition.WORKING_NETHERITE, section.getLocation("workingPositionNetherite")); + locationMap.put(NPCPosition.WORKING_CRAFTING, section.getLocation("workingPositionCrafting")); + locationMap.put(NPCPosition.WORKING_REPAIRABLE, section.getLocation("workingPositionRepairable")); + locationMap.put(NPCPosition.IDLE, section.getLocation("idlePosition")); + npcDataMap.put(UUID.fromString(configurationSectionKey), new NPCData(locationMap)); + } + return new NPCDataManager(npcDataMap); + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/property/NPCPosition.java b/src/main/java/net/knarcraft/blacksmithvisuals/property/NPCPosition.java new file mode 100644 index 0000000..0ccfaab --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/property/NPCPosition.java @@ -0,0 +1,82 @@ +package net.knarcraft.blacksmithvisuals.property; + +import org.bukkit.Material; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A definable NPC position + */ +public enum NPCPosition { + + /** + * The position the NPC should be in while idle + */ + IDLE("idle"), + + /** + * The position the NPC should be in while undoing netherite or armor trims + */ + WORKING_NETHERITE("netherite-workstation"), + + /** + * The position the NPC should be in while reforging armor, weapons or tools + */ + WORKING_REPAIRABLE("repairable-workstation"), + + /** + * The position the NPC should be in while un-crafting items + */ + WORKING_CRAFTING("crafting-workstation"), + ; + + private final String positionName; + + NPCPosition(@NotNull String positionName) { + this.positionName = positionName; + } + + /** + * Gets the user-friendly name for this position + * + * @return

The position name

+ */ + @NotNull + public String getPositionName() { + return this.positionName; + } + + /** + * Gets the NPC position the work station's material + * + * @param material

The material of the workstation

+ * @return

The corresponding NPC position

+ */ + @Nullable + public static NPCPosition getFromMaterial(@NotNull Material material) { + return switch (material) { + case CRAFTING_TABLE -> NPCPosition.WORKING_CRAFTING; + case ANVIL -> NPCPosition.WORKING_REPAIRABLE; + case SMITHING_TABLE -> NPCPosition.WORKING_NETHERITE; + default -> null; + }; + } + + /** + * Gets an NPC position from the given user input + * + * @param input

The input to get an NPC position from

+ * @return

The NPC position, or null if not recognized

+ */ + @Nullable + public static NPCPosition fromInput(@NotNull String input) { + String cleaned = input.toLowerCase().replace("_", "-"); + for (NPCPosition position : NPCPosition.values()) { + if (position.getPositionName().equalsIgnoreCase(cleaned)) { + return position; + } + } + return null; + } + +} diff --git a/src/main/java/net/knarcraft/blacksmithvisuals/property/SoundIdentifier.java b/src/main/java/net/knarcraft/blacksmithvisuals/property/SoundIdentifier.java new file mode 100644 index 0000000..a656f01 --- /dev/null +++ b/src/main/java/net/knarcraft/blacksmithvisuals/property/SoundIdentifier.java @@ -0,0 +1,61 @@ +package net.knarcraft.blacksmithvisuals.property; + +import org.jetbrains.annotations.NotNull; + +/** + * An identifier for the available blacksmith/scrapper sounds + */ +public enum SoundIdentifier { + + /** + * The sound played while a blacksmith is working + */ + REFORGING_WORKING("blacksmith.sounds.reforgeWorking"), + + /** + * The sound played when a blacksmith finishes successfully + */ + REFORGING_SUCCESS("blacksmith.sounds.reforgeSuccess"), + + /** + * The sound played when a blacksmith finishes with a failure + */ + REFORGING_FAILURE("blacksmith.sounds.reforgeFailure"), + + /** + * The sound played while a scrapper is working + */ + SALVAGING_WORKING("scrapper.sounds.salvageWorking"), + + /** + * The sound played when a scrapper finishes successfully + */ + SALVAGING_SUCCESS("scrapper.sounds.salvageSuccess"), + + /** + * The sound played when a scrapper finishes with a failure + */ + SALVAGING_FAILURE("scrapper.sounds.salvageFailure"), + ; + + private final String configNode; + + /** + * Instantiates a new sound identifier + * + * @param configNode

The sound's configuration node

+ */ + SoundIdentifier(@NotNull String configNode) { + this.configNode = configNode; + } + + /** + * Gets the configuration node corresponding to the sound + * + * @return

The configuration node

+ */ + public String getConfigNode() { + return this.configNode; + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..1dd17ca --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,98 @@ +blacksmith: + animation: + # Whether to simulate hitting an anvil or similar by animating the blacksmith's off-hand + animateOffhand: true + # The delay between each potential arm move in ticks + animationDelay: 5 + # The probability (percentage) of an arm swing happening once the animation delay is reached + animationChance: 20 + # Sounds that play on specific actions or events + sounds: + # The sound played when reforging finishes, if successful + reforgeSuccess: + # If disabled, this sound will never play + enabled: true + # The sound to play (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html) + sound: BLOCK_ANVIL_USE + # The sound category to play the sound in (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/SoundCategory.html) + soundCategory: AMBIENT + # The pitch to play at, between 0 and 2. 1 is default. + pitch: 1 + # The volume to play at. Between 0 and infinity. 1 is max volume, but higher value increases the range. + volume: 1 + # The sound played when reforging finishes, if not successful. + reforgeFailure: + # If disabled, this sound will never play. + enabled: true + # The sound to play (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html). + sound: ENTITY_VILLAGER_NO + # The sound category to play the sound in (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/SoundCategory.html) + soundCategory: AMBIENT + # The pitch to play at, between 0 and 2. 1 is default. + pitch: 1 + # The volume to play at. Between 0 and infinity. 1 is max volume, but higher value increases the range. + volume: 1 + # The sound played when the NPC's arm moves. + reforgeWorking: + # If disabled, this sound will never play. + enabled: true + # The sound to play (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html). + sound: ITEM_ARMOR_EQUIP_NETHERITE + # The sound category to play the sound in (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/SoundCategory.html) + soundCategory: AMBIENT + # The pitch to play at, between 0 and 2. 1 is default. + pitch: 1 + # The volume to play at. Between 0 and infinity. 1 is max volume, but higher value increases the range. + volume: 1 + # The offset (delay), in ticks (negative or positive), before the sound should play. + # This can be used to better synchronize the sound to the arm movement. + offset: 0 +scrapper: + animation: + # Whether to simulate hitting an anvil or similar by animating the scrapper's off-hand + animateOffhand: true + # The delay between each potential arm move in ticks + animationDelay: 5 + # The probability (percentage) of an arm swing happening once the animation delay is reached + animationChance: 20 + # Sounds that play on specific actions or events. + sounds: + # The sound played when salvaging finishes, if successful. + salvageSuccess: + # If disabled, this sound will never play. + enabled: true + # The sound to play (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html). + sound: BLOCK_ANVIL_USE + # The sound category to play the sound in (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/SoundCategory.html) + soundCategory: AMBIENT + # The pitch to play at, between 0 and 2. 1 is default. + pitch: 1 + # The volume to play at. Between 0 and infinity. 1 is max volume, but higher value increases the range. + volume: 1 + # The sound played when salvaging finishes, if not successful. + salvageFailure: + # If disabled, this sound will never play. + enabled: true + # The sound to play (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html). + sound: ENTITY_VILLAGER_NO + # The sound category to play the sound in (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/SoundCategory.html) + soundCategory: AMBIENT + # The pitch to play at, between 0 and 2. 1 is default. + pitch: 1 + # The volume to play at. Between 0 and infinity. 1 is max volume, but higher value increases the range. + volume: 1 + # The sound played when the NPC's arm moves. + salvageWorking: + # If disabled, this sound will never play. + enabled: true + # The sound to play (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html). + sound: ITEM_ARMOR_EQUIP_NETHERITE + # The sound category to play the sound in (https://hub.spigotmc.org/javadocs/spigot/org/bukkit/SoundCategory.html) + soundCategory: AMBIENT + # The pitch to play at, between 0 and 2. 1 is default. + pitch: 1 + # The volume to play at. Between 0 and infinity. 1 is max volume, but higher value increases the range. + volume: 1 + # The offset (delay), in ticks (negative or positive), before the sound should play. + # This can be used to better synchronize the sound to the arm movement. + offset: 0 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 4708a2d..fb04e8a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,4 +2,35 @@ name: BlacksmithVisuals version: '${project.version}' main: net.knarcraft.blacksmithvisuals.BlacksmithVisuals api-version: 1.20 -depend: [ ProtocolLib ] \ No newline at end of file +depend: [ ProtocolLib, Blacksmith, Citizens ] +prefix: "Blacksmith Visuals" +description: "And add-on plugin to the blacksmith plugin that adds visual animation and configurable sounds." +website: "https://git.knarcraft.net/KnarCraft/BlacksmithVisuals" + +commands: + reload: + permission: blacksmithvisuals.reload + usage: / + description: Used to reload BlacksmithVisuals + setNPCPosition: + permission: blacksmithvisuals.setposition + usage: / + description: | + Used to set a blacksmith or scrapper NPC's positions + /setNPCPosition idle + /setNPCPosition netherite-workstation + /setNPCPosition reforging-workstation + /setNPCPosition crafting-workstation +permissions: + blacksmithvisuals.*: + description: Gives all permissions + default: op + children: + - blacksmithvisuals.reload + - blacksmithvisuals.setposition + blacksmithvisuals.reload: + description: Allows reloading the plugin + default: false + blacksmithvisuals.setposition: + description: Allows use of the /setIdlePosition and /setWorkingPosition commands + default: false \ No newline at end of file