From df3263bd6ca13a4840e7165453e280a33399859a Mon Sep 17 00:00:00 2001 From: Krakenied Date: Sun, 11 Feb 2024 02:43:57 +0100 Subject: Add ZNPCsPlus support --- bukkit/build.gradle | 4 + .../quests/bukkit/BukkitQuestsPlugin.java | 2 + .../type/dependent/CitizensDeliverTaskType.java | 142 ++------------------ .../tasktype/type/dependent/DeliverTaskType.java | 149 +++++++++++++++++++++ .../type/dependent/ZNPCsPlusDeliverTaskType.java | 45 +++++++ bukkit/src/main/resources/plugin.yml | 23 +++- 6 files changed, 230 insertions(+), 135 deletions(-) create mode 100644 bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/DeliverTaskType.java create mode 100644 bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/ZNPCsPlusDeliverTaskType.java diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 9bdba386..c9c2abe9 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -46,6 +46,8 @@ repositories { maven { url = 'https://nexus.bencodez.com/repository/maven-public/' } // WildStacker maven { url = 'https://repo.bg-software.com/repository/api/' } + // ZNPCsPlus + maven { url = 'https://repo.pyr.lol/snapshots' } // JetBrains Annotations mavenCentral() @@ -116,6 +118,8 @@ dependencies { compileOnly('com.bencodez:votingplugin:6.15') { transitive = false } // WildStacker compileOnly 'com.bgsoftware:WildStackerAPI:2023.3' + // ZNPCsPlus + compileOnly 'lol.pyr:znpcsplus-api:2.0.0-SNAPSHOT' // IridiumSkyblock, PyroFishingPro, uSkyBlock compileOnly fileTree(dir: 'libs', includes: ['*.jar']) diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java index 40a35179..14c2d5a5 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java @@ -109,6 +109,7 @@ import com.leonardobishop.quests.bukkit.tasktype.type.dependent.ShopGUIPlusSellT import com.leonardobishop.quests.bukkit.tasktype.type.dependent.SuperiorSkyblockLevelType; import com.leonardobishop.quests.bukkit.tasktype.type.dependent.SuperiorSkyblockWorthType; import com.leonardobishop.quests.bukkit.tasktype.type.dependent.VotingPluginVoteType; +import com.leonardobishop.quests.bukkit.tasktype.type.dependent.ZNPCsPlusDeliverTaskType; import com.leonardobishop.quests.bukkit.tasktype.type.dependent.uSkyBlockLevelTaskType; import com.leonardobishop.quests.bukkit.util.CompatUtils; import com.leonardobishop.quests.bukkit.util.LogHistory; @@ -448,6 +449,7 @@ public class BukkitQuestsPlugin extends JavaPlugin implements Quests { taskTypeManager.registerTaskType(() -> new uSkyBlockLevelTaskType(this), () -> CompatUtils.isPluginEnabled("uSkyBlock")); taskTypeManager.registerTaskType(() -> new NuVotifierVoteTaskType(this), () -> CompatUtils.isPluginEnabled("Votifier")); // not tested taskTypeManager.registerTaskType(() -> new VotingPluginVoteType(this), () -> CompatUtils.isPluginEnabled("VotingPlugin")); // not tested + taskTypeManager.registerTaskType(() -> new ZNPCsPlusDeliverTaskType(this), () -> CompatUtils.isPluginEnabled("ZNPCsPlus")); // Register task types with enabled specific version plugin compatibility requirement taskTypeManager.registerTaskType(() -> new IridiumSkyblockValueTaskType(this), () -> { // TODO FIX diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/CitizensDeliverTaskType.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/CitizensDeliverTaskType.java index 3cb02a53..c67db9a1 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/CitizensDeliverTaskType.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/CitizensDeliverTaskType.java @@ -1,158 +1,32 @@ package com.leonardobishop.quests.bukkit.tasktype.type.dependent; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.Table; import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; -import com.leonardobishop.quests.bukkit.item.QuestItem; -import com.leonardobishop.quests.bukkit.tasktype.BukkitTaskType; import com.leonardobishop.quests.bukkit.util.TaskUtils; -import com.leonardobishop.quests.bukkit.util.chat.Chat; -import com.leonardobishop.quests.common.config.ConfigProblem; -import com.leonardobishop.quests.common.player.QPlayer; -import com.leonardobishop.quests.common.player.questprogressfile.TaskProgress; -import com.leonardobishop.quests.common.quest.Quest; import com.leonardobishop.quests.common.quest.Task; import net.citizensnpcs.api.event.NPCRightClickEvent; import net.citizensnpcs.api.npc.NPC; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -public final class CitizensDeliverTaskType extends BukkitTaskType { +public final class CitizensDeliverTaskType extends DeliverTaskType { private final BukkitQuestsPlugin plugin; - private final Table fixedQuestItemCache = HashBasedTable.create(); public CitizensDeliverTaskType(BukkitQuestsPlugin plugin) { - super("citizens_deliver", TaskUtils.TASK_ATTRIBUTION_STRING, "Deliver a set of items to a NPC."); + super("citizens_deliver", TaskUtils.TASK_ATTRIBUTION_STRING, "Deliver a set of items to a Citizens NPC."); this.plugin = plugin; - super.addConfigValidator((config, problems) -> { - if (config.containsKey("npc-name") && config.containsKey("npc-id")) { - problems.add(new ConfigProblem(ConfigProblem.ConfigProblemType.WARNING, - "Both npc-name and npc-id is specified; npc-name will be ignored", null, "npc-name")); - } - }); - super.addConfigValidator(TaskUtils.useRequiredConfigValidator(this, "amount")); - super.addConfigValidator(TaskUtils.useIntegerConfigValidator(this, "amount")); - super.addConfigValidator(TaskUtils.useRequiredConfigValidator(this, "item")); - super.addConfigValidator(TaskUtils.useItemStackConfigValidator(this, "item")); - super.addConfigValidator(TaskUtils.useIntegerConfigValidator(this, "data")); - super.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "exact-match")); - super.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "remove-items-when-complete")); - super.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "allow-partial-completion")); - } - - @Override - public void onReady() { - fixedQuestItemCache.clear(); + super.addConfigValidator(TaskUtils.useIntegerConfigValidator(this, "npc-id")); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onNPCRightClick(NPCRightClickEvent event) { - checkInventory(event.getClicker(), event.getNPC(), 1L); + NPC npc = event.getNPC(); + checkInventory(event.getClicker(), npc.getId(), npc.getName(), 1L, plugin); } - @SuppressWarnings("SameParameterValue") - private void checkInventory(Player player, NPC npc, long delay) { - if (player.hasMetadata("NPC") || !player.isOnline()) return; - plugin.getScheduler().runTaskLaterAtLocation(player.getLocation(), () -> checkInventory(player, npc), delay); - } - - @SuppressWarnings("deprecation") - private void checkInventory(Player player, NPC npc) { - if (!player.isOnline()) { - return; - } - - QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); - if (qPlayer == null) { - return; - } - - String npcName = null; - - for (TaskUtils.PendingTask pendingTask : TaskUtils.getApplicableTasks(player, qPlayer, this)) { - Quest quest = pendingTask.quest(); - Task task = pendingTask.task(); - TaskProgress taskProgress = pendingTask.taskProgress(); - - super.debug("Player clicked NPC", quest.getId(), task.getId(), player.getUniqueId()); - - Integer configNPCId = (Integer) task.getConfigValue("npc-id"); - if (configNPCId != null) { - if (npc.getId() != configNPCId) { - super.debug("NPC id " + npc.getId() + " does not match required id, continuing...", quest.getId(), task.getId(), player.getUniqueId()); - continue; - } - } else { - String configNPCName = (String) task.getConfigValue("npc-name"); - if (configNPCName != null) { - if (npcName == null) { - npcName = Chat.legacyStrip(Chat.legacyColor(npc.getName())); - } - - if (!configNPCName.equals(npcName)) { - super.debug("NPC name " + npcName + " does not match required name, continuing...", quest.getId(), task.getId(), player.getUniqueId()); - continue; - } - } - } - - boolean remove = TaskUtils.getConfigBoolean(task, "remove-items-when-complete"); - boolean allowPartial = TaskUtils.getConfigBoolean(task, "allow-partial-completion"); - - QuestItem qi; - if ((qi = fixedQuestItemCache.get(quest.getId(), task.getId())) == null) { - QuestItem fetchedItem = TaskUtils.getConfigQuestItem(task, "item", "data"); - fixedQuestItemCache.put(quest.getId(), task.getId(), fetchedItem); - qi = fetchedItem; - } - - boolean exactMatch = TaskUtils.getConfigBoolean(task, "exact-match", true); - int[] amountPerSlot = TaskUtils.getAmountsPerSlot(player, qi, exactMatch); - super.debug("Player has " + amountPerSlot[36] + " of the required item", quest.getId(), task.getId(), player.getUniqueId()); - - int amount = (int) task.getConfigValue("amount"); - - if (allowPartial) { - int progress = TaskUtils.getIntegerTaskProgress(taskProgress); - int total = Math.min(amountPerSlot[36], amount - progress); - - if (total == 0) { - continue; - } - - // We must ALWAYS remove items if partial completion is allowed - // https://github.com/LMBishop/Quests/issues/375 - TaskUtils.removeItemsInSlots(player, amountPerSlot, total); - super.debug("Removing " + total + " items from inventory", quest.getId(), task.getId(), player.getUniqueId()); - - progress += total; - taskProgress.setProgress(progress); - super.debug("Updating task progress (now " + progress + ")", quest.getId(), task.getId(), player.getUniqueId()); - - if (progress >= amount) { - taskProgress.setCompleted(true); - super.debug("Marking task as complete", quest.getId(), task.getId(), player.getUniqueId()); - } - } else { - int progress = Math.min(amountPerSlot[36], amount); - taskProgress.setProgress(progress); - super.debug("Updating task progress (now " + progress + ")", quest.getId(), task.getId(), player.getUniqueId()); - - if (progress >= amount) { - taskProgress.setCompleted(true); - super.debug("Marking task as complete", quest.getId(), task.getId(), player.getUniqueId()); - - if (remove) { - TaskUtils.removeItemsInSlots(player, amountPerSlot, progress); - super.debug("Removing items from inventory", quest.getId(), task.getId(), player.getUniqueId()); - } - } - } - - TaskUtils.sendTrackAdvancement(player, quest, task, taskProgress, amount); - } + @Override + public Integer getNPCId(Task task) { + return (Integer) task.getConfigValue("npc-id"); } } diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/DeliverTaskType.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/DeliverTaskType.java new file mode 100644 index 00000000..31a7af51 --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/DeliverTaskType.java @@ -0,0 +1,149 @@ +package com.leonardobishop.quests.bukkit.tasktype.type.dependent; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import com.leonardobishop.quests.bukkit.item.QuestItem; +import com.leonardobishop.quests.bukkit.tasktype.BukkitTaskType; +import com.leonardobishop.quests.bukkit.util.TaskUtils; +import com.leonardobishop.quests.bukkit.util.chat.Chat; +import com.leonardobishop.quests.common.config.ConfigProblem; +import com.leonardobishop.quests.common.player.QPlayer; +import com.leonardobishop.quests.common.player.questprogressfile.TaskProgress; +import com.leonardobishop.quests.common.quest.Quest; +import com.leonardobishop.quests.common.quest.Task; +import org.bukkit.entity.Player; + +public abstract class DeliverTaskType extends BukkitTaskType { + + private final Table fixedQuestItemCache = HashBasedTable.create(); + + public DeliverTaskType(String type, String author, String description) { + super(type, author, description); + + super.addConfigValidator((config, problems) -> { + if (config.containsKey("npc-name") && config.containsKey("npc-id")) { + problems.add(new ConfigProblem(ConfigProblem.ConfigProblemType.WARNING, + "Both npc-name and npc-id is specified; npc-name will be ignored", null, "npc-name")); + } + }); + + super.addConfigValidator(TaskUtils.useRequiredConfigValidator(this, "amount")); + super.addConfigValidator(TaskUtils.useIntegerConfigValidator(this, "amount")); + super.addConfigValidator(TaskUtils.useRequiredConfigValidator(this, "item")); + super.addConfigValidator(TaskUtils.useItemStackConfigValidator(this, "item")); + super.addConfigValidator(TaskUtils.useIntegerConfigValidator(this, "data")); + super.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "exact-match")); + super.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "remove-items-when-complete")); + super.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "allow-partial-completion")); + } + + @Override + public void onReady() { + fixedQuestItemCache.clear(); + } + + public abstract T getNPCId(Task task); + + public void checkInventory(Player player, T npcId, String npcName, long delay, BukkitQuestsPlugin plugin) { + if (player.hasMetadata("NPC") || !player.isOnline()) return; + plugin.getScheduler().runTaskLaterAtLocation(player.getLocation(), () -> checkInventory(player, npcId, npcName, plugin), delay); + } + + public void checkInventory(Player player, T npcId, String npcName, BukkitQuestsPlugin plugin) { + if (!player.isOnline()) { + return; + } + + QPlayer qPlayer = plugin.getPlayerManager().getPlayer(player.getUniqueId()); + if (qPlayer == null) { + return; + } + + boolean nameCorrected = false; + + for (TaskUtils.PendingTask pendingTask : TaskUtils.getApplicableTasks(player, qPlayer, this)) { + Quest quest = pendingTask.quest(); + Task task = pendingTask.task(); + TaskProgress taskProgress = pendingTask.taskProgress(); + + super.debug("Player clicked NPC", quest.getId(), task.getId(), player.getUniqueId()); + + T configNPCId = getNPCId(task); + if (configNPCId != null) { + if (!npcId.equals(configNPCId)) { + super.debug("NPC id " + npcId + " does not match required id, continuing...", quest.getId(), task.getId(), player.getUniqueId()); + continue; + } + } else { + String configNPCName = (String) task.getConfigValue("npc-name"); + if (configNPCName != null) { + if (!nameCorrected) { + npcName = Chat.legacyStrip(Chat.legacyColor(npcName)); + nameCorrected = true; + } + + if (!configNPCName.equals(npcName)) { + super.debug("NPC name " + npcName + " does not match required name, continuing...", quest.getId(), task.getId(), player.getUniqueId()); + continue; + } + } + } + + boolean remove = TaskUtils.getConfigBoolean(task, "remove-items-when-complete"); + boolean allowPartial = TaskUtils.getConfigBoolean(task, "allow-partial-completion"); + + QuestItem qi; + if ((qi = fixedQuestItemCache.get(quest.getId(), task.getId())) == null) { + QuestItem fetchedItem = TaskUtils.getConfigQuestItem(task, "item", "data"); + fixedQuestItemCache.put(quest.getId(), task.getId(), fetchedItem); + qi = fetchedItem; + } + + boolean exactMatch = TaskUtils.getConfigBoolean(task, "exact-match", true); + int[] amountPerSlot = TaskUtils.getAmountsPerSlot(player, qi, exactMatch); + super.debug("Player has " + amountPerSlot[36] + " of the required item", quest.getId(), task.getId(), player.getUniqueId()); + + int amount = (int) task.getConfigValue("amount"); + + if (allowPartial) { + int progress = TaskUtils.getIntegerTaskProgress(taskProgress); + int total = Math.min(amountPerSlot[36], amount - progress); + + if (total == 0) { + continue; + } + + // We must ALWAYS remove items if partial completion is allowed + // https://github.com/LMBishop/Quests/issues/375 + TaskUtils.removeItemsInSlots(player, amountPerSlot, total); + super.debug("Removing " + total + " items from inventory", quest.getId(), task.getId(), player.getUniqueId()); + + progress += total; + taskProgress.setProgress(progress); + super.debug("Updating task progress (now " + progress + ")", quest.getId(), task.getId(), player.getUniqueId()); + + if (progress >= amount) { + taskProgress.setCompleted(true); + super.debug("Marking task as complete", quest.getId(), task.getId(), player.getUniqueId()); + } + } else { + int progress = Math.min(amountPerSlot[36], amount); + taskProgress.setProgress(progress); + super.debug("Updating task progress (now " + progress + ")", quest.getId(), task.getId(), player.getUniqueId()); + + if (progress >= amount) { + taskProgress.setCompleted(true); + super.debug("Marking task as complete", quest.getId(), task.getId(), player.getUniqueId()); + + if (remove) { + TaskUtils.removeItemsInSlots(player, amountPerSlot, progress); + super.debug("Removing items from inventory", quest.getId(), task.getId(), player.getUniqueId()); + } + } + } + + TaskUtils.sendTrackAdvancement(player, quest, task, taskProgress, amount); + } + } +} diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/ZNPCsPlusDeliverTaskType.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/ZNPCsPlusDeliverTaskType.java new file mode 100644 index 00000000..efd4bc0c --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/dependent/ZNPCsPlusDeliverTaskType.java @@ -0,0 +1,45 @@ +package com.leonardobishop.quests.bukkit.tasktype.type.dependent; + +import com.leonardobishop.quests.bukkit.BukkitQuestsPlugin; +import com.leonardobishop.quests.bukkit.util.TaskUtils; +import com.leonardobishop.quests.common.quest.Task; +import lol.pyr.znpcsplus.api.event.NpcInteractEvent; +import lol.pyr.znpcsplus.api.hologram.Hologram; +import lol.pyr.znpcsplus.api.interaction.InteractionType; +import lol.pyr.znpcsplus.api.npc.NpcEntry; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; + +public final class ZNPCsPlusDeliverTaskType extends DeliverTaskType { + + private final BukkitQuestsPlugin plugin; + + public ZNPCsPlusDeliverTaskType(BukkitQuestsPlugin plugin) { + super("znpcsplus_deliver", TaskUtils.TASK_ATTRIBUTION_STRING, "Deliver a set of items to a ZNPCsPlus NPC."); + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onNpcInteract(NpcInteractEvent event) { + if (event.getClickType() != InteractionType.RIGHT_CLICK) { + return; + } + + NpcEntry entry = event.getEntry(); + Hologram hologram = event.getNpc().getHologram(); + + StringBuilder nameBuilder = new StringBuilder(); + for (int i = 0; i < hologram.lineCount(); i++) { + String line = hologram.getLine(i); + nameBuilder.append(line).append('\n'); + } + nameBuilder.deleteCharAt(nameBuilder.length() - 1); + + checkInventory(event.getPlayer(), entry.getId(), nameBuilder.toString(), 1L, plugin); + } + + @Override + public String getNPCId(Task task) { + return (String) task.getConfigValue("npc-id"); + } +} diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index b135cef4..fd816a1b 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -6,7 +6,28 @@ version: "${version}" main: com.leonardobishop.quests.bukkit.BukkitQuestsPlugin website: https://github.com/LMBishop/Quests author: "LMBishop & contributors" -softdepend: [ASkyBlock, BentoBox, Citizens, CoreProtect, Essentials, FabledSkyBlock, IridiumSkyblock, MythicMobs, PlaceholderAPI, PlayerBlockTracker, PlayerPoints, ShopGUIPlus, SuperiorSkyblock2, uSkyBlock, Votifier, VotingPlugin, WildStacker] +softdepend: +- ASkyBlock +- BentoBox +- Citizens +- CoreProtect +- EcoBosses +- EcoMobs +- Essentials +- FabledSkyBlock +- IridiumSkyblock +- MythicMobs +- PlaceholderAPI +- PlayerBlockTracker +- PlayerPoints +- PyroFishingPro +- ShopGUIPlus +- SuperiorSkyblock2 +- uSkyBlock +- Votifier +- VotingPlugin +- WildStacker +- ZNPCsPlus prefix: Quests api-version: "1.13" # allows new API features but Quests will still work pre-1.13 folia-supported: true -- cgit v1.2.3-70-g09d2