EpicKnarvik97 3c677c18cb Makes sure to invalidate sessions properly
Sessions are invalidated if the player leaves the NPC or if 10 seconds pass, but only if the blacksmith isn't currently reforging.
2022-10-18 12:44:35 +02:00

238 lines
8.7 KiB
Java

package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.NPCSettings;
import net.knarcraft.blacksmith.formatting.TimeFormatter;
import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
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.formatting.StringFormatter.replacePlaceholder;
import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage;
/**
* The class representing the Blacksmith NPC trait
*/
public class BlacksmithTrait extends Trait {
private final Map<UUID, Calendar> coolDowns = new HashMap<>();
private ReforgeSession session;
private final NPCSettings config;
private long _sessionStart = System.currentTimeMillis();
/**
* Instantiates a new blacksmith trait
*/
public BlacksmithTrait() {
super("blacksmith");
//This should crash if the blacksmith plugin hasn't been properly registered
Bukkit.getServer().getPluginManager().getPlugin("Blacksmith");
this.config = new NPCSettings(BlacksmithPlugin.getInstance().getSettings());
}
/**
* Gets the current settings for this NPC
*
* @return <p>The current settings for this NPC</p>
*/
public NPCSettings getSettings() {
return config;
}
/**
* Gets whether this blacksmith is already in a reforging session
*
* @return <p>Whether already in a reforge session</p>
*/
public boolean hasSession() {
return this.session != null;
}
/**
* 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;
}
/**
* Loads all config values stored in citizens' config file for this NPC
*
* @param key <p>The data key used for the config root</p>
*/
@Override
public void load(DataKey key) {
config.loadVariables(key);
}
/**
* Saves all config values for this NPC
*
* @param key <p>The data key used for the config root</p>
*/
@Override
public void save(DataKey key) {
config.saveVariables(key);
}
/**
* 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>
*/
public boolean prepareForSession(Player player) {
UUID playerId = player.getUniqueId();
//If cool-down has been disabled after it was set for this player, remove the cool-down
if (config.getDisableCoolDown() && coolDowns.get(playerId) != null) {
coolDowns.remove(playerId);
}
//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(playerId) != null) {
Calendar calendar = Calendar.getInstance();
if (!calendar.after(coolDowns.get(playerId))) {
int secondDifference = (int) ((calendar.getTimeInMillis() - coolDowns.get(playerId).getTimeInMillis()) * 1000);
boolean exactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime();
sendNPCMessage(this.npc, player, replacePlaceholder(config.getCoolDownUnexpiredMessage(),
"{time}", TimeFormatter.formatTime(exactTime, secondDifference)));
return false;
}
coolDowns.remove(playerId);
}
//If already in a session, but the player has failed to interact, and left the blacksmith, allow a new session
if (session != null) {
if (!session.isRunning() && (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>
*/
public void continueSession(Player player) {
//Another player is using the blacksmith
if (!session.isInSession(player)) {
sendNPCMessage(this.npc, player, config.getBusyWithPlayerMessage());
return;
}
//The blacksmith is already reforging for the player
if (session.isRunning()) {
int timeRemaining = (int) ((session.getFinishTime() - System.currentTimeMillis()) / 1000);
boolean showExactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime();
sendNPCMessage(this.npc, player, replacePlaceholder(config.getBusyReforgingMessage(), "{time}",
TimeFormatter.formatTime(showExactTime, timeRemaining)));
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>
*/
public void startSession(Player player) {
ItemStack hand = player.getInventory().getItemInMainHand();
//Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item
List<Material> reforgeAbleItems = config.getReforgeAbleItems();
if (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) {
String invalidMessage = replacePlaceholder(config.getInvalidItemMessage(),
"{title}", config.getBlacksmithTitle());
sendNPCMessage(this.npc, player, invalidMessage);
return;
}
if (ItemHelper.getDamage(hand) == 0) {
sendNPCMessage(this.npc, player, config.getNotDamagedMessage());
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 = EconomyManager.formatCost(player);
String itemName = hand.getType().name().toLowerCase().replace('_', ' ');
sendNPCMessage(this.npc, player, config.getCostMessage().replace("{cost}", 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) {
sendNPCMessage(this.npc, player, config.getStartReforgeMessage());
EconomyManager.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 whether the given item is repairable
*
* @param item <p>The item to check</p>
* @return <p>True if the item is repairable</p>
*/
public static boolean isRepairable(ItemStack item) {
return item.getItemMeta() instanceof Damageable && EnchantmentTarget.BREAKABLE.includes(item);
}
}