Changes package name to net.knarcraft

This commit is contained in:
2022-07-19 02:41:01 +02:00
parent 555d405bcc
commit 3870ead92f
8 changed files with 16 additions and 16 deletions

View File

@ -0,0 +1,315 @@
package net.knarcraft.blacksmith;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.config.Setting;
import net.knarcraft.blacksmith.config.Settings;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import regalowl.hyperconomy.HyperAPI;
import regalowl.hyperconomy.HyperConomy;
import regalowl.hyperconomy.bukkit.BukkitConnector;
import regalowl.hyperconomy.inventory.HItemStack;
import regalowl.hyperconomy.tradeobject.TradeObject;
import java.util.ArrayList;
import java.util.Objects;
import java.util.logging.Level;
import static net.knarcraft.blacksmith.util.Sanitizer.sanitizeItemName;
/**
* Blacksmith's main class
*/
public class BlacksmithPlugin extends JavaPlugin {
private static BlacksmithPlugin instance;
private Settings config;
private Economy economy;
private HyperAPI hyperAPI;
private BukkitConnector bukkitConnector;
private boolean useHyperAPI = false;
/**
* Gets an instance of the Blacksmith plugin
*
* @return <p>An instance of the blacksmith plugin</p>
*/
public static BlacksmithPlugin getInstance() {
return instance;
}
/**
* Gets settings for the blacksmith plugin
*
* @return <p>Settings for the blacksmith plugin</p>
*/
public Settings getSettings() {
return config;
}
@Override
public void onDisable() {
// config.save();
getLogger().log(Level.INFO, " v" + getDescription().getVersion() + " disabled.");
}
@Override
public void onEnable() {
instance = this;
//Load settings
config = new Settings(this);
config.load();
//Load HyperConomy if available
setupHyperConomy();
getLogger().log(Level.INFO, "Setting Up Vault now....");
boolean canLoad = setupVault();
if (!canLoad) {
getLogger().log(Level.SEVERE, "Vault Integration Failed....");
getServer().getPluginManager().disablePlugin(this);
return;
}
//Register the blacksmith trait with Citizens
CitizensAPI.getTraitFactory().registerTrait(
net.citizensnpcs.api.trait.TraitInfo.create(BlacksmithTrait.class).withName("blacksmith"));
getLogger().log(Level.INFO, " v" + getDescription().getVersion() + " enabled.");
}
/**
* Sets up HyperConomy
*
* <p>Setup HyperConomy (Soft-Depend only, so this is completely optional!). HyperConomy uses your favorite
* Vault-compatible economy system and calculates prices for items based on supply and demand on the fly.
* This is only used to get the cost of a repair.</p>
*/
private void setupHyperConomy() {
if (Bukkit.getPluginManager().getPlugin("HyperConomy") != null) {
getServer().getLogger().log(Level.INFO, "Found HyperConomy! Using that for calculating prices, " +
"base-prices and price-per-durability-point in the Blacksmith config.yml will NOT be used!");
this.useHyperAPI = true;
Plugin hyperConomyPlugin = getServer().getPluginManager().getPlugin("HyperConomy");
bukkitConnector = (BukkitConnector) hyperConomyPlugin;
HyperConomy hyperConomy = Objects.requireNonNull(bukkitConnector).getHC();
this.hyperAPI = (HyperAPI) hyperConomy.getAPI();
}
}
/**
* Sets up Vault for economy
*
* @return <p>True if Vault was successfully set up</p>
*/
private boolean setupVault() {
// Setup Vault
RegisteredServiceProvider<Economy> economyProvider = getServer().getServicesManager().getRegistration(
Economy.class);
if (economyProvider != null) {
economy = economyProvider.getProvider();
return true;
} else {
// Disable if no economy plugin was found
getServer().getLogger().log(Level.SEVERE, "Failed to load an economy plugin. Disabling...");
return false;
}
}
@Override
public boolean onCommand(final CommandSender sender, final @NotNull Command command, final @NotNull String label,
final String[] args) {
//Handle the reload command
config.load();
sender.sendMessage(ChatColor.GREEN + "Blacksmith config reloaded!");
return true;
}
/**
* Gets whether the given item is a type of tool
*
* @param item <p>The item to check if is tool or not</p>
* @return <p>True if the given item is a type of tool</p>
*/
public boolean isTool(ItemStack item) {
return switch (item.getType()) {
case WOODEN_PICKAXE, WOODEN_SHOVEL, WOODEN_HOE, WOODEN_SWORD, WOODEN_AXE, STONE_PICKAXE, STONE_SHOVEL,
STONE_HOE, STONE_SWORD, STONE_AXE, GOLDEN_PICKAXE, GOLDEN_SHOVEL, GOLDEN_HOE, GOLDEN_SWORD,
GOLDEN_AXE, IRON_PICKAXE, IRON_SHOVEL, IRON_HOE, IRON_SWORD, IRON_AXE, DIAMOND_PICKAXE,
DIAMOND_SHOVEL, DIAMOND_HOE, DIAMOND_SWORD, DIAMOND_AXE, NETHERITE_SWORD, NETHERITE_SHOVEL,
NETHERITE_PICKAXE, NETHERITE_AXE, NETHERITE_HOE, BOW, CROSSBOW, FLINT_AND_STEEL, FISHING_ROD,
SHEARS, TRIDENT, SHIELD -> true;
default -> false;
};
}
/**
* Gets whether the given item is a type of armor
*
* @param item <p>The item to check if is armor or not</p>
* @return <p>True if the given item is a type of armor</p>
*/
public static boolean isArmor(ItemStack item) {
return switch (item.getType()) {
case LEATHER_HELMET, LEATHER_CHESTPLATE, LEATHER_LEGGINGS, LEATHER_BOOTS, CHAINMAIL_HELMET,
CHAINMAIL_CHESTPLATE, CHAINMAIL_LEGGINGS, CHAINMAIL_BOOTS, GOLDEN_HELMET, GOLDEN_CHESTPLATE,
GOLDEN_LEGGINGS, GOLDEN_BOOTS, IRON_HELMET, IRON_CHESTPLATE, IRON_LEGGINGS, IRON_BOOTS,
DIAMOND_HELMET, DIAMOND_CHESTPLATE, DIAMOND_LEGGINGS, DIAMOND_BOOTS, TURTLE_HELMET, ELYTRA,
NETHERITE_HELMET, NETHERITE_CHESTPLATE, NETHERITE_LEGGINGS, NETHERITE_BOOTS -> true;
default -> false;
};
}
public boolean doesPlayerHaveEnough(Player player) {
return economy.getBalance(player) - getCost(player.getInventory().getItemInMainHand(), player) >= 0;
}
public String formatCost(Player player) {
double cost = getCost(player.getInventory().getItemInMainHand(), player);
return economy.format(cost);
}
public void withdraw(Player player) {
economy.withdrawPlayer(player, getCost(player.getInventory().getItemInMainHand(), player));
}
/**
* Gets the durability of the given item
*
* @param itemStack <p>The item to get the durability of</p>
* @return <p>The durability of the item</p>
*/
public static short getDurability(ItemStack itemStack) {
Damageable damageable = (Damageable) itemStack.getItemMeta();
return (short) (itemStack.getType().getMaxDurability() - damageable.getDamage());
}
/**
* Gets the damage done to the given item
*
* @param itemStack <p>The damage done to the item</p>
* @return <p>The damage done to the item</p>
*/
public static short getDamage(ItemStack itemStack) {
Damageable damageable = (Damageable) itemStack.getItemMeta();
return (short) damageable.getDamage();
}
private double getCost(ItemStack item, Player player) {
DataKey root = config.getConfig().getKey("");
String itemName = sanitizeItemName(item.getType().name());
double price;
if (root.keyExists("base-prices." + itemName)) {
price = root.getDouble("base-prices." + itemName);
} else {
price = Setting.BASE_PRICE.asDouble();
}
// Adjust price based on durability and enchantments
if (this.useHyperAPI) {
return getHyperAPICost(player, item, root, price);
} else {
double pricePerDurabilityPoint;
if (root.keyExists("price-per-durability-point." + itemName)) {
pricePerDurabilityPoint = root.getDouble("price-per-durability-point." + itemName);
} else {
pricePerDurabilityPoint = Setting.PRICE_PER_DURABILITY_POINT.asDouble();
}
if (config.getNaturalCost()) {
//Cost increases with damage
price += ((double) getDamage(item)) * pricePerDurabilityPoint;
} else {
//Cost decreases with damage
price += ((double) getDurability(item)) * pricePerDurabilityPoint;
}
}
//Add the enchantment modifier for each enchantment on the item
price += getEnchantmentCost(item, root);
return price;
}
private double getHyperAPICost(Player player, ItemStack item, DataKey root, double price) {
// If using hyperConomy, price is calculated like so:
// New Item Price + Enchantments Price (from hyperConomy) / maxDurability = price per durability point
// Total price would then be base_price + price per durability point * current durability
double hyperPrice = 0;
HItemStack hi = hyperAPI.getHyperPlayer(player.getName()).getItemInHand();
ItemStack item2 = player.getInventory().getItemInMainHand().clone();
for (TradeObject enchant : hyperAPI.getEnchantmentHyperObjects(hi, player.getName())) {
hyperPrice = hyperPrice + enchant.getBuyPrice(1);
Enchantment enchantment = Enchantment.getByKey(
NamespacedKey.minecraft(enchant.getEnchantment().getEnchantmentName()));
item2.removeEnchantment(Objects.requireNonNull(enchantment));
}
ArrayList<Material> leathers = new ArrayList<>();
leathers.add(Material.LEATHER_BOOTS);
leathers.add(Material.LEATHER_CHESTPLATE);
leathers.add(Material.LEATHER_HELMET);
leathers.add(Material.LEATHER_LEGGINGS);
HItemStack hi3 = null;
if (leathers.contains(player.getInventory().getItemInMainHand().getType())) {
hi3 = bukkitConnector.getBukkitCommon().getSerializableItemStack(
new ItemStack(player.getInventory().getItemInMainHand().getType()));
}
TradeObject to = this.hyperAPI.getHyperObject(hi, "default");
if (to == null) {
to = hyperAPI.getHyperObject(hi3, "default");
if (to == null) {
HItemStack hi4 = bukkitConnector.getBukkitCommon().getSerializableItemStack(
new ItemStack(player.getInventory().getItemInMainHand().getType()));
to = this.hyperAPI.getHyperObject(hi4, "default");
}
hyperPrice = hyperPrice + to.getSellPrice(1);
} else {
hyperPrice = to.getSellPrice(1);
}
double hyperPricePerDurability = hyperPrice / item.getType().getMaxDurability();
price += getDurability(item) * hyperPricePerDurability;
price += getEnchantmentCost(item2, root);
return price;
}
/**
* Gets the cost resulting from all enchantments on the given item
*
* @param item <p>The item to calculate enchantment cost for</p>
* @param root <p>The data key containing the enchantment-modifiers option</p>
* @return <p>The resulting enchantment cost</p>
*/
private double getEnchantmentCost(ItemStack item, DataKey root) {
double enchantmentModifier = Setting.ENCHANTMENT_MODIFIER.asDouble();
double price = 0;
for (Enchantment enchantment : item.getEnchantments().keySet()) {
String enchantmentKey = "enchantment-modifiers." + sanitizeItemName(enchantment.getKey().asString());
if (root.keyExists(enchantmentKey)) {
price += root.getDouble(enchantmentKey) * item.getEnchantmentLevel(enchantment);
} else {
price += enchantmentModifier * item.getEnchantmentLevel(enchantment);
}
}
return price;
}
}

