9 Commits
v1.1.1 ... dev

Author SHA1 Message Date
f5bfbfd4f8 Attenpts to mitigate an NPC's entity becoming unavailable
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-09-27 01:37:07 +02:00
d4feda78ae Changes version back to snapshot
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-08-07 15:08:15 +02:00
81e65810e1 Bumps version for release
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-08-07 14:09:42 +02:00
0dab832bfd Adds a note about sticks being an NPC selector
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-08-07 13:24:06 +02:00
1510960089 Fixes missing cost message for extended salvage
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-08-03 17:05:28 +02:00
cdb0f267a3 Renames an event method
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-08-03 16:44:15 +02:00
81c15b6600 Adds information about the appropriate crafting station in ActionStartEvent
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-08-03 16:32:44 +02:00
5efba6687c Adds a separate message for clicking an NPC with an empty hand
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-08-03 13:09:09 +02:00
d0df4800f0 Changes back to SNAPSHOT
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
2024-07-29 19:35:10 +02:00
25 changed files with 240 additions and 64 deletions

View File

@ -83,6 +83,11 @@ example planks into wood, four wood will be taken.
When an item is salvaged, EXP will be returned based on enchantments on the item.
Note that sticks are valid items to be handled by Scrappers if extended salvage is enabled. The item is also the default
item for selecting NPCs in Citizens. If you find the NPC selection message annoying, and don't normally use sticks for
selection, you can change the item in Citizens' config and replace it with STRUCTURE_VOID or some other unobtainable
item.
## Commands
| Command | Arguments | Description |
@ -230,6 +235,7 @@ All currently supported presets, and available filters for each preset:
| startReforgeMessage | &eOk, let's see what I can do... | The message displayed when a blacksmith starts reforging an item. |
| successMessage | There you go! All better! | The message displayed when a blacksmith successfully repairs an item. |
| notDamagedMessage | &cThat item is not in need of repair | The message displayed if a player tries to reforge an item with full durability. |
| noItemMessage | Please present the item you want to reforge | The message displayed when a blacksmith is clicked with an empty hand |
### Scrapper global-only options
@ -284,6 +290,7 @@ All currently supported presets, and available filters for each preset:
| cannotSalvageArmorTrimMessage | &cI'm sorry, but I'm unable to salvage armor trims! | The message displayed when telling a player that the scrapper cannot salvage items with armor trim. |
| armorTrimSalvageNotFoundMessage | &cI'm sorry, but I don't know how to salvage that armor trim! | The message displayed when telling a player that the scrapper cannot find the correct items to return as armor trim salvage. This will happen if more armor trim materials are added, or the Spigot API is changed. |
| cannotSalvageNetheriteMessage | &cI'm sorry, but I'm unable to salvage netherite items! | The message displayed when telling a player that the scrapper cannot salvage netherite items. |
| noItemMessage | Please present the item you want to salvage | The message displayed when a scrapper is clicked with an empty hand |
## Language customization

View File

@ -6,7 +6,7 @@
<groupId>net.knarcraft</groupId>
<artifactId>blacksmith</artifactId>
<version>1.1.1</version>
<version>1.1.3-SNAPSHOT</version>
<name>Blacksmith</name>
<description>Blacksmith NPC for the Citizens API</description>

View File

@ -175,6 +175,16 @@ public class BlacksmithNPCSettings implements TraitSettings<BlacksmithSetting> {
return asString(BlacksmithSetting.ITEM_UNEXPECTEDLY_CHANGED_MESSAGE);
}
/**
* Gets the message displayed if a player presents the blacksmith with an empty hand
*
* @return <p>The no item message</p>
*/
@NotNull
public String getNoItemMessage() {
return asString(BlacksmithSetting.NO_ITEM_MESSAGE);
}
/**
* Gets all items reforge-able by this NPC
*

View File

@ -186,6 +186,12 @@ public enum BlacksmithSetting implements Setting {
"The message to display if a player is trying to reforge an item with full durability",
true, true),
/**
* The message displayed when clicking a blacksmith with an empty hand
*/
NO_ITEM_MESSAGE("noItemMessage", SettingValueType.STRING, "Please present the item you want to reforge",
"The message to display when a blacksmith is clicked with an empty hand", true, true),
/*------------------
| Global settings |
------------------*/

