277 lines
10 KiB
Java
Raw Normal View History

package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.NPCSettings;
import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.ItemHelper;
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 org.bukkit.scheduler.BukkitScheduler;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.logging.Level;
import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage;
2022-09-29 01:49:12 +02:00
2022-07-19 02:38:35 +02:00
/**
* 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 long finishTime = 0;
private final NPCSettings config;
private static final String[] enchantments = new String[Enchantment.values().length];
private static final Random random = new Random();
2022-07-19 02:38:35 +02:00
/**
* 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, NPCSettings config) {
this.blacksmithTrait = blacksmithTrait;
this.player = player;
this.npc = npc;
this.itemToReforge = player.getInventory().getItemInMainHand();
this.config = config;
//Populate enchantments the first time this is run
if (enchantments[0] == null) {
int i = 0;
for (Enchantment enchantment : Enchantment.values()) {
enchantments[i++] = enchantment.getKey().toString();
}
}
}
/**
* Gets the time in milliseconds when this reforging session will finish
*
* @return <p>The time the reforging will finish</p>
*/
public long getFinishTime() {
return this.finishTime;
}
/**
* Runs the actual reforge which fixes the item and gives it back to the player
*/
@Override
public void run() {
2022-09-29 01:49:12 +02:00
sendNPCMessage(this.npc, player, reforgeItem() ? config.getSuccessMessage() : config.getFailMessage());
//Stop the reforged item from displaying in the blacksmith's hand
if (npc.getEntity() instanceof Player) {
((Player) npc.getEntity()).getInventory().setItemInMainHand(null);
} else {
Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(null);
}
//Give the item back to the player
if (!config.getDisableDelay()) {
//If the player isn't online, or the player cannot fit the item, drop the item to prevent it from disappearing
if (config.getDropItem() || !player.isOnline() || player.getInventory().firstEmpty() == -1) {
player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), itemToReforge);
} else {
player.getInventory().addItem(itemToReforge);
}
} else {
//It can be assumed as this happens instantly, that the player still has the item's previous slot selected
player.getInventory().setItemInMainHand(itemToReforge);
}
//Mark this blacksmith as available
blacksmithTrait.unsetSession();
// Start cool-down
Calendar wait = Calendar.getInstance();
wait.add(Calendar.SECOND, config.getReforgeCoolDown());
blacksmithTrait.addCoolDown(player.getUniqueId(), wait);
}
/**
* Performs the actual reforge where the item's damage is reduced
*
* @return <p>Whether the reforge was successful. False if the blacksmith failed to fully repair the item.</p>
*/
private boolean reforgeItem() {
if (random.nextInt(100) < config.getFailChance()) {
failReforge();
return false;
} else {
succeedReforge();
return true;
}
}
/**
* The method to run when a blacksmith successfully reforges an item
*/
private void succeedReforge() {
// Remove any damage done to the item
updateDamage(itemToReforge, 0);
// Add random enchantments
int roll = random.nextInt(100);
if (!(roll < config.getExtraEnchantmentChance() &&
itemToReforge.getEnchantments().keySet().size() < config.getMaxEnchantments())) {
// Abort if randomness isn't on our side, or if max enchantments has been reached
return;
}
//Find usable enchantments first
List<Enchantment> usableEnchantments = new ArrayList<>();
for (String enchantmentName : enchantments) {
Enchantment enchantment = Enchantment.getByKey(NamespacedKey.fromString(enchantmentName));
if (enchantment != null && enchantment.canEnchantItem(itemToReforge)) {
usableEnchantments.add(enchantment);
}
}
//Choose a random enchantment
Enchantment randomEnchantment = usableEnchantments.get(random.nextInt(usableEnchantments.size()));
if (randomEnchantment != null) {
int randomBound = randomEnchantment.getMaxLevel() + 1;
//A workaround for the random method's bound sometimes being negative
if (randomBound >= 0) {
int existingLevel = itemToReforge.getEnchantmentLevel(randomEnchantment);
/* Add a random enchantment whose level is no lower than the start level, and no lower than the
existing level (to prevent making the item worse) */
itemToReforge.addEnchantment(randomEnchantment, Math.max(Math.max(random.nextInt(randomBound),
randomEnchantment.getStartLevel()), existingLevel));
}
}
}
/**
* The method to run when a blacksmith fails re-forging an item
*/
private void failReforge() {
// Remove or downgrade existing enchantments
for (Enchantment enchantment : itemToReforge.getEnchantments().keySet()) {
if (random.nextBoolean()) {
itemToReforge.removeEnchantment(enchantment);
} else {
if (itemToReforge.getEnchantmentLevel(enchantment) > 1) {
itemToReforge.removeEnchantment(enchantment);
itemToReforge.addEnchantment(enchantment, 1);
}
}
}
// Damage the item
short currentItemDurability = ItemHelper.getDurability(itemToReforge);
short newDurability = (short) (currentItemDurability + (currentItemDurability * random.nextInt(8)));
short maxDurability = itemToReforge.getType().getMaxDurability();
if (newDurability <= 0) {
newDurability = (short) (maxDurability / 3);
} else if (currentItemDurability + newDurability > maxDurability) {
newDurability = (short) (maxDurability - random.nextInt(maxDurability - 25));
}
updateDamage(itemToReforge, maxDurability - newDurability);
}
/**
* Updates the damage done to an item
*
* @param item <p>The item to update damage for</p>
* @param newDamage <p>The new damage done</p>
*/
private void updateDamage(ItemStack item, int newDamage) {
ItemMeta meta = item.getItemMeta();
Damageable damageable = (Damageable) meta;
if (damageable != null) {
damageable.setDamage(newDamage);
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Unable to change damage of " + item);
}
item.setItemMeta(meta);
}
/**
* Gets whether to end the current session
*
* <p>If the player has switched their item, or the player cannot pay, this returns true.</p>
*
* @return <p>True if the current session should end</p>
*/
public boolean endSession() {
// Prevent player from switching items during session
ItemStack itemInHand = player.getInventory().getItemInMainHand();
if (!itemToReforge.equals(itemInHand)) {
2022-09-29 01:49:12 +02:00
sendNPCMessage(this.npc, player, config.getItemChangedMessage());
return true;
}
// The player is unable to pay
if (!EconomyManager.canPay(player)) {
2022-09-29 01:49:12 +02:00
sendNPCMessage(this.npc, player, config.getInsufficientFundsMessage());
return true;
}
return false;
}
/**
* Gets whether the current session is still running
*
* @return <p>True if the current session is still running</p>
*/
public boolean isRunning() {
return BlacksmithPlugin.getInstance().getServer().getScheduler().isQueued(taskId);
}
/**
* Gets whether the given player is currently in a reforging session
*
* @param other <p>The player to check if is in session</p>
* @return <p>True if the given player is in a reforge session</p>
*/
public boolean isInSession(Player other) {
return player.getName().equals(other.getName());
}
/**
* Begins the actual item reforging
*/
public void beginReforge() {
BukkitScheduler scheduler = BlacksmithPlugin.getInstance().getServer().getScheduler();
int reforgeDelay;
if (!config.getDisableCoolDown()) {
//Finish the reforging after a random delay between the max and min
reforgeDelay = new Random().nextInt(config.getMaxReforgeDelay()) + config.getMinReforgeDelay();
} else {
//Finish the reforging as soon as possible
reforgeDelay = 0;
}
this.finishTime = System.currentTimeMillis() + (reforgeDelay * 1000L);
taskId = scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), this, reforgeDelay * 20L);
}
/**
* Gets the player currently in this reforge session
*
* @return <p>The player currently in this reforge session</p>
*/
public Player getPlayer() {
return player;
}
2022-07-19 02:38:35 +02:00
}