View File

@ -0,0 +1,269 @@
package net.knarcraft.blacksmith;
import net.citizensnpcs.api.CitizensAPI;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.config.Settings;
import net.knarcraft.blacksmith.util.Sanitizer;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import static net.knarcraft.blacksmith.util.Sanitizer.sanitizedToItemName;
/**
* The class representing the Blacksmith NPC trait
*/
public class BlacksmithTrait extends Trait {
private final BlacksmithPlugin plugin;
private final List<Material> reforgeAbleItems = new ArrayList<>();
private final Map<UUID, Calendar> coolDowns = new HashMap<>();
private ReforgeSession session;
private final Settings config;
private long _sessionStart = System.currentTimeMillis();
/**
* Instantiates a new blacksmith trait
*/
public BlacksmithTrait() {
super("blacksmith");
plugin = (BlacksmithPlugin) Bukkit.getServer().getPluginManager().getPlugin("Blacksmith");
this.config = BlacksmithPlugin.getInstance().getSettings();
}
/**
* Adds a cool-down for the given player's next blacksmith reforge
*
* @param playerUUID <p>The ID of the player to add the cool-down for</p>
* @param waitUntil <p>The time when the player can interact again</p>
*/
public void addCoolDown(UUID playerUUID, Calendar waitUntil) {
coolDowns.put(playerUUID, waitUntil);
}
/**
* Unsets the session of this blacksmith, making it ready for another round
*/
public void unsetSession() {
this.session = null;
}
@Override
public void load(DataKey key) {
for (DataKey sub : key.getRelative("reforge-able-items").getIntegerSubKeys()) {
Material material = Material.getMaterial(sanitizedToItemName(sub.getString("")));
if (material != null) {
reforgeAbleItems.add(material);
}
}
config.loadVariables(key);
}
@Override
public void save(DataKey key) {
//Save all items the blacksmith knows how to reforge
for (int i = 0; i < reforgeAbleItems.size(); i++) {
key.getRelative("reforge-able-items").setString(String.valueOf(i), Sanitizer.sanitizeItemName(
reforgeAbleItems.get(i).name()));
}
//Save all other config values
config.saveVariables(key);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onClick(PlayerInteractEvent event) {
if (event.getHand() == null || (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() !=
Action.RIGHT_CLICK_BLOCK)) {
return;
}
//If the player is not looking at a blacksmith, there is no need to do anything
Entity target = getTarget(event.getPlayer(), event.getPlayer().getLocation().getNearbyEntities(15, 10, 15));
if (!CitizensAPI.getNPCRegistry().isNPC(target) ||
!CitizensAPI.getNPCRegistry().getNPC(target).hasTrait(BlacksmithTrait.class)) {
return;
}
//Block armor equip if interacting with a blacksmith
ItemStack usedItem = event.getPlayer().getInventory().getItem(event.getHand());
if (usedItem != null && BlacksmithPlugin.isArmor(usedItem)) {
event.setUseItemInHand(Event.Result.DENY);
event.getPlayer().updateInventory();
}
}
@EventHandler
public void onRightClick(net.citizensnpcs.api.event.NPCRightClickEvent event) {
if (this.npc != event.getNPC()) {
return;
}
//Perform any necessary pre-session work
Player player = event.getClicker();
if (!prepareForSession(player)) {
return;
}
if (session != null) {
//Continue the existing session
continueSession(player);
} else {
//Start a new session
startSession(player);
}
}
/**
* Performs necessary work before the session is started or continued
*
* @param player <p>The player to prepare the session for</p>
* @return <p>True if preparations were successful. False if a session shouldn't be started</p>
*/
private boolean prepareForSession(Player player) {
//If cool-down has been disabled after it was set for this player, remove the cool-down
if (config.getDisableCoolDown() && coolDowns.get(player.getUniqueId()) != null) {
coolDowns.remove(player.getUniqueId());
}
//Deny if permission is missing
if (!player.hasPermission("blacksmith.reforge")) {
return false;
}
//Deny if on cool-down, or remove cool-down if expired
if (coolDowns.get(player.getUniqueId()) != null) {
if (!Calendar.getInstance().after(coolDowns.get(player.getUniqueId()))) {
player.sendMessage(config.getCoolDownUnexpiredMessage());
return false;
}
coolDowns.remove(player.getUniqueId());
}
//If already in a session, but the player has failed to interact, or left the blacksmith, allow a new session
if (session != null) {
if (System.currentTimeMillis() > _sessionStart + 10 * 1000 ||
this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20) {
session = null;
}
}
return true;
}
/**
* Tries to continue the session for the given player
*
* @param player <p>The player to continue the session for</p>
*/
private void continueSession(Player player) {
//Another player is using the blacksmith
if (!session.isInSession(player)) {
player.sendMessage(config.getBusyWithPlayerMessage());
return;
}
//The blacksmith is already reforging for the player
if (session.isRunning()) {
player.sendMessage(config.getBusyReforgingMessage());
return;
}
if (session.endSession()) {
//Quit if the player cannot afford, or has changed their item
session = null;
} else {
//Start reforging for the player
reforge(npc, player);
}
}
/**
* Starts a new session, and prepares to repair the player's item
*
* @param player <p>The player to start the session for</p>
*/
private void startSession(Player player) {
ItemStack hand = player.getInventory().getItemInMainHand();
//If not a tool, not armor, and not set in reforgeAbleItems, refuse to repair
if ((!plugin.isTool(hand) && !BlacksmithPlugin.isArmor(hand)) ||
(!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) {
player.sendMessage(config.getInvalidItemMessage());
return;
}
//Start a new reforge session for the player
_sessionStart = System.currentTimeMillis();
session = new ReforgeSession(this, player, npc, config);
//Tell the player the cost of repairing the item
String cost = plugin.formatCost(player);
String itemName = hand.getType().name().toLowerCase().replace('_', ' ');
player.sendMessage(config.getCostMessage().replace("<price>", cost).replace("<item>", itemName));
}
/**
* Starts reforging the player's item
*
* @param npc <p>The NPC performing the reforge</p>
* @param player <p>The player that initiated the reforge</p>
*/
private void reforge(NPC npc, Player player) {
player.sendMessage(config.getStartReforgeMessage());
plugin.withdraw(player);
session.beginReforge();
ItemStack heldItem = player.getInventory().getItemInMainHand();
//Display the item in the NPC's hand
if (npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(heldItem);
} else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(heldItem);
}
//Remove the item from the player's inventory
player.getInventory().setItemInMainHand(null);
}
/**
* Gets the target-entity the entity is looking at
*
* @param entity <p>The entity looking at something</p>
* @param entities <p>Entities near the player</p>
* @param <T> <p>The type of entity the player is looking at</p>
* @return <p>The entity the player is looking at, or null if no such entity exists</p>
*/
private static <T extends Entity> T getTarget(final Entity entity, final Iterable<T> entities) {
if (entity == null) {
return null;
}
T target = null;
final double threshold = 1;
for (final T other : entities) {
final Vector n = other.getLocation().toVector().subtract(entity.getLocation().toVector());
if (entity.getLocation().getDirection().normalize().crossProduct(n).lengthSquared() < threshold &&
n.normalize().dot(entity.getLocation().getDirection().normalize()) >= 0) {
if (target == null ||
target.getLocation().distanceSquared(entity.getLocation()) >
other.getLocation().distanceSquared(entity.getLocation())) {
target = other;
}
}
}
return target;
}
}

View File

@ -0,0 +1,166 @@
package net.knarcraft.blacksmith;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.config.Settings;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Calendar;
import java.util.Objects;
import java.util.Random;
/**
* A representation of the session between a player and a blacksmith
*/
public class ReforgeSession implements Runnable {
private final BlacksmithTrait blacksmithTrait;
private final Player player;
private final NPC npc;
private final ItemStack itemToReforge;
private int taskId;
private final Settings config;
private static final String[] enchantments = new String[Enchantment.values().length];
/**
* Instantiates a new session
*
* @param blacksmithTrait <p>A reference to the blacksmith trait</p>
* @param player <p>The player initiating the session</p>
* @param npc <p>The Blacksmith NPC involved in the session</p>
* @param config <p>The config to use for the session</p>
*/
ReforgeSession(BlacksmithTrait blacksmithTrait, Player player, NPC npc, Settings config) {
this.blacksmithTrait = blacksmithTrait;
this.player = player;
this.npc = npc;
itemToReforge = player.getInventory().getItemInMainHand();
this.config = config;
int i = 0;
for (Enchantment enchantment : Enchantment.values()) {
enchantments[i++] = enchantment.getKey().asString();
}
}
@Override
public void run() {
player.sendMessage(reforgeItemInHand() ? config.getSuccessMessage() : config.getFailMessage());
if (npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(null);
} else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(null);
}
if (!config.getDisableDelay()) {
if (config.getDropItem()) {
player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), itemToReforge);
} else {
player.getInventory().addItem(itemToReforge);
}
} else {
player.getInventory().setItemInMainHand(itemToReforge);
}
blacksmithTrait.unsetSession();
// Start cool down
Calendar wait = Calendar.getInstance();
wait.add(Calendar.SECOND, config.getReforgeCoolDown());
blacksmithTrait.addCoolDown(player.getUniqueId(), wait);
}
private boolean reforgeItemInHand() {
Random random = new Random();
if (random.nextInt(100) < config.getFailChance()) {
for (Enchantment enchantment : itemToReforge.getEnchantments().keySet()) {
// Remove or downgrade enchantments
if (random.nextBoolean()) {
itemToReforge.removeEnchantment(enchantment);
} else {
if (itemToReforge.getEnchantmentLevel(enchantment) > 1) {
itemToReforge.removeEnchantment(enchantment);
itemToReforge.addEnchantment(enchantment, 1);
}
}
}
// Damage the item
short reforgeDurability = BlacksmithPlugin.getDurability(itemToReforge);
short durability = (short) (reforgeDurability + reforgeDurability * random.nextInt(8));
short maxDurability = itemToReforge.getType().getMaxDurability();
if (durability <= 0) {
durability = (short) (maxDurability / 3);
} else if (reforgeDurability + durability > maxDurability) {
durability = (short) (maxDurability - random.nextInt(maxDurability - 25));
}
updateDamage(itemToReforge, maxDurability - durability);
return false;
} else {
updateDamage(itemToReforge, 0);
// Add random enchantments
int roll = random.nextInt(100);
if (roll < config.getExtraEnchantmentChance() && itemToReforge.getEnchantments().keySet().size() < config.getMaxEnchantments()) {
Enchantment enchantment = Enchantment.getByKey(NamespacedKey.fromString(enchantments[random.nextInt(enchantments.length)]));
if (Objects.requireNonNull(enchantment).canEnchantItem(itemToReforge)) {
int randomBound = enchantment.getMaxLevel() - enchantment.getStartLevel();
//A workaround for the random method's bound sometimes being negative
if (randomBound >= 0) {
itemToReforge.addEnchantment(enchantment, random.nextInt(randomBound) +
enchantment.getStartLevel());
}
}
}
return true;
}
}
private void updateDamage(ItemStack item, int newDamage) {
ItemMeta meta = item.getItemMeta();
Damageable damageable = (Damageable) meta;
damageable.setDamage(newDamage);
item.setItemMeta(meta);
}
// Return if the session should end
boolean endSession() {
// Prevent player from switching items during session
ItemStack itemInHand = player.getInventory().getItemInMainHand();
if (!itemToReforge.equals(itemInHand)) {
player.sendMessage(config.getItemChangedMessage());
return true;
}
if (!BlacksmithPlugin.getInstance().doesPlayerHaveEnough(player)) {
player.sendMessage(config.getInsufficientFundsMessage());
return true;
}
return false;
}
boolean isRunning() {
return BlacksmithPlugin.getInstance().getServer().getScheduler().isQueued(taskId);
}
boolean isInSession(Player other) {
return player.getName().equals(other.getName());
}
void beginReforge() {
if (!config.getDisableCoolDown()) {
taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(
BlacksmithPlugin.getInstance(), this, (new Random().nextInt(config.getMaxReforgeDelay()) +
config.getMinReforgeDelay()) * 20L);
} else {
taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(
BlacksmithPlugin.getInstance(), this, 0);
}
}
public Player getPlayer() {
return player;
}
}

