package net.apunch.blacksmith; import net.apunch.blacksmith.util.Settings.Setting; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; 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.Random; public class BlacksmithTrait extends Trait { private static final String[] enchantments = new String[Enchantment.values().length]; private final BlacksmithPlugin plugin; private final List reforgeAbleItems = new ArrayList<>(); private final Map coolDowns = new HashMap<>(); private ReforgeSession session; // Defaults private String busyWithPlayerMsg = Setting.BUSY_WITH_PLAYER_MESSAGE.asString(); private String busyReforgingMsg = Setting.BUSY_WITH_REFORGE_MESSAGE.asString(); private String costMsg = Setting.COST_MESSAGE.asString(); private String invalidItemMsg = Setting.INVALID_ITEM_MESSAGE.asString(); private String startReforgeMsg = Setting.START_REFORGE_MESSAGE.asString(); private String successMsg = Setting.SUCCESS_MESSAGE.asString(); private String failMsg = Setting.FAIL_MESSAGE.asString(); private String insufficientFundsMsg = Setting.INSUFFICIENT_FUNDS_MESSAGE.asString(); private String coolDownUnexpiredMessage = Setting.COOLDOWN_UNEXPIRED_MESSAGE.asString(); private String itemChangedMsg = 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_COOLDOWN.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_COOLDOWN.asBoolean(); private boolean disableDelay = Setting.DISABLE_DELAY.asBoolean(); public BlacksmithTrait() { super("blacksmith"); plugin = (BlacksmithPlugin) Bukkit.getServer().getPluginManager().getPlugin("Blacksmith"); int i = 0; for (Enchantment enchantment : Enchantment.values()) { enchantments[i++] = enchantment.getKey().asString(); } } @Override public void load(DataKey key) { for (DataKey sub : key.getRelative("reforgeable-items").getIntegerSubKeys()) { if (Material.getMaterial(sub.getString("").toUpperCase().replace('-', '_')) != null) { reforgeAbleItems.add(Material.getMaterial(sub.getString("").toUpperCase().replace('-', '_'))); } } // Override defaults if they exist if (key.keyExists("messages.busy-with-player")) { busyWithPlayerMsg = key.getString("messages.busy-with-player"); } if (key.keyExists("messages.busy-with-reforge")) { busyReforgingMsg = key.getString("messages.busy-with-reforge"); } if (key.keyExists("messages.cost")) { costMsg = key.getString("messages.cost"); } if (key.keyExists("messages.invalid-item")) { invalidItemMsg = key.getString("messages.invalid-item"); } if (key.keyExists("messages.start-reforge")) { startReforgeMsg = key.getString("messages.start-reforge"); } if (key.keyExists("messages.successful-reforge")) { successMsg = key.getString("messages.successful-reforge"); } if (key.keyExists("messages.fail-reforge")) { failMsg = key.getString("messages.fail-reforge"); } if (key.keyExists("messages.insufficient-funds")) { insufficientFundsMsg = key.getString("messages.insufficient-funds"); } if (key.keyExists("messages.cooldown-not-expired")) { coolDownUnexpiredMessage = key.getString("messages.cooldown-not-expired"); } if (key.keyExists("messages.item-changed-during-reforge")) { itemChangedMsg = key.getString("messages.item-changed-during-reforge"); } if (key.keyExists("delays-in-seconds.minimum")) { minReforgeDelay = key.getInt("delays-in-seconds.minimum"); } if (key.keyExists("delays-in-seconds.maximum")) { maxReforgeDelay = key.getInt("delays-in-seconds.maximum"); } if (key.keyExists("delays-in-seconds.reforge-cooldown")) { reforgeCoolDown = key.getInt("delays-in-seconds.reforge-cooldown"); } if (key.keyExists("percent-chance-to-fail-reforge")) { failChance = key.getInt("percent-chance-to-fail-reforge"); } if (key.keyExists("maximum-enchantments")) { maxEnchantments = key.getInt("maximum-enchantments"); } if (key.keyExists("extra-enchantments-chance")) { extraEnchantmentChance = key.getInt("extra-enchantment-chance"); } if (key.keyExists("dropitem")) { dropItem = key.getBoolean("dropitem"); } if (key.keyExists("disable-cooldown")) { disableCoolDown = key.getBoolean("disable-cooldown"); } if (key.keyExists("disable-delay")) { disableDelay = key.getBoolean("disable-delay"); } } @EventHandler public void onRightClick(net.citizensnpcs.api.event.NPCRightClickEvent event) { if (this.npc != event.getNPC()) { return; } Player player = event.getClicker(); if ((disableCoolDown & (coolDowns.get(player.getName()) != (null)))) { coolDowns.remove(player.getName()); } if (!player.hasPermission("blacksmith.reforge")) { return; } if (coolDowns.get(player.getName()) != null) { if (!Calendar.getInstance().after(coolDowns.get(player.getName()))) { player.sendMessage(coolDownUnexpiredMessage); return; } coolDowns.remove(player.getName()); } ItemStack hand = player.getInventory().getItemInMainHand(); if (session != null) { //timeout if (System.currentTimeMillis() > _sessionstart + 10 * 1000 || this.npc.getEntity().getLocation().distance(session.player.getLocation()) > 20) { session = null; } } if (session != null) { if (!session.isInSession(player)) { player.sendMessage(busyWithPlayerMsg); return; } if (session.isRunning()) { player.sendMessage(busyReforgingMsg); return; } if (session.handleClick()) { session = null; } else { reforge(npc, player); } } else { if ((!plugin.isTool(hand) && !plugin.isArmor(hand)) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) { player.sendMessage(invalidItemMsg); return; } String cost = plugin.formatCost(player); _sessionstart = System.currentTimeMillis(); session = new ReforgeSession(player, npc); player.sendMessage(costMsg.replace("", cost).replace("", hand.getType().name().toLowerCase().replace('_', ' '))); } } private long _sessionstart = System.currentTimeMillis(); @Override public void save(DataKey key) { for (int i = 0; i < reforgeAbleItems.size(); i++) { key.getRelative("reforgeable-items").setString(String.valueOf(i), reforgeAbleItems.get(i).name().toLowerCase().replace('_', '-')); } key.setString("messages.busy-with-player", busyWithPlayerMsg); key.setString("messages.busy-with-reforge", busyReforgingMsg); key.setString("messages.cost", costMsg); key.setString("messages.invalid-item", invalidItemMsg); key.setString("messages.start-reforge", startReforgeMsg); key.setString("messages.successful-reforge", successMsg); key.setString("messages.fail-reforge", failMsg); key.setString("messages.insufficient-funds", insufficientFundsMsg); key.setString("messages.cooldown-not-expired", coolDownUnexpiredMessage); key.setString("messages.item-changed-during-reforge", itemChangedMsg); key.setInt("delays-in-seconds.minimum", minReforgeDelay); key.setInt("delays-in-seconds.maximum", maxReforgeDelay); key.setInt("delays-in-seconds.reforge-cooldown", reforgeCoolDown); key.setInt("percent-chance-to-fail-reforge", failChance); key.setInt("percent-chance-for-extra-enchantment", extraEnchantmentChance); key.setInt("maximum-enchantments", maxEnchantments); key.setBoolean("drop-item", dropItem); key.setBoolean("disable-delay", disableDelay); key.setBoolean("disable-cooldown", disableCoolDown); } private void reforge(NPC npc, Player player) { player.sendMessage(startReforgeMsg); //plugin.deposit(npc, player); // CitiTrader dependency outdated and broken plugin.withdraw(player); session.beginReforge(); if (npc.getEntity() instanceof Player) { ((Player) npc.getEntity()).getInventory().setItemInMainHand(player.getInventory().getItemInMainHand()); } else { Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(player.getInventory().getItemInMainHand()); } player.getInventory().setItemInMainHand(null); } private class ReforgeSession implements Runnable { private final Player player; private final NPC npc; private final ItemStack reforge; private int taskId; private ReforgeSession(Player player, NPC npc) { this.player = player; this.npc = npc; reforge = player.getInventory().getItemInMainHand(); } @Override public void run() { player.sendMessage(reforgeItemInHand() ? successMsg : failMsg); if (npc.getEntity() instanceof Player) { ((Player) npc.getEntity()).getInventory().setItemInMainHand(null); } else { Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(null); } if (!disableDelay) { if (dropItem) { player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), reforge); } else { player.getInventory().addItem(reforge); } } else { player.getInventory().setItemInMainHand(reforge); } session = null; // Start cooldown Calendar wait = Calendar.getInstance(); wait.add(Calendar.SECOND, reforgeCoolDown); coolDowns.put(player.getName(), wait); } private boolean reforgeItemInHand() { Random random = new Random(); if (random.nextInt(100) < failChance) { for (Enchantment enchantment : reforge.getEnchantments().keySet()) { // Remove or downgrade enchantments if (random.nextBoolean()) { reforge.removeEnchantment(enchantment); } else { if (reforge.getEnchantmentLevel(enchantment) > 1) { reforge.removeEnchantment(enchantment); reforge.addEnchantment(enchantment, 1); } } } // Damage the item short durability = (short) (((Damageable) reforge).getDamage() + ((Damageable) reforge).getDamage() * random.nextInt(8)); short maxDurability = reforge.getType().getMaxDurability(); if (durability <= 0) { durability = (short) (maxDurability / 3); } else if (((Damageable) reforge).getDamage() + durability > maxDurability) { durability = (short) (maxDurability - random.nextInt(maxDurability - 25)); } ((Damageable) reforge).setDamage(durability); return false; } ((Damageable) reforge).setDamage((short) 0); // Add random enchantments // If durability is full, chance is multiplied by 4. Seems unbalanced, so disabled for now. /*if (reforge.getDurability() == 0) chance *= 4; else */ int roll = random.nextInt(100); if (roll < extraEnchantmentChance && reforge.getEnchantments().keySet().size() < maxEnchantments) { Enchantment enchantment = Enchantment.getByKey(NamespacedKey.fromString(enchantments[random.nextInt(enchantments.length)])); if (Objects.requireNonNull(enchantment).canEnchantItem(reforge)) { reforge.addEnchantment(enchantment, random.nextInt(enchantment.getMaxLevel() - enchantment.getStartLevel()) + enchantment.getStartLevel()); } } return true; } // Return if the session should end private boolean handleClick() { // Prevent player from switching items during session if (!reforge.equals(player.getInventory().getItemInMainHand())) { player.sendMessage(itemChangedMsg); return true; } if (!plugin.doesPlayerHaveEnough(player)) { player.sendMessage(insufficientFundsMsg); return true; } return false; } private boolean isRunning() { return plugin.getServer().getScheduler().isQueued(taskId); } private boolean isInSession(Player other) { return player.getName().equals(other.getName()); } private void beginReforge() { if (!disableCoolDown) { taskId = plugin .getServer() .getScheduler() .scheduleSyncDelayedTask(plugin, this, (new Random().nextInt(maxReforgeDelay) + minReforgeDelay) * 20L); } else { taskId = plugin .getServer() .getScheduler() .scheduleSyncDelayedTask(plugin, this, 0); } } } }