View File

@ -178,13 +178,25 @@ public class ScrapperNPCSettings implements TraitSettings<ScrapperSetting> {
}
/**
* The message displayed if a player presents a different item after seeing the price to salvage an item
* Gets the message displayed if a player presents a different item after seeing the price to salvage an item
*
* @return <p>The item changed message</p>
*/
@NotNull
public String getItemChangedMessage() {
return asString(ScrapperSetting.ITEM_UNEXPECTEDLY_CHANGED_MESSAGE);
}
/**
* Gets the message displayed if a player presents the scrapper with an empty hand
*
* @return <p>The no item message</p>
*/
@NotNull
public String getNoItemMessage() {
return asString(ScrapperSetting.NO_ITEM_MESSAGE);
}
/**
* Gets the minimum delay used to wait for a salvaging to finish.
*

View File

@ -236,6 +236,12 @@ public enum ScrapperSetting implements Setting {
"&cI'm sorry, but I'm unable to salvage netherite items!",
"The message to display when asked to salvage netherite items, and that option is disabled", true, true),
/**
* The message displayed when clicking a scrapper with an empty hand
*/
NO_ITEM_MESSAGE("noItemMessage", SettingValueType.STRING, "Please present the item you want to salvage",
"The message to display when a scrapper is clicked with an empty hand", true, true),
/*------------------
| Global settings |
------------------*/

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
@ -13,15 +14,18 @@ public abstract class AbstractBlacksmithPluginEvent extends Event implements Bla
protected final NPC npc;
protected final Player player;
protected final Entity entity;
/**
* Instantiates a new blacksmith plugin event
*
* @param npc <p>The NPC involved in the event</p>
* @param entity <p>The entity of the NPC</p>
* @param player <p>The player involved in the event</p>
*/
public AbstractBlacksmithPluginEvent(@NotNull NPC npc, @NotNull Player player) {
public AbstractBlacksmithPluginEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player) {
this.npc = npc;
this.entity = entity;
this.player = player;
}
@ -31,6 +35,12 @@ public abstract class AbstractBlacksmithPluginEvent extends Event implements Bla
return this.npc;
}
@Override
@NotNull
public Entity getEntity() {
return this.entity;
}
@Override
@NotNull
public Player getPlayer() {

View File

@ -1,5 +1,7 @@
package net.knarcraft.blacksmith.event;
import org.bukkit.Material;
/**
* An event triggered when a blacksmith or scrapper starts reforging or salvaging an item
*/
@ -13,4 +15,11 @@ public interface ActionStartEvent extends BlacksmithPluginEvent {
*/
long getActionDurationTicks();
/**
* Gets the appropriate crafting station for this action
*
* @return <p>The appropriate crafting station</p>
*/
Material getCraftingStation();
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@ -18,6 +19,14 @@ public interface BlacksmithPluginEvent {
@NotNull
NPC getNpc();
/**
* Gets the entity of the NPC
*
* @return <p>The NPC entity</p>
*/
@NotNull
Entity getEntity();
/**
* Gets the player involved in the event
*

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
@ -15,10 +16,12 @@ public class BlacksmithReforgeFailEvent extends AbstractBlacksmithPluginEvent im
/**
* Instantiates a new blacksmith reforge fail event
*
* @param npc <p>The NPC involved in the event</p>
* @param npc <p>The NPC involved in the event</p>
* @param entity <p>The entity of the NPC</p>
* @param player <p>The player that initiated the session</p>
*/
public BlacksmithReforgeFailEvent(@NotNull NPC npc, @NotNull Player player) {
super(npc, player);
public BlacksmithReforgeFailEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player) {
super(npc, entity, player);
}
/**

View File

@ -1,6 +1,8 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
@ -12,17 +14,22 @@ public class BlacksmithReforgeStartEvent extends AbstractBlacksmithPluginEvent i
private static final HandlerList handlers = new HandlerList();
private final long durationTicks;
private final Material craftingStation;
/**
* Instantiates a new blacksmith reforge start event
*
* @param npc <p>The NPC involved in the event</p>
* @param player <p>The player involved in the event</p>
* @param durationTicks <p>The duration of the reforge</p>
* @param npc <p>The NPC involved in the event</p>
* @param entity <p>The entity of the NPC</p>
* @param player <p>The player involved in the event</p>
* @param durationTicks <p>The duration of the reforge</p>
* @param craftingStation <p>The appropriate crafting station for this reforging</p>
*/
public BlacksmithReforgeStartEvent(@NotNull NPC npc, @NotNull Player player, long durationTicks) {
super(npc, player);
public BlacksmithReforgeStartEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player, long durationTicks,
@NotNull Material craftingStation) {
super(npc, entity, player);
this.durationTicks = durationTicks;
this.craftingStation = craftingStation;
}
@Override
@ -30,6 +37,11 @@ public class BlacksmithReforgeStartEvent extends AbstractBlacksmithPluginEvent i
return durationTicks;
}
@Override
public Material getCraftingStation() {
return craftingStation;
}
/**
* Gets a handler-list containing all event handlers
*

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
@ -16,10 +17,11 @@ public class BlacksmithReforgeSucceedEvent extends AbstractBlacksmithPluginEvent
* Instantiates a new blacksmith reforge succeed event
*
* @param npc <p>The NPC involved in the event</p>
* @param entity <p>The entity of the NPC</p>
* @param player <p>The player involved in the event</p>
*/
public BlacksmithReforgeSucceedEvent(@NotNull NPC npc, @NotNull Player player) {
super(npc, player);
public BlacksmithReforgeSucceedEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player) {
super(npc, entity, player);
}
/**

View File

@ -3,6 +3,7 @@ package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
@ -25,15 +26,16 @@ public class NPCSoundEvent extends AbstractBlacksmithPluginEvent implements Canc
* Instantiates a new NPC sound event
*
* @param npc <p>The NPC playing the sound</p>
* @param entity <p>The entity playing the sound</p>
* @param player <p>The player whose interaction triggered the sound</p>
* @param soundCategory <p>The category the sound is to play in</p>
* @param sound <p>The sound to play</p>
* @param volume <p>The volume of the played sound</p>
* @param pitch <p>The pitch of the played sound</p>
*/
public NPCSoundEvent(@NotNull NPC npc, @NotNull Player player, @NotNull SoundCategory soundCategory,
public NPCSoundEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player, @NotNull SoundCategory soundCategory,
@NotNull Sound sound, float volume, float pitch) {
super(npc, player);
super(npc, entity, player);
this.soundCategory = soundCategory;
this.sound = sound;
this.volume = volume;

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
@ -16,10 +17,11 @@ public class ScrapperSalvageFailEvent extends AbstractBlacksmithPluginEvent impl
* Instantiates a new scrapper salvage fail event
*
* @param npc <p>The NPC involved in the event</p>
* @param entity <p>The entity of the NPC</p>
* @param player <p>The player involved in the event</p>
*/
public ScrapperSalvageFailEvent(@NotNull NPC npc, @NotNull Player player) {
super(npc, player);
public ScrapperSalvageFailEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player) {
super(npc, entity, player);
}
/**

View File

@ -1,6 +1,8 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
@ -13,17 +15,22 @@ public class ScrapperSalvageStartEvent extends AbstractBlacksmithPluginEvent imp
private static final HandlerList handlers = new HandlerList();
private final long durationTicks;
private final Material craftingStation;
/**
* Instantiates a new scrapper salvage start event
*
* @param npc <p>The NPC involved in the event</p>
* @param player <p>The player involved in the event</p>
* @param durationTicks <p>The duration of the salvage</p>
* @param npc <p>The NPC involved in the event</p>
* @param entity <p>The entity of the NPC</p>
* @param player <p>The player involved in the event</p>
* @param durationTicks <p>The duration of the salvage</p>
* @param craftingStation <p>The appropriate crafting station for this salvaging</p>
*/
public ScrapperSalvageStartEvent(@NotNull NPC npc, @NotNull Player player, long durationTicks) {
super(npc, player);
public ScrapperSalvageStartEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player, long durationTicks,
@NotNull Material craftingStation) {
super(npc, entity, player);
this.durationTicks = durationTicks;
this.craftingStation = craftingStation;
}
@Override
@ -31,6 +38,11 @@ public class ScrapperSalvageStartEvent extends AbstractBlacksmithPluginEvent imp
return this.durationTicks;
}
@Override
public Material getCraftingStation() {
return this.craftingStation;
}
/**
* Gets a handler-list containing all event handlers
*

View File

@ -1,6 +1,7 @@
package net.knarcraft.blacksmith.event;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
@ -16,10 +17,11 @@ public class ScrapperSalvageSucceedEvent extends AbstractBlacksmithPluginEvent i
* Instantiates a new scrapper salvage succeed event
*
* @param npc <p>The NPC involved in the event</p>
* @param entity <p>The entity of the NPC</p>
* @param player <p>The player involved in the event</p>
*/
public ScrapperSalvageSucceedEvent(@NotNull NPC npc, @NotNull Player player) {
super(npc, player);
public ScrapperSalvageSucceedEvent(@NotNull NPC npc, @NotNull Entity entity, @NotNull Player player) {
super(npc, entity, player);
}
/**

View File

@ -92,7 +92,7 @@ public class EconomyManager {
private static double getSalvageCost(@NotNull SalvageMethod salvageMethod) {
GlobalScrapperSettings settings = BlacksmithPlugin.getInstance().getGlobalScrapperSettings();
return switch (salvageMethod) {
case SALVAGE -> settings.getSalvageCost();
case SALVAGE, EXTENDED_SALVAGE -> settings.getSalvageCost();
case NETHERITE -> settings.getNetheriteSalvageCost();
case ARMOR_TRIM -> settings.getArmorTrimSalvageCost();
};

View File

@ -10,6 +10,11 @@ public enum SalvageMethod {
*/
SALVAGE,
/**
* Salvaging unrepairable items normally by returning the item's crafting recipe, but with unrestricted
*/
EXTENDED_SALVAGE,
/**
* Removing the armor trim from an item
*/

View File

@ -68,6 +68,11 @@ public class BlacksmithTrait extends CustomTrait<BlacksmithSetting> {
@Override
public void startSession(@NotNull Player player) {
ItemStack hand = player.getInventory().getItemInMainHand();
if (hand.getType().isAir()) {
sendNPCMessage(this.npc, player, config.getNoItemMessage());
return;
}
//Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item
List<Material> reforgeAbleItems = config.getReforgeAbleItems();

View File

@ -2,14 +2,17 @@ package net.knarcraft.blacksmith.trait;
import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.api.trait.Trait;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.Setting;
import net.knarcraft.blacksmith.config.Settings;
import net.knarcraft.blacksmith.config.TraitSettings;
import net.knarcraft.blacksmith.formatting.TimeFormatter;
import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.knarlib.formatting.StringFormatter;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
@ -18,7 +21,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
@ -153,8 +155,13 @@ public abstract class CustomTrait<K extends Setting> extends Trait {
}
//If already in a session, but the player has failed to interact, and left the blacksmith, allow a new session
Entity entity = npc.getEntity();
if (entity == null) {
BlacksmithPlugin.error("NPC session could not be started, as the NPC does not have a valid entity");
return false;
}
if (session != null && !session.isRunning() && (System.currentTimeMillis() > currentSessionStartTime + 10 * 1000 ||
this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20)) {
entity.getLocation().distance(session.getPlayer().getLocation()) > 20)) {
session = null;
}
return true;
@ -189,10 +196,16 @@ public abstract class CustomTrait<K extends Setting> extends Trait {
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);
Entity entity = npc.getEntity();
if (entity instanceof Player playerNPC) {
playerNPC.getInventory().setItemInMainHand(heldItem);
} else if (entity instanceof LivingEntity) {
EntityEquipment equipment = ((LivingEntity) entity).getEquipment();
if (equipment != null) {
((LivingEntity) entity).getEquipment().setItemInMainHand(heldItem);
} else {
BlacksmithPlugin.warn("Failed to update NPC item, as its equipment was irretrievable.");
}
}
//Remove the item from the player's inventory
if (this.session instanceof SalvageSession salvageSession) {

View File

@ -15,13 +15,13 @@ import org.bukkit.Sound;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
@ -92,6 +92,11 @@ public class ReforgeSession extends Session implements Runnable {
}
}
@Override
protected @NotNull Material getCraftingStation() {
return Material.ANVIL;
}
/**
* Runs the actual reforge which fixes the item and gives it back to the player
*/
@ -102,10 +107,15 @@ public class ReforgeSession extends Session implements Runnable {
playSound(success ? Sound.BLOCK_ANVIL_USE : Sound.ENTITY_VILLAGER_NO);
//Stop the reforged item from displaying in the blacksmith's hand
if (this.npc.getEntity() instanceof Player) {
((Player) this.npc.getEntity()).getInventory().setItemInMainHand(null);
} else {
Objects.requireNonNull(((LivingEntity) this.npc.getEntity()).getEquipment()).setItemInMainHand(null);
if (this.entity instanceof Player playerNPC) {
playerNPC.getInventory().setItemInMainHand(null);
} else if (this.entity instanceof LivingEntity) {
EntityEquipment equipment = ((LivingEntity) this.entity).getEquipment();
if (equipment != null) {
equipment.setItemInMainHand(null);
} else {
BlacksmithPlugin.warn("Failed to clear Blacksmith item, as its equipment was irretrievable.");
}
}
//Give the item back to the player
@ -124,8 +134,7 @@ public class ReforgeSession extends Session implements Runnable {
* Gives the reforged item back to the player
*/
private void giveReforgedItem() {
giveResultingItem(this.config.getMaxReforgeDelay() > 0, this.config.getDropItem(), this.npc,
this.itemToReforge);
giveResultingItem(this.config.getMaxReforgeDelay() > 0, this.config.getDropItem(), this.itemToReforge);
}
/**
@ -136,11 +145,11 @@ public class ReforgeSession extends Session implements Runnable {
private boolean reforgeItem() {
if (random.nextInt(100) < this.config.getFailChance()) {
failReforge();
BlacksmithPlugin.getInstance().callEvent(new BlacksmithReforgeFailEvent(this.npc, this.player));
BlacksmithPlugin.getInstance().callEvent(new BlacksmithReforgeFailEvent(this.npc, this.entity, this.player));
return false;
} else {
succeedReforge();
BlacksmithPlugin.getInstance().callEvent(new BlacksmithReforgeSucceedEvent(this.npc, this.player));
BlacksmithPlugin.getInstance().callEvent(new BlacksmithReforgeSucceedEvent(this.npc, this.entity, this.player));
return true;
}
}

View File

@ -9,16 +9,17 @@ import net.knarcraft.blacksmith.manager.EconomyManager;
import net.knarcraft.blacksmith.property.SalvageMethod;
import net.knarcraft.blacksmith.util.ItemHelper;
import net.knarcraft.blacksmith.util.SalvageHelper;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage;
@ -87,6 +88,15 @@ public class SalvageSession extends Session implements Runnable {
}
}
@Override
protected @NotNull Material getCraftingStation() {
return switch (this.salvageMethod) {
case EXTENDED_SALVAGE -> Material.CRAFTING_TABLE;
case SALVAGE -> Material.ANVIL;
case NETHERITE, ARMOR_TRIM -> Material.SMITHING_TABLE;
};
}
/**
* Runs the actual salvage which fixes the item and gives it back to the player
*/
@ -95,10 +105,15 @@ public class SalvageSession extends Session implements Runnable {
salvageItem();
//Stop the reforged item from displaying in the scrapper's hand
if (this.npc.getEntity() instanceof Player) {
((Player) this.npc.getEntity()).getInventory().setItemInMainHand(null);
} else {
Objects.requireNonNull(((LivingEntity) this.npc.getEntity()).getEquipment()).setItemInMainHand(null);
if (this.entity instanceof Player playerNPC) {
playerNPC.getInventory().setItemInMainHand(null);
} else if (this.entity instanceof LivingEntity) {
EntityEquipment equipment = ((LivingEntity) this.entity).getEquipment();
if (equipment != null) {
equipment.setItemInMainHand(null);
} else {
BlacksmithPlugin.warn("Failed to clear Scrapper item, as its equipment was irretrievable.");
}
}
//Mark this scrapper as available
@ -126,11 +141,11 @@ public class SalvageSession extends Session implements Runnable {
if (random.nextInt(100) < this.config.getFailChance()) {
playSound(Sound.ENTITY_VILLAGER_NO);
failSalvage();
BlacksmithPlugin.getInstance().callEvent(new ScrapperSalvageFailEvent(this.npc, this.player));
BlacksmithPlugin.getInstance().callEvent(new ScrapperSalvageFailEvent(this.npc, this.entity, this.player));
} else {
playSound(Sound.BLOCK_ANVIL_USE);
giveSalvage();
BlacksmithPlugin.getInstance().callEvent(new ScrapperSalvageSucceedEvent(this.npc, this.player));
BlacksmithPlugin.getInstance().callEvent(new ScrapperSalvageSucceedEvent(this.npc, this.entity, this.player));
sendNPCMessage(this.npc, this.player, this.config.getSuccessMessage());
}
}
@ -145,14 +160,13 @@ public class SalvageSession extends Session implements Runnable {
if (ItemHelper.getMaxDurability(this.itemToSalvage) > 0) {
//Damage the item if possible
damageItemRandomly(this.itemToSalvage);
giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), this.npc,
this.itemToSalvage);
giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), this.itemToSalvage);
sendNPCMessage(this.npc, this.player, this.config.getFailSalvageMessage());
} else {
// Give half the salvage
List<ItemStack> halfSalvage = SalvageHelper.pickRandomSalvage(this.salvage, new ArrayList<>(), 0.5);
for (ItemStack item : halfSalvage) {
giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), this.npc, item);
giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), item);
}
sendNPCMessage(this.npc, this.player, this.config.getFailExtendedSalvageMessage());
}
@ -167,7 +181,7 @@ public class SalvageSession extends Session implements Runnable {
this.player.giveExpLevels(this.enchantmentLevels);
BlacksmithPlugin.debug("Giving salvage " + this.salvage);
for (ItemStack item : this.salvage) {
giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), this.npc, item);
giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), item);
}
}