View File

@ -0,0 +1,151 @@
package net.knarcraft.blacksmith.config;
import java.util.Arrays;
/**
* An enum representing all of Blacksmith's settings
*/
public enum Setting {
BASE_PRICE("base-prices.default", 10.0),
PRICE_PER_DURABILITY_POINT("price-per-durability-point.default", 1.0),
BUSY_WITH_PLAYER_MESSAGE("defaults.messages.busy-with-player", "§cI'm busy at the moment. Come back later!"),
BUSY_WITH_REFORGE_MESSAGE("defaults.messages.busy-with-reforge", "§cI'm working on it. Be patient!"),
COOL_DOWN_UNEXPIRED_MESSAGE(
"defaults.messages.cool-down-not-expired",
"§cYou've already had your chance! Give me a break!"),
COST_MESSAGE(
"defaults.messages.cost",
"§eIt will cost §a<price> §eto reforge that §a<item>§e! Click again to reforge!"),
DROP_ITEM("defaults.drop-item", true),
DISABLE_COOL_DOWN("defaults.disable-cool-down", false),
DISABLE_DELAY("defaults.disable-delay", false),
ENCHANTMENT_MODIFIER("enchantment-modifiers.default", 5),
FAIL_CHANCE("defaults.percent-chance-to-fail-reforge", 10),
FAIL_MESSAGE("defaults.messages.fail-reforge", "§cWhoops! Didn't mean to do that! Maybe next time?"),
INSUFFICIENT_FUNDS_MESSAGE(
"defaults.messages.insufficient-funds",
"§cYou don't have enough money to reforge that item!"),
INVALID_ITEM_MESSAGE("defaults.messages.invalid-item", "§cI'm sorry, but I don't know how to reforge that!"),
ITEM_UNEXPECTEDLY_CHANGED_MESSAGE(
"defaults.messages.item-changed-during-reforge",
"§cThat's not the item you wanted to reforge before!"),
EXTRA_ENCHANTMENT_CHANCE("defaults.percent-chance-for-extra-enchantment", 5),
MAX_ENCHANTMENTS("defaults.maximum-enchantments", 3),
MAX_REFORGE_DELAY("defaults.delays-in-seconds.maximum", 30),
MIN_REFORGE_DELAY("defaults.delays-in-seconds.minimum", 5),
REFORGE_COOL_DOWN("defaults.delays-in-seconds.reforge-cool-down", 60),
START_REFORGE_MESSAGE("defaults.messages.start-reforge", "§eOk, let's see what I can do..."),
SUCCESS_MESSAGE("defaults.messages.successful-reforge", "There you go! All better!"),
NATURAL_COST("defaults.natural-cost", true);
private final String path;
private final String childPath;
private Object value;
/**
* Instantiates a new setting
*
* @param path <p>The full config path for this setting</p>
* @param value <p>The default value of this setting</p>
*/
Setting(String path, Object value) {
this.path = path;
this.value = value;
String[] pathParts = path.split("\\.");
this.childPath = String.join(".", Arrays.copyOfRange(pathParts, 1, pathParts.length));
}
/**
* Gets the full config path for this setting
*
* @return <p>The full config path for this setting</p>
*/
public String getPath() {
return path;
}
/**
* Gets the config path without the root node
*
* @return <p>The config path without the root node</p>
*/
public String getChildPath() {
return childPath;
}
/**
* Gets this setting as a boolean
*
* <p>This will throw an exception if used for a non-boolean value</p>
*
* @return <p>This setting as a boolean</p>
*/
public boolean asBoolean() {
if (value instanceof String) {
return Boolean.parseBoolean((String) value);
} else {
return (Boolean) value;
}
}
/**
* Gets this setting as a double
*
* <p>This will throw an exception if used for a non-double setting</p>
*
* @return <p>This setting as a double</p>
*/
public double asDouble() {
if (value instanceof String) {
return Double.parseDouble((String) value);
} else if (value instanceof Integer) {
return (Integer) value;
} else {
return (Double) value;
}
}
/**
* Gets this setting as an integer
*
* <p>This will throw an exception if used for a non-integer setting</p>
*
* @return <p>This setting as an integer</p>
*/
public int asInt() {
if (value instanceof String) {
return Integer.parseInt((String) value);
} else {
return (Integer) value;
}
}
/**
* Gets this setting as a string
*
* @return <p>This setting as a string</p>
*/
public String asString() {
return value.toString();
}
/**
* Gets the value of this setting
*
* @return <p>The value of this setting</p>
*/
Object get() {
return value;
}
/**
* Sets the value of this setting
*
* @param value <p>The new value of this setting</p>
*/
void set(Object value) {
this.value = value;
}
}

