From 0aabd9991bc3d6c9ef1b1505c0e498c386ea7143 Mon Sep 17 00:00:00 2001 From: Krakenied Date: Tue, 10 Sep 2024 23:09:30 +0200 Subject: [PATCH] Add bartering task type Compatible with Spigot 1.16.5+ --- .../quests/bukkit/BukkitQuestsPlugin.java | 2 + .../tasktype/type/BarteringTaskType.java | 167 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/BarteringTaskType.java 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 12916b66..7de0c1bd 100644 --- a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/BukkitQuestsPlugin.java @@ -62,6 +62,7 @@ import com.leonardobishop.quests.bukkit.storage.MySqlStorageProvider; import com.leonardobishop.quests.bukkit.storage.YamlStorageProvider; import com.leonardobishop.quests.bukkit.tasktype.BukkitTaskTypeManager; +import com.leonardobishop.quests.bukkit.tasktype.type.BarteringTaskType; import com.leonardobishop.quests.bukkit.tasktype.type.BlockItemdroppingTaskType; import com.leonardobishop.quests.bukkit.tasktype.type.BlockshearingTaskType; import com.leonardobishop.quests.bukkit.tasktype.type.BreedingTaskType; @@ -459,6 +460,7 @@ public void onEnable() { taskTypeManager.registerTaskType(new WalkingTaskType(this)); // Register task types with class/method compatibility requirement + taskTypeManager.registerTaskType(() -> new BarteringTaskType(this), () -> CompatUtils.classExists("org.bukkit.event.entity.PiglinBarterEvent")); taskTypeManager.registerTaskType(() -> new BlockItemdroppingTaskType(this), () -> CompatUtils.classExists("org.bukkit.event.block.BlockDropItemEvent")); taskTypeManager.registerTaskType(() -> new BlockshearingTaskType(this), () -> CompatUtils.classExists("io.papermc.paper.event.block.PlayerShearBlockEvent")); taskTypeManager.registerTaskType(() -> new BrewingTaskType(this), () -> CompatUtils.classWithMethodExists("org.bukkit.event.inventory.BrewEvent", "getResults")); diff --git a/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/BarteringTaskType.java b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/BarteringTaskType.java new file mode 100644 index 00000000..b2d4c88c --- /dev/null +++ b/bukkit/src/main/java/com/leonardobishop/quests/bukkit/tasktype/type/BarteringTaskType.java @@ -0,0 +1,167 @@ +package com.leonardobishop.quests.bukkit.tasktype.type; + +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.constraint.TaskConstraintSet; +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.LivingEntity; +import org.bukkit.entity.Piglin; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.entity.PiglinBarterEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; + +public final class BarteringTaskType extends BukkitTaskType { + + private final BukkitQuestsPlugin plugin; + private final Map piglin2ThrowerIdMap = new WeakHashMap<>(); + private final Table fixedQuestInputCache = HashBasedTable.create(); + private final Table fixedQuestOutputCache = HashBasedTable.create(); + + public BarteringTaskType(final @NotNull BukkitQuestsPlugin plugin) { + super("bartering", TaskUtils.TASK_ATTRIBUTION_STRING, "Make a bartering interaction with a piglin."); + this.plugin = plugin; + + this.addConfigValidator(TaskUtils.useRequiredConfigValidator(this, "amount")); + this.addConfigValidator(TaskUtils.useIntegerConfigValidator(this, "amount")); + this.addConfigValidator(TaskUtils.useItemStackConfigValidator(this, "input")); + this.addConfigValidator(TaskUtils.useItemStackConfigValidator(this, "output")); + this.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "input-exact-match")); + this.addConfigValidator(TaskUtils.useBooleanConfigValidator(this, "output-exact-match")); + this.addConfigValidator(TaskUtils.useAcceptedValuesConfigValidator(this, Mode.STRING_MODE_MAP.keySet(), "mode")); + } + + @Override + public void onReady() { + this.fixedQuestInputCache.clear(); + this.fixedQuestOutputCache.clear(); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onEntityPickupItem(final @NotNull EntityPickupItemEvent event) { + final LivingEntity entity = event.getEntity(); + + if (entity instanceof final Piglin piglin) { + final UUID throwerId = event.getItem().getOwner(); + this.piglin2ThrowerIdMap.put(piglin, throwerId); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onPiglinBarter(final @NotNull PiglinBarterEvent event) { + final Piglin piglin = event.getEntity(); + + final UUID throwerId = this.piglin2ThrowerIdMap.get(piglin); + if (throwerId == null) { + return; + } + + final Player player = this.plugin.getServer().getPlayer(throwerId); + if (player == null) { + return; + } + + final QPlayer qPlayer = this.plugin.getPlayerManager().getPlayer(throwerId); + if (qPlayer == null) { + return; + } + + final List outcome = event.getOutcome(); + for (final ItemStack output : outcome) { + this.handle(player, qPlayer, event.getInput(), output); + } + } + + private void handle(final @NotNull Player player, final @NotNull QPlayer qPlayer, final @NotNull ItemStack input, final @NotNull ItemStack output) { + final int inputAmount = input.getAmount(); + final int outputAmount = output.getAmount(); + + for (final TaskUtils.PendingTask pendingTask : TaskUtils.getApplicableTasks(player, qPlayer, this, TaskConstraintSet.ALL)) { + final Quest quest = pendingTask.quest(); + final Task task = pendingTask.task(); + final TaskProgress taskProgress = pendingTask.taskProgress(); + + this.debug("Player completed a bartering interaction from " + inputAmount + " x " + input.getType() + " to " + outputAmount + " x " + output.getType(), quest.getId(), task.getId(), player.getUniqueId()); + + if (task.hasConfigKey("input")) { + QuestItem qi; + if ((qi = this.fixedQuestInputCache.get(quest.getId(), task.getId())) == null) { + final QuestItem fetchedItem = TaskUtils.getConfigQuestItem(task, "input", "data"); + this.fixedQuestInputCache.put(quest.getId(), task.getId(), fetchedItem); + qi = fetchedItem; + } + + final boolean exactMatch = TaskUtils.getConfigBoolean(task, "input-exact-match", true); + if (!qi.compareItemStack(input, exactMatch)) { + this.debug("Input does not match, continuing...", quest.getId(), task.getId(), player.getUniqueId()); + continue; + } + } + + if (task.hasConfigKey("output")) { + QuestItem qi; + if ((qi = this.fixedQuestOutputCache.get(quest.getId(), task.getId())) == null) { + final QuestItem fetchedItem = TaskUtils.getConfigQuestItem(task, "output", "data"); + this.fixedQuestOutputCache.put(quest.getId(), task.getId(), fetchedItem); + qi = fetchedItem; + } + + final boolean exactMatch = TaskUtils.getConfigBoolean(task, "output-exact-match", true); + if (!qi.compareItemStack(output, exactMatch)) { + this.debug("Output does not match, continuing...", quest.getId(), task.getId(), player.getUniqueId()); + continue; + } + } + + final Object modeObject = task.getConfigValue("mode"); + + // not suspicious at all ඞ + //noinspection SuspiciousMethodCalls + final Mode requiredMode = Mode.STRING_MODE_MAP.getOrDefault(modeObject, Mode.OUTPUT); + + final int itemAmount = switch (requiredMode) { + case INPUT -> inputAmount; + case OUTPUT -> outputAmount; + }; + + final int progress = TaskUtils.incrementIntegerTaskProgress(taskProgress, itemAmount); + this.debug("Incrementing task progress (now " + progress + ")", quest.getId(), task.getId(), player.getUniqueId()); + + final int amount = (int) task.getConfigValue("amount"); + if (progress >= amount) { + this.debug("Marking task as complete", quest.getId(), task.getId(), player.getUniqueId()); + taskProgress.setCompleted(true); + } + + TaskUtils.sendTrackAdvancement(player, quest, task, pendingTask, amount); + } + } + + private enum Mode { + INPUT, + OUTPUT; + + private static final Map STRING_MODE_MAP = new HashMap<>() {{ + for (final BarteringTaskType.Mode mode : BarteringTaskType.Mode.values()) { + this.put(mode.name().toLowerCase(Locale.ROOT), mode); + } + }}; + } +}