View File

@ -85,6 +85,11 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
*/
public void startSession(@NotNull Player player) {
ItemStack itemInHand = player.getInventory().getItemInMainHand().clone();
if (itemInHand.getType().isAir()) {
sendNPCMessage(this.npc, player, config.getNoItemMessage());
return;
}
List<Material> salvageAbleItems = getSettings().getSalvageAbleItems();
boolean extended = getSettings().extendedSalvageEnabled();
@ -172,12 +177,13 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
// Check if any salvage will be produced
RecipeResult recipeResult = getSalvage(itemInHand, extended);
SalvageMethod method = ItemHelper.isRepairable(itemInHand) ? SalvageMethod.SALVAGE : SalvageMethod.EXTENDED_SALVAGE;
boolean noUsefulSalvage = recipeResult == null || recipeResult.salvage().isEmpty();
if (noUsefulSalvage) {
sendNPCMessage(this.npc, player, getSettings().getTooDamagedMessage());
return new SalvageResult(SalvageMethod.SALVAGE, new ArrayList<>(), SalvageState.NO_SALVAGE, 0);
return new SalvageResult(method, new ArrayList<>(), SalvageState.NO_SALVAGE, 0);
} else {
return new SalvageResult(SalvageMethod.SALVAGE, recipeResult.salvage(), SalvageState.FOUND_SALVAGE,
return new SalvageResult(method, recipeResult.salvage(), SalvageState.FOUND_SALVAGE,
recipeResult.recipe().getResult().getAmount());
}
}
@ -250,7 +256,7 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
replacer.add("{item}", itemInHand.getType().name().toLowerCase().replace('_', ' '));
if (salvageMethod == SalvageMethod.ARMOR_TRIM) {
sendNPCMessage(this.npc, player, replacer.replace(getSettings().getArmorTrimCostMessage()));
} else if (salvageMethod == SalvageMethod.SALVAGE) {
} else if (salvageMethod == SalvageMethod.SALVAGE || salvageMethod == SalvageMethod.EXTENDED_SALVAGE) {
String expectedYield;
if (ItemHelper.getDamage(itemInHand) <= 0) {
expectedYield = getSettings().getFullSalvageMessage();
@ -261,6 +267,8 @@ public class ScrapperTrait extends CustomTrait<ScrapperSetting> {
sendNPCMessage(this.npc, player, replacer.replace(getSettings().getCostMessage()));
} else if (salvageMethod == SalvageMethod.NETHERITE) {
sendNPCMessage(this.npc, player, replacer.replace(getSettings().getNetheriteCostMessage()));
} else {
BlacksmithPlugin.error("Unrecognized salvage method " + salvageMethod);
}
}

View File

@ -6,6 +6,7 @@ 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;
@ -25,6 +26,7 @@ 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;
@ -37,6 +39,10 @@ public abstract class Session implements Runnable {
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.");
}
}
/**
@ -100,10 +106,11 @@ public abstract class Session implements Runnable {
this.finishTime = System.currentTimeMillis() + (actionDelay * 1000L);
long actionDelayTicks = actionDelay * 20L;
BlacksmithPlugin instance = BlacksmithPlugin.getInstance();
if (this instanceof ReforgeSession) {
BlacksmithPlugin.getInstance().callEvent(new BlacksmithReforgeStartEvent(npc, player, actionDelayTicks));
instance.callEvent(new BlacksmithReforgeStartEvent(this.npc, this.entity, this.player, actionDelayTicks, getCraftingStation()));
} else if (this instanceof SalvageSession) {
BlacksmithPlugin.getInstance().callEvent(new ScrapperSalvageStartEvent(npc, player, actionDelayTicks));
instance.callEvent(new ScrapperSalvageStartEvent(this.npc, this.entity, this.player, actionDelayTicks, getCraftingStation()));
}
taskId = scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), this, actionDelayTicks);
@ -114,14 +121,13 @@ public abstract class Session implements Runnable {
*
* @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 npc <p>The NPC holding the item</p>
* @param itemToReturn <p>The item to return to the player</p>
*/
protected void giveResultingItem(boolean hasDelay, boolean dropItem, @NotNull NPC npc, @NotNull ItemStack itemToReturn) {
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(npc.getEntity().getLocation(), itemToReturn);
this.player.getWorld().dropItemNaturally(this.entity.getLocation(), itemToReturn);
} else {
this.player.getInventory().addItem(itemToReturn);
}
@ -157,13 +163,20 @@ public abstract class Session implements Runnable {
*/
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.npc.getEntity(), sound);
playSound(this.entity, sound);
}
/**
@ -178,14 +191,13 @@ public abstract class Session implements Runnable {
return;
}
NPCSoundEvent event = new NPCSoundEvent(this.npc, this.player, SoundCategory.AMBIENT, sound, 0.5f, 1.0f);
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.getNpc().getEntity(), event.getSound(), event.getSoundCategory(), event.getVolume(),
event.getPitch());
world.playSound(event.getEntity(), event.getSound(), event.getSoundCategory(), event.getVolume(), event.getPitch());
}
}

View File

@ -111,6 +111,9 @@ blacksmith:
# The message to display if a player is trying to reforge an item with full durability
notDamagedMessage: "&cThat item is not in need of repair"
# The message to display when a blacksmith is clicked with an empty hand
noItemMessage: "Please present the item you want to reforge"
# Settings for the scrapper trait
scrapper:
@ -239,4 +242,7 @@ scrapper:
armorTrimSalvageNotFoundMessage: "&cI'm sorry, but I don't know how to salvage that armor trim!"
# The message to display when asked to salvage netherite items, and that option is disabled
cannotSalvageNetheriteMessage: "&cI'm sorry, but I'm unable to salvage netherite items!"
cannotSalvageNetheriteMessage: "&cI'm sorry, but I'm unable to salvage netherite items!"
# The message to display when a scrapper is clicked with an empty hand
noItemMessage: "Please present the item you want to salvage"