View File

@ -0,0 +1,357 @@
package net.knarcraft.blacksmith.config;
import net.citizensnpcs.api.util.DataKey;
import net.citizensnpcs.api.util.YamlStorage;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import java.io.File;
/**
* A class which keeps track of all Blacksmith settings/config values
*/
public class Settings {
private String busyWithPlayerMessage = Setting.BUSY_WITH_PLAYER_MESSAGE.asString();
private String busyReforgingMessage = Setting.BUSY_WITH_REFORGE_MESSAGE.asString();
private String costMessage = Setting.COST_MESSAGE.asString();
private String invalidItemMessage = Setting.INVALID_ITEM_MESSAGE.asString();
private String startReforgeMessage = Setting.START_REFORGE_MESSAGE.asString();
private String successMessage = Setting.SUCCESS_MESSAGE.asString();
private String failMessage = Setting.FAIL_MESSAGE.asString();
private String insufficientFundsMessage = Setting.INSUFFICIENT_FUNDS_MESSAGE.asString();
private String coolDownUnexpiredMessage = Setting.COOL_DOWN_UNEXPIRED_MESSAGE.asString();
private String itemChangedMessage = Setting.ITEM_UNEXPECTEDLY_CHANGED_MESSAGE.asString();
private int minReforgeDelay = Setting.MIN_REFORGE_DELAY.asInt();
private int maxReforgeDelay = Setting.MAX_REFORGE_DELAY.asInt();
private int reforgeCoolDown = Setting.REFORGE_COOL_DOWN.asInt();
private int failChance = Setting.FAIL_CHANCE.asInt();
private int extraEnchantmentChance = Setting.EXTRA_ENCHANTMENT_CHANCE.asInt();
private int maxEnchantments = Setting.MAX_ENCHANTMENTS.asInt();
private boolean dropItem = Setting.DROP_ITEM.asBoolean();
private boolean disableCoolDown = Setting.DISABLE_COOL_DOWN.asBoolean();
private boolean disableDelay = Setting.DISABLE_DELAY.asBoolean();
private boolean naturalCost = Setting.NATURAL_COST.asBoolean();
private final YamlStorage config;
/**
* Instantiates a new "Settings"
*
* @param plugin <p>A reference to the blacksmith plugin</p>
*/
public Settings(BlacksmithPlugin plugin) {
config = new YamlStorage(new File(plugin.getDataFolder() + File.separator + "config.yml"),
"Blacksmith Configuration\nWarning: The values under defaults are the values set for a blacksmith" +
"upon creation. To change any values for existing NPCs, edit the citizens NPC file.");
}
/**
* Loads all configuration values from the config file
*/
public void load() {
//Load the config from disk
config.load();
DataKey root = config.getKey("");
for (Setting setting : Setting.values()) {
if (!root.keyExists(setting.getPath())) {
//If the setting does not exist in the config file, add it
root.setRaw(setting.getPath(), setting.get());
} else {
//Set the setting to the value found in the path
setting.set(root.getRaw(setting.getPath()));
}
}
//Save any modified values to disk
config.save();
}
/**
* Gets the configuration used for saving/loading from disk
*
* @return <p>The configuration</p>
*/
public YamlStorage getConfig() {
return config;
}
/**
* Loads variables from the given data key
*
* @param key <p>The data key to load variables from</p>
*/
public void loadVariables(DataKey key) {
// Override defaults if they exist
if (key.keyExists(Setting.BUSY_WITH_PLAYER_MESSAGE.getChildPath())) {
busyWithPlayerMessage = key.getString(Setting.BUSY_WITH_PLAYER_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.BUSY_WITH_REFORGE_MESSAGE.getChildPath())) {
busyReforgingMessage = key.getString(Setting.BUSY_WITH_REFORGE_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.COST_MESSAGE.getChildPath())) {
costMessage = key.getString(Setting.COST_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.INVALID_ITEM_MESSAGE.getChildPath())) {
invalidItemMessage = key.getString(Setting.INVALID_ITEM_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.START_REFORGE_MESSAGE.getChildPath())) {
startReforgeMessage = key.getString(Setting.START_REFORGE_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.SUCCESS_MESSAGE.getChildPath())) {
successMessage = key.getString(Setting.SUCCESS_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.FAIL_MESSAGE.getChildPath())) {
failMessage = key.getString(Setting.FAIL_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.INSUFFICIENT_FUNDS_MESSAGE.getChildPath())) {
insufficientFundsMessage = key.getString(Setting.INSUFFICIENT_FUNDS_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.COOL_DOWN_UNEXPIRED_MESSAGE.getChildPath())) {
coolDownUnexpiredMessage = key.getString(Setting.COOL_DOWN_UNEXPIRED_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.ITEM_UNEXPECTEDLY_CHANGED_MESSAGE.getChildPath())) {
itemChangedMessage = key.getString(Setting.ITEM_UNEXPECTEDLY_CHANGED_MESSAGE.getChildPath());
}
if (key.keyExists(Setting.MIN_REFORGE_DELAY.getChildPath())) {
minReforgeDelay = key.getInt(Setting.MIN_REFORGE_DELAY.getChildPath());
}
if (key.keyExists(Setting.MAX_REFORGE_DELAY.getChildPath())) {
maxReforgeDelay = key.getInt(Setting.MAX_REFORGE_DELAY.getChildPath());
}
if (key.keyExists(Setting.REFORGE_COOL_DOWN.getChildPath())) {
reforgeCoolDown = key.getInt(Setting.REFORGE_COOL_DOWN.getChildPath());
}
if (key.keyExists(Setting.FAIL_CHANCE.getChildPath())) {
failChance = key.getInt(Setting.FAIL_CHANCE.getChildPath());
}
if (key.keyExists(Setting.MAX_ENCHANTMENTS.getChildPath())) {
maxEnchantments = key.getInt(Setting.MAX_ENCHANTMENTS.getChildPath());
}
if (key.keyExists(Setting.EXTRA_ENCHANTMENT_CHANCE.getChildPath())) {
extraEnchantmentChance = key.getInt(Setting.EXTRA_ENCHANTMENT_CHANCE.getChildPath());
}
if (key.keyExists(Setting.DROP_ITEM.getChildPath())) {
dropItem = key.getBoolean(Setting.DROP_ITEM.getChildPath());
}
if (key.keyExists(Setting.DISABLE_COOL_DOWN.getChildPath())) {
disableCoolDown = key.getBoolean(Setting.DISABLE_COOL_DOWN.getChildPath());
}
if (key.keyExists(Setting.DISABLE_DELAY.getChildPath())) {
disableDelay = key.getBoolean(Setting.DISABLE_DELAY.getChildPath());
}
if (key.keyExists(Setting.NATURAL_COST.getChildPath())) {
naturalCost = key.getBoolean(Setting.NATURAL_COST.getChildPath());
}
}
/**
* Saves variables to the given data key
*
* @param key <p>The data key to save variables to</p>
*/
public void saveVariables(DataKey key) {
//This saves variables to the specific NPC.
key.setString(Setting.BUSY_WITH_PLAYER_MESSAGE.getChildPath(), getBusyWithPlayerMessage());
key.setString(Setting.BUSY_WITH_REFORGE_MESSAGE.getChildPath(), getBusyReforgingMessage());
key.setString(Setting.COST_MESSAGE.getChildPath(), getCostMessage());
key.setString(Setting.INVALID_ITEM_MESSAGE.getChildPath(), getInvalidItemMessage());
key.setString(Setting.START_REFORGE_MESSAGE.getChildPath(), getStartReforgeMessage());
key.setString(Setting.SUCCESS_MESSAGE.getChildPath(), getSuccessMessage());
key.setString(Setting.FAIL_MESSAGE.getChildPath(), getFailMessage());
key.setString(Setting.INSUFFICIENT_FUNDS_MESSAGE.getChildPath(), getInsufficientFundsMessage());
key.setString(Setting.COOL_DOWN_UNEXPIRED_MESSAGE.getChildPath(), getCoolDownUnexpiredMessage());
key.setString(Setting.ITEM_UNEXPECTEDLY_CHANGED_MESSAGE.getChildPath(), getItemChangedMessage());
key.setInt(Setting.MIN_REFORGE_DELAY.getChildPath(), getMinReforgeDelay());
key.setInt(Setting.MAX_REFORGE_DELAY.getChildPath(), getMaxReforgeDelay());
key.setInt(Setting.REFORGE_COOL_DOWN.getChildPath(), getReforgeCoolDown());
key.setInt(Setting.FAIL_CHANCE.getChildPath(), getFailChance());
key.setInt(Setting.EXTRA_ENCHANTMENT_CHANCE.getChildPath(), getExtraEnchantmentChance());
key.setInt(Setting.MAX_ENCHANTMENTS.getChildPath(), getMaxEnchantments());
key.setBoolean(Setting.DROP_ITEM.getChildPath(), getDropItem());
key.setBoolean(Setting.DISABLE_DELAY.getChildPath(), getDisableDelay());
key.setBoolean(Setting.DISABLE_COOL_DOWN.getChildPath(), getDisableCoolDown());
key.setBoolean(Setting.DISABLE_COOL_DOWN.getChildPath(), getDisableCoolDown());
key.setBoolean(Setting.NATURAL_COST.getChildPath(), getNaturalCost());
}
/**
* Gets the message to display when the blacksmith is busy with another player
*
* @return <p>The busy with player message</p>
*/
public String getBusyWithPlayerMessage() {
return busyWithPlayerMessage;
}
/**
* Gets the message to display when the blacksmith is busy with reforging an item
*
* @return <p>The busy reforging message</p>
*/
public String getBusyReforgingMessage() {
return busyReforgingMessage;
}
/**
* Gets the message to use for displaying an item's cost
*
* @return <p>The message to use for displaying item cost</p>
*/
public String getCostMessage() {
return costMessage;
}
/**
* Gets the message to display when a blacksmith has been given an invalid item
*
* @return <p>The invalid item message</p>
*/
public String getInvalidItemMessage() {
return invalidItemMessage;
}
/**
* Gets the message to display when a blacksmith starts reforging an item
*
* @return <p>The start reforge message</p>
*/
public String getStartReforgeMessage() {
return startReforgeMessage;
}
/**
* Gets the message to display when a blacksmith has successfully repaired an item
*
* @return <p>The reforge success message</p>
*/
public String getSuccessMessage() {
return successMessage;
}
/**
* Gets the message to display when a blacksmith has failed to repair an item
*
* @return <p>The reforge fail message</p>
*/
public String getFailMessage() {
return failMessage;
}
/**
* Gets the message to display when a player cannot afford re-forging an item
*
* @return <p>The insufficient funds message</p>
*/
public String getInsufficientFundsMessage() {
return insufficientFundsMessage;
}
/**
* Gets the message to display when a blacksmith is still affected by a cool-down
*
* @return <p>The cool down unexpired message</p>
*/
public String getCoolDownUnexpiredMessage() {
return coolDownUnexpiredMessage;
}
/**
* Gets the message to display when a player has changed the item they're trying to reforge
*
* @return <p>The item changed message</p>
*/
public String getItemChangedMessage() {
return itemChangedMessage;
}
/**
* Gets the minimum delay used to wait for a re-forge to finish.
*
* @return <p>The minimum reforge delay</p>
*/
public int getMinReforgeDelay() {
return minReforgeDelay;
}
/**
* Gets the maximum delay used to wait for a re-forge to finish
*
* @return <p>The maximum reforge delay</p>
*/
public int getMaxReforgeDelay() {
return maxReforgeDelay;
}
/**
* Gets the cool-down between each reforge
*
* @return <p>The reforge cool-down</p>
*/
public int getReforgeCoolDown() {
return reforgeCoolDown;
}
/**
* Gets the chance to fail a re-forge
*
* @return <p>The fail chance</p>
*/
public int getFailChance() {
return failChance;
}
/**
* Gets the chance for adding an extra enchantment to an item
*
* @return <p>The extra enchantment chance</p>
*/
public int getExtraEnchantmentChance() {
return extraEnchantmentChance;
}
/**
* Gets the max number of enchantment to add to an item
*
* @return <p>The maximum enchantments</p>
*/
public int getMaxEnchantments() {
return maxEnchantments;
}
/**
* Gets whether an item should be dropped on the ground instead of being given to the player
*
* @return <p>Whether to drop reforged items on the ground</p>
*/
public boolean getDropItem() {
return dropItem;
}
/**
* Gets whether to disable the reforge-cool-down
*
* @return <p>Whether to disable the reforge-cool-down</p>
*/
public boolean getDisableCoolDown() {
return disableCoolDown;
}
/**
* Gets whether to disable the delay between starting reforging and the re-forge finishing
*
* @return <p>Whether to disable the reforge delay</p>
*/
public boolean getDisableDelay() {
return disableDelay;
}
/**
* Gets whether to use a natural cost calculation
*
* <p>The natural cost makes repairs more expensive the more damaged an item is, rather than the opposite.</p>
*
* @return <p>Whether to use a natural cost calculation</p>
*/
public boolean getNaturalCost() {
return naturalCost;
}
}

View File

@ -0,0 +1,29 @@
package net.knarcraft.blacksmith.util;
public final class Sanitizer {
private Sanitizer() {
}
/**
* Sanitizes an item name to the format used by this plugin
*
* @param itemName <p>The item name to sanitize</p>
* @return <p>The sanitized name</p>
*/
public static String sanitizeItemName(String itemName) {
return itemName.toLowerCase().replace('_', '-');
}
/**
* Converts a sanitized item name to the original name
*
* @param itemName <p>The item name to convert</p>
* @return <p>The un-sanitized name</p>
*/
public static String sanitizedToItemName(String itemName) {
return itemName.toUpperCase().replace('-', '_');
}
}