EpicKnarvik97 f5bfbfd4f8
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
Attenpts to mitigate an NPC's entity becoming unavailable
2024-09-27 01:37:07 +02:00

204 lines
7.3 KiB
Java

package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.event.BlacksmithReforgeStartEvent;
import net.knarcraft.blacksmith.event.NPCSoundEvent;
import net.knarcraft.blacksmith.event.ScrapperSalvageStartEvent;
import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Material;
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.inventory.ItemStack;
import org.bukkit.scheduler.BukkitScheduler;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
/**
* A runnable session for performing a reforging/salvage task
*/
public abstract class Session implements Runnable {
protected static final Random random = new Random();
protected final Player player;
protected final NPC npc;
protected final Entity entity;
protected long finishTime;
protected int taskId;
/**
* Instantiates a new session
*
* @param player <p>The player the session belongs to</p>
* @param npc <p>The NPC involved in the session</p>
*/
public Session(@NotNull Player player, @NotNull NPC npc) {
this.player = player;
this.npc = npc;
this.entity = npc.getEntity();
if (entity == null) {
throw new IllegalArgumentException("Tried to start session for an NPC without an assigned entity.");
}
}
/**
* 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>False if the given player is in a reforge session</p>
*/
public boolean isNotInSession(@NotNull Player other) {
return !player.getUniqueId().equals(other.getUniqueId());
}
/**
* Gets the player currently in this reforge session
*
* @return <p>The player currently in this reforge session</p>
*/
public @NotNull Player getPlayer() {
return player;
}
/**
* Gets whether the current session is invalid, and should be ended
*
* <p>If the player has switched their item, the player cannot pay, or some other condition fails,
* this returns true.</p>
*
* @return <p>True if the current session should end</p>
*/
public abstract boolean isSessionInvalid();
/**
* 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 the time in milliseconds when this session's action will finish
*
* <p>This returns 0 if the action hasn't been run yet.</p>
*
* @return <p>The timestamp for when the action will finish</p>
*/
public long getFinishTime() {
return this.finishTime;
}
/**
* Schedules this session's main task for after the action delay has passed
*/
public void scheduleAction() {
if (isRunning()) {
throw new IllegalStateException("Session action tried to run twice!");
}
BukkitScheduler scheduler = BlacksmithPlugin.getInstance().getServer().getScheduler();
int actionDelay = getActionDelay();
this.finishTime = System.currentTimeMillis() + (actionDelay * 1000L);
long actionDelayTicks = actionDelay * 20L;
BlacksmithPlugin instance = BlacksmithPlugin.getInstance();
if (this instanceof ReforgeSession) {
instance.callEvent(new BlacksmithReforgeStartEvent(this.npc, this.entity, this.player, actionDelayTicks, getCraftingStation()));
} else if (this instanceof SalvageSession) {
instance.callEvent(new ScrapperSalvageStartEvent(this.npc, this.entity, this.player, actionDelayTicks, getCraftingStation()));
}
taskId = scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), this, actionDelayTicks);
}
/**
* Gives the resulting item to the player
*
* @param hasDelay <p>Whether the session was delayed, or if the item was processed instantly</p>
* @param dropItem <p>Whether the item should be dropped on the ground</p>
* @param itemToReturn <p>The item to return to the player</p>
*/
protected void giveResultingItem(boolean hasDelay, boolean dropItem, @NotNull ItemStack itemToReturn) {
if (hasDelay) {
//If the player isn't online, or the player cannot fit the item, drop the item to prevent it from disappearing
if (dropItem || !this.player.isOnline() || !ItemHelper.canFitItem(this.player.getInventory(), itemToReturn)) {
this.player.getWorld().dropItemNaturally(this.entity.getLocation(), itemToReturn);
} else {
this.player.getInventory().addItem(itemToReturn);
}
} else {
//It can be assumed as this happens instantly, that the player still has the item's previous slot selected
this.player.getInventory().setItemInMainHand(itemToReturn);
}
}
/**
* Randomly damages the given item
*
* @param item <p>The item to damage</p>
*/
protected void damageItemRandomly(@NotNull ItemStack item) {
short currentItemDurability = ItemHelper.getDurability(item);
short newDurability = (short) (currentItemDurability + (currentItemDurability * random.nextInt(8)));
short maxDurability = item.getType().getMaxDurability();
if (newDurability <= 0) {
newDurability = (short) (maxDurability / 3);
} else if (currentItemDurability + newDurability > maxDurability) {
newDurability = (short) (maxDurability - random.nextInt(maxDurability - 25));
}
if (ItemHelper.updateDamage(item, maxDurability - newDurability)) {
BlacksmithPlugin.warn("Unable to update damage for " + item);
}
}
/**
* Gets the delay for this session's action to finish
*
* @return <p>The delay to wait for this session's action to finish</p>
*/
protected abstract int getActionDelay();
/**
* Gets the appropriate crafting station for this session
*
* @return <p>The appropriate crafting station</p>
*/
protected abstract @NotNull Material getCraftingStation();
/**
* Plays an NPC sound
*
* @param sound <p>The sound to play</p>
*/
protected void playSound(Sound sound) {
playSound(this.entity, sound);
}
/**
* Plays a npc sound using a cancellable event
*
* @param entity <p>The entity that should play the sound</p>
* @param sound <p>The sound to play</p>
*/
private void playSound(@NotNull Entity entity, @NotNull Sound sound) {
World world = entity.getLocation().getWorld();
if (world == null) {
return;
}
NPCSoundEvent event = new NPCSoundEvent(this.npc, this.entity, this.player, SoundCategory.AMBIENT, sound, 0.5f, 1.0f);
BlacksmithPlugin.getInstance().callEvent(event);
if (event.isCancelled()) {
return;
}
world.playSound(event.getEntity(), event.getSound(), event.getSoundCategory(), event.getVolume(), event.getPitch());
}
}