Adds customization and automatic movement to crafting stations
All checks were successful
KnarCraft/BlacksmithVisuals/pipeline/head This commit looks good
All checks were successful
KnarCraft/BlacksmithVisuals/pipeline/head This commit looks good
This commit is contained in:
parent
b1dab9b2cf
commit
87fa2f7c64
4
pom.xml
4
pom.xml
@ -12,7 +12,7 @@
|
||||
<name>BlacksmithVisuals</name>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<java.version>16</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@ -84,7 +84,7 @@
|
||||
<dependency>
|
||||
<groupId>net.knarcraft</groupId>
|
||||
<artifactId>blacksmith</artifactId>
|
||||
<version>1.1.1-SNAPSHOT</version>
|
||||
<version>1.1.2-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -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 <p>The event that's starting</p>
|
||||
*/
|
||||
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 <p>The action start event to display for</p>
|
||||
*/
|
||||
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 <p>The entity to play the sound at</p>
|
||||
*/
|
||||
private void playFailSound(@NotNull Entity entity) {
|
||||
playSound(entity, Sound.ENTITY_VILLAGER_NO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the succeed action sound
|
||||
*
|
||||
* @param entity <p>The entity to play the sound at</p>
|
||||
*/
|
||||
private void playSuccessSound(@NotNull Entity entity) {
|
||||
playSound(entity, Sound.BLOCK_ANVIL_USE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a npc sound using a cancellable event
|
||||
*
|
||||
* @param entity <p>The entity that should play the sound</p>
|
||||
*/
|
||||
private void playWorkSound(@NotNull Entity entity) {
|
||||
playSound(entity, Sound.ITEM_ARMOR_EQUIP_NETHERITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a npc sound using a cancellable event
|
||||
*
|
||||
* @param entity <p>The entity that should play the sound</p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -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 <p>The name of the command</p>
|
||||
* @param executor <p>The executor to bind to the command</p>
|
||||
*/
|
||||
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 <p>The NPC data manager</p>
|
||||
*/
|
||||
@NotNull
|
||||
public NPCDataManager getNpcDataManager() {
|
||||
return this.npcDataManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration manager
|
||||
*
|
||||
* @return <p>The configuration manager</p>
|
||||
*/
|
||||
@NotNull
|
||||
public ConfigurationManager getConfigurationManager() {
|
||||
return this.configurationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this plugin
|
||||
*
|
||||
* @return <p>An instance of this plugin</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static BlacksmithVisuals getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<String> 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<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
if (npcPositionNames == null) {
|
||||
List<String> output = new ArrayList<>();
|
||||
for (NPCPosition position : NPCPosition.values()) {
|
||||
output.add(position.getPositionName());
|
||||
}
|
||||
npcPositionNames = output;
|
||||
}
|
||||
return npcPositionNames;
|
||||
} else {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <p>Whether to animate the off-hand at all</p>
|
||||
* @param animationDelay <p>The delay between each time the animation should be attempted (max swing rate)</p>
|
||||
* @param animationChance <p>The probability of the animation triggering when the animation delay has passed</p>
|
||||
*/
|
||||
public record AnimationData(boolean animateOffHand, int animationDelay, int animationChance) {
|
||||
|
||||
/**
|
||||
* Instantiates a new animation data object with sanity checks
|
||||
*
|
||||
* @param animateOffHand <p>Whether to animate the off-hand at all</p>
|
||||
* @param animationDelay <p>The delay between each time the animation should be attempted (max swing rate)</p>
|
||||
* @param animationChance <p>The probability of the animation triggering when the animation delay has passed</p>
|
||||
*/
|
||||
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 <p>The configuration to load values from</p>
|
||||
* @param rootKey <p>The key to load the data from</p>
|
||||
* @return <p>The loaded sound data</p>
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
@ -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 <p>A map storing all NPC positions</p>
|
||||
*/
|
||||
public record NPCData(@NotNull Map<NPCPosition, Location> positions) {
|
||||
|
||||
}
|
@ -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 <p>The category the sound should be played in</p>
|
||||
* @param sound <p>The sound to be played</p>
|
||||
* @param volume <p>The volume to play the sound at (> 0)</p>
|
||||
* @param pitch <p>The pitch to play the sound at</p>
|
||||
* @param offsetTicks <p>The amount of ticks to offset the sound by</p>
|
||||
* @param enabled <p>Whether this sound is enabled</p>
|
||||
*/
|
||||
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 <p>The category the sound should be played in</p>
|
||||
* @param sound <p>The sound to be played</p>
|
||||
* @param volume <p>The volume to play the sound at (> 0)</p>
|
||||
* @param pitch <p>The pitch to play the sound at</p>
|
||||
* @param offsetTicks <p>The amount of ticks to offset the sound by</p>
|
||||
* @param enabled <p>Whether this sound is enabled</p>
|
||||
*/
|
||||
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 <p>The configuration to load values from</p>
|
||||
* @param rootKey <p>The key to load the data from</p>
|
||||
* @return <p>The loaded sound data</p>
|
||||
*/
|
||||
@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 <p>The name of the sound to parse</p>
|
||||
* @return <p>The parsed sound</p>
|
||||
*/
|
||||
@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 <p>The name of the category to parse</p>
|
||||
* @return <p>The parsed category</p>
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <p>The configuration manager to get configuration values from</p>
|
||||
*/
|
||||
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 <p>The action that started</p>
|
||||
* @param soundIdentifier <p>The identifier for the sound to play</p>
|
||||
* @param animationData <p>The animation data for the animation to play</p>
|
||||
*/
|
||||
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 <p>The NPC to move</p>
|
||||
*/
|
||||
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 <p>The NPC to move</p>
|
||||
* @param npcPosition <p>The npc position to move to</p>
|
||||
* @return <p>The time the move will take</p>
|
||||
*/
|
||||
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 <p>The entity to play the sound at</p>
|
||||
* @param soundData <p>The sound data for the sound to play</p>
|
||||
* @param animationData <p>The animation data for the animation to play</p>
|
||||
* @param durationTicks <p>The duration of the work animation</p>
|
||||
*/
|
||||
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 <p>The location to start from</p>
|
||||
* @param targetLocation <p>The target location</p>
|
||||
* @return <p>The time in ticks</p>
|
||||
*/
|
||||
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 <p>The entity to animate and play the sound at</p>
|
||||
* @param soundData <p>The sound to be played</p>
|
||||
* @param animationData <p>The animation to be played</p>
|
||||
*/
|
||||
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 <p>The entity to animate</p>
|
||||
*/
|
||||
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 <p>The entity to play the sound from</p>
|
||||
* @param soundData <p>The data describing the sound to play</p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@ -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<SoundIdentifier, SoundData> soundSettings;
|
||||
private AnimationData scrapperAnimationData;
|
||||
private AnimationData blacksmithAnimationData;
|
||||
|
||||
/**
|
||||
* Instantiates a new configuration manager
|
||||
*
|
||||
* @param fileConfiguration <p>The file configuration to load values from</p>
|
||||
* @throws InvalidConfigurationException <p>If the configuration file has missing or invalid values</p>
|
||||
*/
|
||||
public ConfigurationManager(@NotNull FileConfiguration fileConfiguration) throws InvalidConfigurationException {
|
||||
load(fileConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration from disk
|
||||
*
|
||||
* @param fileConfiguration <p>The file configuration to get values from</p>
|
||||
* @throws InvalidConfigurationException <p>If the configuration file has missing or invalid values</p>
|
||||
*/
|
||||
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 <p>Scrapper animation data</p>
|
||||
*/
|
||||
@NotNull
|
||||
public AnimationData getScrapperAnimationData() {
|
||||
return this.scrapperAnimationData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the animation data for the blacksmith's working animation
|
||||
*
|
||||
* @return <p>Blacksmith animation data</p>
|
||||
*/
|
||||
@NotNull
|
||||
public AnimationData getBlacksmithAnimationData() {
|
||||
return this.blacksmithAnimationData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sound data for the given identifier
|
||||
*
|
||||
* @param identifier <p>The identifier of the sound</p>
|
||||
* @return <p>The sound's sound data</p>
|
||||
*/
|
||||
@NotNull
|
||||
public SoundData getSoundData(@NotNull SoundIdentifier identifier) {
|
||||
return soundSettings.get(identifier);
|
||||
}
|
||||
|
||||
}
|
@ -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<UUID, NPCData> npcDataMap;
|
||||
private static final File configurationFile = new File(BlacksmithVisuals.getInstance().getDataFolder(), "data.yml");
|
||||
|
||||
/**
|
||||
* Instantiates a new NPC data manager
|
||||
*
|
||||
* @param npcDataMap <p>The map containing existing NPC data</p>
|
||||
*/
|
||||
public NPCDataManager(@NotNull Map<UUID, NPCData> npcDataMap) {
|
||||
this.npcDataMap = npcDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NPC data for the NPC with the given id
|
||||
*
|
||||
* @param npcId <p>The id of the NPC to get data for</p>
|
||||
* @return <p>The NPC data, or null if not set</p>
|
||||
*/
|
||||
@Nullable
|
||||
public NPCData getData(@NotNull UUID npcId) {
|
||||
return this.npcDataMap.get(npcId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new NPC data to an NPC
|
||||
*
|
||||
* @param npcId <p>The id of the NPC to add data to</p>
|
||||
* @param npcData <p>The data to set</p>
|
||||
*/
|
||||
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 <p>If unable to write the configuration file</p>
|
||||
*/
|
||||
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<UUID, NPCData> 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 <p>The loaded data manager</p>
|
||||
* @throws IOException <p>If unable to load the configuration file</p>
|
||||
*/
|
||||
@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<UUID, NPCData> npcDataMap = new HashMap<>();
|
||||
|
||||
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configurationFile);
|
||||
for (String configurationSectionKey : configuration.getKeys(false)) {
|
||||
ConfigurationSection section = configuration.getConfigurationSection(configurationSectionKey);
|
||||
if (section == null) {
|
||||
continue;
|
||||
}
|
||||
Map<NPCPosition, Location> 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);
|
||||
}
|
||||
|
||||
}
|
@ -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 <p>The position name</p>
|
||||
*/
|
||||
@NotNull
|
||||
public String getPositionName() {
|
||||
return this.positionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NPC position the work station's material
|
||||
*
|
||||
* @param material <p>The material of the workstation</p>
|
||||
* @return <p>The corresponding NPC position</p>
|
||||
*/
|
||||
@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 <p>The input to get an NPC position from</p>
|
||||
* @return <p>The NPC position, or null if not recognized</p>
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
@ -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 <p>The sound's configuration node</p>
|
||||
*/
|
||||
SoundIdentifier(@NotNull String configNode) {
|
||||
this.configNode = configNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration node corresponding to the sound
|
||||
*
|
||||
* @return <p>The configuration node</p>
|
||||
*/
|
||||
public String getConfigNode() {
|
||||
return this.configNode;
|
||||
}
|
||||
|
||||
}
|
98
src/main/resources/config.yml
Normal file
98
src/main/resources/config.yml
Normal file
@ -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
|
@ -2,4 +2,35 @@ name: BlacksmithVisuals
|
||||
version: '${project.version}'
|
||||
main: net.knarcraft.blacksmithvisuals.BlacksmithVisuals
|
||||
api-version: 1.20
|
||||
depend: [ ProtocolLib ]
|
||||
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: /<command>
|
||||
description: Used to reload BlacksmithVisuals
|
||||
setNPCPosition:
|
||||
permission: blacksmithvisuals.setposition
|
||||
usage: /<command> <position type>
|
||||
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
|
Loading…
Reference in New Issue
Block a user