/* * Copyright (c) PikaMug and contributors * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package me.pikamug.quests.player; import com.alessiodp.parties.api.interfaces.Party; import com.alessiodp.parties.api.interfaces.PartyPlayer; import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.util.player.UserManager; import io.github.znetworkw.znpcservers.npc.NPC; import lol.pyr.znpcsplus.api.npc.Npc; import me.clip.placeholderapi.PlaceholderAPI; import me.pikamug.localelib.LocaleManager; import me.pikamug.quests.BukkitQuestsPlugin; import me.pikamug.quests.conditions.BukkitCondition; import me.pikamug.quests.config.BukkitConfigSettings; import me.pikamug.quests.config.ConfigSettings; import me.pikamug.quests.convo.misc.QuestAbandonPrompt; import me.pikamug.quests.dependencies.BukkitDependencies; import me.pikamug.quests.entity.BukkitCountableMob; import me.pikamug.quests.enums.ObjectiveType; import me.pikamug.quests.events.quest.QuestQuitEvent; import me.pikamug.quests.events.quest.QuestTakeEvent; import me.pikamug.quests.events.quester.BukkitQuesterPostStartQuestEvent; import me.pikamug.quests.events.quester.BukkitQuesterPostUpdateObjectiveEvent; import me.pikamug.quests.events.quester.BukkitQuesterPreOpenGUIEvent; import me.pikamug.quests.events.quester.BukkitQuesterPreStartQuestEvent; import me.pikamug.quests.events.quester.BukkitQuesterPreUpdateObjectiveEvent; import me.pikamug.quests.item.BukkitQuestJournal; import me.pikamug.quests.module.CustomObjective; import me.pikamug.quests.module.CustomRequirement; import me.pikamug.quests.nms.BukkitActionBarProvider; import me.pikamug.quests.nms.BukkitTitleProvider; import me.pikamug.quests.quests.components.BukkitObjective; import me.pikamug.quests.quests.BukkitQuest; import me.pikamug.quests.quests.components.BukkitRequirements; import me.pikamug.quests.quests.components.BukkitStage; import me.pikamug.quests.quests.components.Objective; import me.pikamug.quests.quests.components.Planner; import me.pikamug.quests.quests.Quest; import me.pikamug.quests.quests.components.Stage; import me.pikamug.quests.tasks.BukkitStageTimer; import me.pikamug.quests.util.BukkitConfigUtil; import me.pikamug.quests.util.BukkitInventoryUtil; import me.pikamug.quests.util.BukkitItemUtil; import me.pikamug.quests.util.BukkitMiscUtil; import me.pikamug.quests.util.BukkitLang; import me.pikamug.quests.util.RomanNumeral; import me.pikamug.unite.api.objects.PartyProvider; import net.citizensnpcs.api.CitizensAPI; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.DyeColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.conversations.ConversationFactory; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.EnchantmentStorageMeta; import org.bukkit.material.Crops; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListSet; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; public class BukkitQuester implements Quester { private final BukkitQuestsPlugin plugin; private UUID id; protected String questIdToTake; protected String questIdToQuit; private String lastKnownName; protected int questPoints = 0; private String compassTargetQuestId; private long lastNotifiedCondition = 0L; protected ConcurrentHashMap timers = new ConcurrentHashMap<>(); protected ConcurrentHashMap currentQuests = new ConcurrentHashMap() { private static final long serialVersionUID = 6361484975823846780L; @Override public Integer put(final @NotNull Quest key, final @NotNull Integer val) { final Integer data = super.put(key, val); updateJournal(); return data; } @Override public Integer remove(final @NotNull Object key) { final Integer i = super.remove(key); updateJournal(); return i; } @Override public void clear() { super.clear(); updateJournal(); } @Override public void putAll(final Map m) { super.putAll(m); updateJournal(); } }; protected ConcurrentSkipListSet completedQuests = new ConcurrentSkipListSet() { private static final long serialVersionUID = -269110128568487000L; @Override public boolean add(final Quest e) { final boolean b = super.add(e); updateJournal(); return b; } @Override public boolean addAll(final @NotNull Collection c) { final boolean b = super.addAll(c); updateJournal(); return b; } @Override public void clear() { super.clear(); updateJournal(); } @Override public boolean remove(final Object o) { final boolean b = super.remove(o); updateJournal(); return b; } @Override public boolean removeAll(final Collection c) { final boolean b = super.removeAll(c); updateJournal(); return b; } }; protected ConcurrentHashMap completedTimes = new ConcurrentHashMap<>(); protected ConcurrentHashMap amountsCompleted = new ConcurrentHashMap() { private static final long serialVersionUID = 5475202358792520975L; @Override public Integer put(final @NotNull Quest key, final @NotNull Integer val) { final Integer data = super.put(key, val); updateJournal(); return data; } @Override public Integer remove(final @NotNull Object key) { final Integer i = super.remove(key); updateJournal(); return i; } @Override public void clear() { super.clear(); updateJournal(); } @Override public void putAll(final Map m) { super.putAll(m); updateJournal(); } }; protected ConcurrentHashMap questProgress = new ConcurrentHashMap() { private static final long serialVersionUID = -4607112433003926066L; @Override public BukkitQuestProgress put(final @NotNull Quest key, final @NotNull BukkitQuestProgress val) { final BukkitQuestProgress data = super.put(key, val); updateJournal(); return data; } @Override public BukkitQuestProgress remove(final @NotNull Object key) { final BukkitQuestProgress data = super.remove(key); updateJournal(); return data; } @Override public void clear() { super.clear(); updateJournal(); } @Override public void putAll(final Map m) { super.putAll(m); updateJournal(); } }; public BukkitQuester(final BukkitQuestsPlugin plugin, final UUID uuid) { this.plugin = plugin; this.id = uuid; if (getPlayer() != null) { this.lastKnownName = getPlayer().getName(); } else { this.lastKnownName = getOfflinePlayer().getName(); } } @Override public int compareTo(final Quester quester) { return id.compareTo(quester.getUUID()); } @Override public UUID getUUID() { return id; } @Override public void setUUID(final UUID id) { this.id = id; } @Override public String getQuestIdToTake() { return questIdToTake; } @Override public void setQuestIdToTake(final String questIdToTake) { this.questIdToTake = questIdToTake; } @Override public String getQuestIdToQuit() { return questIdToQuit; } @Override public void setQuestIdToQuit(final String questIdToQuit) { this.questIdToQuit = questIdToQuit; } @Override public String getLastKnownName() { return lastKnownName; } @Override public void setLastKnownName(final String lastKnownName) { this.lastKnownName = lastKnownName; } @Override public int getQuestPoints() { return questPoints; } @Override public void setQuestPoints(final int questPoints) { this.questPoints = questPoints; } /** * Get compass target quest. Returns null if not set * * @return Quest or null */ @Override public Quest getCompassTarget() { return compassTargetQuestId != null ? plugin.getQuestById(compassTargetQuestId) : null; } /** * Set compass target quest. Does not update in-game * * @param quest The target quest */ @Override public void setCompassTarget(final Quest quest) { if (quest != null) { compassTargetQuestId = quest.getId(); } } @Override public ConcurrentHashMap getTimers() { return timers; } @Override public void setTimers(final ConcurrentHashMap timers) { this.timers = timers; } @Override public void removeTimer(final Integer timerId) { this.timers.remove(timerId); } @Override public ConcurrentHashMap getCurrentQuests() { final ConcurrentHashMap map = new ConcurrentHashMap<>(); for (final Entry cq : currentQuests.entrySet()) { final BukkitQuest q = (BukkitQuest) cq.getKey(); map.put(q, cq.getValue()); } return map; } @Override public void setCurrentQuests(final ConcurrentHashMap currentQuests) { this.currentQuests = currentQuests; } public ConcurrentSkipListSet getCompletedQuests() { final ConcurrentSkipListSet set = new ConcurrentSkipListSet<>(); for (final Quest iq : completedQuests) { final BukkitQuest q = (BukkitQuest) iq; set.add(q); } return set; } @Override public void setCompletedQuests(final ConcurrentSkipListSet completedQuests) { this.completedQuests = completedQuests; } @Override public ConcurrentHashMap getCompletedTimes() { return completedTimes; } @Override public void setCompletedTimes(final ConcurrentHashMap completedTimes) { this.completedTimes = completedTimes; } @Override public ConcurrentHashMap getAmountsCompleted() { return amountsCompleted; } @Override public void setAmountsCompleted(final ConcurrentHashMap amountsCompleted) { this.amountsCompleted = amountsCompleted; } public ConcurrentHashMap getQuestProgress() { return questProgress; } public void setQuestProgress(final ConcurrentHashMap questProgress) { this.questProgress = questProgress; } @Override public Player getPlayer() { return plugin.getServer().getPlayer(id); } @Override public OfflinePlayer getOfflinePlayer() { return plugin.getServer().getOfflinePlayer(id); } @Override public void sendMessage(final String message) { if (getPlayer() == null || !getPlayer().isOnline() || message.trim().isEmpty()) { return; } getPlayer().sendMessage(message); } @Override public Stage getCurrentStage(final Quest quest) { if (currentQuests.containsKey(quest)) { return quest.getStage(currentQuests.get(quest)); } return null; } /** * Get quest progress for given quest, or default values if not found * * @param quest The quest to check * @return Existing or current progress, or default */ @Override public QuestProgress getQuestDataOrDefault(final Quest quest) { if (questProgress.get(quest) != null) { return questProgress.get(quest); } if (currentQuests.get(quest) != null) { addEmptiesFor(quest, currentQuests.get(quest)); } return new BukkitQuestProgress(this); } @Override public boolean hasJournal() { return getJournal() != null; } @Override public ItemStack getJournal() { if (getPlayer() == null || !getPlayer().isOnline()) { return null; } for (final ItemStack is : getPlayer().getInventory().getContents()) { if (BukkitItemUtil.isJournal(is)) { return is; } } return null; } @Override public int getJournalIndex() { if (getPlayer() == null || !getPlayer().isOnline()) { return -1; } final ItemStack[] arr = getPlayer().getInventory().getContents(); for (int i = 0; i < arr.length; i++) { if (arr[i] != null) { if (BukkitItemUtil.isJournal(arr[i])) { return i; } } } return -1; } @Override public void updateJournal() { if (getPlayer() == null) { return; } if (!getPlayer().isOnline()) { plugin.getLogger().info("Could not update Quests Journal for " + getPlayer().getName() + " while offline"); return; } final int index = getJournalIndex(); if (index != -1) { final BukkitQuestJournal journal = new BukkitQuestJournal(plugin, this); getPlayer().getInventory().setItem(index, journal.toItemStack()); } } /** * Check if quest is available and, if so, ask Quester if they would like to start it

* * @param quest The quest to check and then offer * @param giveReason Whether to inform Quester of unavailability * @return true if successful */ public boolean offerQuest(final Quest quest, final boolean giveReason) { if (quest == null) { return false; } final QuestTakeEvent event = new QuestTakeEvent(quest, this); plugin.getServer().getPluginManager().callEvent(event); if (event.isCancelled()) { return false; } if (canAcceptOffer(quest, giveReason)) { if (getPlayer() != null) { if (!getPlayer().isConversing()) { setQuestIdToTake(quest.getId()); final String s = ChatColor.GOLD + BukkitConfigUtil.parseString(BukkitLang.get("questObjectivesTitle") + "\n" + ChatColor.RESET + quest.getDescription(), quest, getPlayer()); for (final String msg : s.split("
")) { sendMessage(msg); } if (!plugin.getConfigSettings().canConfirmAccept()) { takeQuest(quest, false); } else { plugin.getConversationFactory().buildConversation(getPlayer()).begin(); } return true; } else { sendMessage(ChatColor.YELLOW + BukkitLang.get(getPlayer(), "alreadyConversing")); } } } return false; } /** * Check if quest is available to this Quester

* * @param quest The quest to check * @param giveReason Whether to inform Quester of unavailability * @return true if available */ public boolean canAcceptOffer(final Quest quest, final boolean giveReason) { if (quest == null) { return false; } final BukkitQuest bukkitQuest = (BukkitQuest) quest; if (!plugin.getConfigSettings().canAllowCommandsForNpcQuests() && bukkitQuest.getNpcStart() != null && getPlayer().getLocation().getWorld() != null) { final UUID uuid = bukkitQuest.getNpcStart(); Entity npc = null; if (plugin.getDependencies().getCitizens() != null && plugin.getDependencies().getCitizens().getNPCRegistry().getByUniqueId(uuid) != null) { npc = plugin.getDependencies().getCitizens().getNPCRegistry().getByUniqueId(uuid).getEntity(); } else if (plugin.getDependencies().getZnpcsPlus() != null && plugin.getDependencies().getZnpcsPlusUuids().contains(uuid)) { final Optional opt = NPC.all().stream().filter(npc1 -> npc1.getUUID().equals(uuid)).findAny(); if (opt.isPresent()) { npc = (Entity) opt.get().getBukkitEntity(); } } if (npc != null && npc.getLocation().getWorld() != null && npc.getLocation().getWorld().getName() .equals(getPlayer().getLocation().getWorld().getName()) && npc.getLocation().distance(getPlayer().getLocation()) > 6.0) { if (giveReason) { final String msg = BukkitLang.get(getPlayer(), "mustSpeakTo") .replace("", ChatColor.DARK_PURPLE + npc.getName() + ChatColor.YELLOW); sendMessage(ChatColor.YELLOW + msg); } return false; } if (plugin.getDependencies().getZnpcsPlusApi() != null && plugin.getDependencies().getZnpcsPlusApi().getNpcRegistry().getByUuid(uuid) != null) { Npc znpc = plugin.getDependencies().getZnpcsPlusApi().getNpcRegistry().getByUuid(uuid).getNpc(); if (znpc.getWorld() != null && znpc.getWorld().equals(getPlayer().getWorld()) && znpc.getLocation().toBukkitLocation(znpc.getWorld()).distance(getPlayer().getLocation()) > 6.0) { if (giveReason) { final String msg = BukkitLang.get(getPlayer(), "mustSpeakTo") .replace("", ChatColor.DARK_PURPLE + plugin.getDependencies().getNpcName(znpc.getUuid()) + ChatColor.YELLOW); sendMessage(ChatColor.YELLOW + msg); } return false; } } } if (getCurrentQuests().size() >= plugin.getConfigSettings().getMaxQuests() && plugin.getConfigSettings().getMaxQuests() > 0) { if (giveReason) { final String msg = BukkitLang.get(getPlayer(), "questMaxAllowed").replace("", String.valueOf(plugin.getConfigSettings().getMaxQuests())); sendMessage(ChatColor.YELLOW + msg); } return false; } else if (getCurrentQuests().containsKey(bukkitQuest)) { if (giveReason) { final String msg = BukkitLang.get(getPlayer(), "questAlreadyOn"); sendMessage(ChatColor.YELLOW + msg); } return false; } else if (getCompletedQuests().contains(bukkitQuest) && bukkitQuest.getPlanner().getCooldown() < 0) { if (giveReason) { final String msg = BukkitLang.get(getPlayer(), "questAlreadyCompleted") .replace("", ChatColor.DARK_PURPLE + bukkitQuest.getName() + ChatColor.YELLOW); sendMessage(ChatColor.YELLOW + msg); } return false; } else if (bukkitQuest.getBlockStart() != null) { if (giveReason) { final String msg = BukkitLang.get(getPlayer(), "noCommandStart").replace("", ChatColor.DARK_PURPLE + bukkitQuest.getName() + ChatColor.YELLOW); sendMessage(ChatColor.YELLOW + msg); } return false; } else if (getCompletedQuests().contains(bukkitQuest) && getRemainingCooldown(bukkitQuest) > 0 && !bukkitQuest.getPlanner().getOverride()) { if (giveReason) { final String msg = BukkitLang.get(getPlayer(), "questTooEarly").replace("", ChatColor.AQUA + bukkitQuest.getName()+ ChatColor.YELLOW).replace("

* * For player cooldown, use {@link #canAcceptOffer(Quest, boolean)} instead * * @param quest The quest to check * @param giveReason Whether to inform Quester of unpunctuality * @return true if on time */ public boolean isOnTime(final Quest quest, final boolean giveReason) { final Planner pln = quest.getPlanner(); final long currentTime = System.currentTimeMillis(); final long start = pln.getStartInMillis(); // Start time in milliseconds since UTC epoch final long end = pln.getEndInMillis(); // End time in milliseconds since UTC epoch final long duration = end - start; // How long the quest can be active for final long repeat = pln.getRepeat(); // Length to wait in-between start times if (start != -1) { if (currentTime < start) { if (giveReason) { String early = BukkitLang.get("plnTooEarly"); early = early.replace("", ChatColor.AQUA + quest.getName() + ChatColor.YELLOW); early = early.replace("

* * @param quest The quest to check and then offer * @param message Messages to send Quester upon quit, can be left null or empty * @return true if successful */ public boolean abandonQuest(final Quest quest, final String message) { return abandonQuest(quest, new String[] {message}); } /** * End a quest for this Quester, but ask permission first if possible

* * @param quest The quest to check and then offer * @param messages Messages to send Quester upon quit, can be left null or empty * @return true if successful */ public boolean abandonQuest(final Quest quest, final String[] messages) { if (quest == null) { return false; } final QuestQuitEvent event = new QuestQuitEvent(quest, this); plugin.getServer().getPluginManager().callEvent(event); if (event.isCancelled()) { return false; } final ConfigSettings settings = plugin.getConfigSettings(); if (getPlayer() != null) { setQuestIdToQuit(quest.getId()); if (settings.canConfirmAbandon()) { final ConversationFactory cf = new ConversationFactory(plugin).withModality(false) .withPrefix(context -> ChatColor.GRAY.toString()) .withFirstPrompt(new QuestAbandonPrompt(plugin)).withTimeout(settings.getAcceptTimeout()) .thatExcludesNonPlayersWithMessage("Console may not perform this conversation!") .addConversationAbandonedListener(plugin.getConvoListener()); cf.buildConversation(getPlayer()).begin(); } else { quitQuest(quest, messages); } return true; } return false; } /** * End a quest for this Quester * * @param quest The quest to start * @param message Message to inform player, can be left null or empty * @since 3.8.6 */ public void quitQuest(final Quest quest, final String message) { quitQuest(quest, new String[] {message}); } /** * End a quest for this Quester * * @param quest The quest to start * @param messages Messages to inform player, can be left null or empty * @since 3.8.6 */ public void quitQuest(final Quest quest, final String[] messages) { if (quest == null) { return; } hardQuit(quest); if (plugin.getConfigSettings().getConsoleLogging() > 1) { plugin.getLogger().info(getPlayer().getUniqueId() + " quit quest " + quest.getName()); } for (final String message : messages) { if (message != null && !message.equals("") && getPlayer().isOnline()) { sendMessage(message); } } saveData(); updateJournal(); if (compassTargetQuestId != null && compassTargetQuestId.equals(quest.getId())) { compassTargetQuestId = null; } } /** * Show the player a list of their available quests * * @param quester Quester to show the list * @param page Page to display, with 7 quests per page */ public void listQuests(final Quester quester, final int page) { // Although we could copy the quests list to a new object, we instead opt to // duplicate code to improve efficiency if ignore-locked-quests is set to 'false' final int rows = 7; final Player player = quester.getPlayer(); final Collection quests = plugin.getLoadedQuests(); if (plugin.getConfigSettings().canIgnoreLockedQuests()) { final LinkedList available = new LinkedList<>(); for (final Quest q : quests) { if (!quester.getCompletedQuests().contains(q)) { if (q.testRequirements(quester)) { available.add(q); } } else if (q.getPlanner().hasCooldown() && quester.getRemainingCooldown(q) < 0) { if (q.testRequirements(quester)) { available.add(q); } } } if ((available.size() + rows) <= (page * rows) || available.size() == 0) { BukkitLang.send(player, ChatColor.YELLOW + BukkitLang.get(player, "pageNotExist")); } else { BukkitLang.send(player, ChatColor.GOLD + BukkitLang.get(player, "questListTitle")); int fromOrder = (page - 1) * rows; final List subQuests; if (available.size() >= (fromOrder + rows)) { subQuests = available.subList((fromOrder), (fromOrder + rows)); } else { subQuests = available.subList((fromOrder), available.size()); } fromOrder++; for (final Quest q : subQuests) { if (quester.canAcceptOffer(q, false)) { quester.sendMessage(ChatColor.YELLOW + Integer.toString(fromOrder) + ". " + q.getName()); } else { quester.sendMessage(ChatColor.GRAY + Integer.toString(fromOrder) + ". " + q.getName()); } fromOrder++; } final int numPages = (int) Math.ceil(((double) available.size()) / ((double) rows)); String msg = BukkitLang.get(player, "pageFooter"); msg = msg.replace("", String.valueOf(page)); msg = msg.replace("", String.valueOf(numPages)); BukkitLang.send(player, ChatColor.GOLD + msg); } } else { if ((quests.size() + rows) <= (page * rows) || quests.size() == 0) { BukkitLang.send(player, ChatColor.YELLOW + BukkitLang.get(player, "pageNotExist")); } else { BukkitLang.send(player, ChatColor.GOLD + BukkitLang.get(player, "questListTitle")); int fromOrder = (page - 1) * rows; final List subQuests; if (quests.size() >= (fromOrder + rows)) { subQuests = new LinkedList<>(quests).subList((fromOrder), (fromOrder + rows)); } else { subQuests = new LinkedList<>(quests).subList((fromOrder), quests.size()); } fromOrder++; for (final Quest q : subQuests) { if (quester.canAcceptOffer(q, false)) { BukkitLang.send(player, ChatColor.YELLOW + Integer.toString(fromOrder) + ". " + q.getName()); } else { BukkitLang.send(player, ChatColor.GRAY + Integer.toString(fromOrder) + ". " + q.getName()); } fromOrder++; } final int numPages = (int) Math.ceil(((double) quests.size()) / ((double) rows)); String msg = BukkitLang.get(player, "pageFooter"); msg = msg.replace("", String.valueOf(page)); msg = msg.replace("", String.valueOf(numPages)); BukkitLang.send(player, ChatColor.GOLD + msg); } } } /** * Get current requirements for a quest, both finished and unfinished * * @param quest The quest to get objectives of * @param ignoreOverrides Whether to ignore objective-overrides * @return List of detailed requirements */ public LinkedList getCurrentRequirements(final Quest quest, final boolean ignoreOverrides) { if (quest == null) { return new LinkedList<>(); } final BukkitRequirements requirements = (BukkitRequirements) quest.getRequirements(); if (!ignoreOverrides) { if (requirements.getDetailsOverride() != null && !requirements.getDetailsOverride().isEmpty()) { final LinkedList current = new LinkedList<>(); for (final String s : requirements.getDetailsOverride()) { String message = ChatColor.RED + BukkitConfigUtil.parseString( ChatColor.translateAlternateColorCodes('&', s), quest, getPlayer()); if (plugin.getDependencies().getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(getPlayer(), message); } current.add(message); } return current; } } final LinkedList unfinishedRequirements = new LinkedList<>(); final LinkedList finishedRequirements = new LinkedList<>(); final LinkedList current = new LinkedList<>(); final OfflinePlayer player = getPlayer(); if (requirements.getMoney() > 0 && plugin.getDependencies().getVaultEconomy() != null) { final String currency = requirements.getMoney() > 1 ? plugin.getDependencies().getVaultEconomy() .currencyNamePlural() : plugin.getDependencies().getVaultEconomy().currencyNameSingular(); if (plugin.getDependencies().getVaultEconomy().getBalance(player) >= requirements.getMoney()) { unfinishedRequirements.add(ChatColor.GREEN + "" + requirements.getMoney() + " " + currency); } else { finishedRequirements.add(ChatColor.GRAY + "" + requirements.getMoney() + " " + currency); } } if (requirements.getQuestPoints() > 0) { if (getQuestPoints() >= requirements.getQuestPoints()) { unfinishedRequirements.add(ChatColor.GREEN + "" + requirements.getQuestPoints() + " " + BukkitLang.get("questPoints")); } else { finishedRequirements.add(ChatColor.GRAY + "" + requirements.getQuestPoints() + " " + BukkitLang.get("questPoints")); } } final Map completed = completedQuests.stream() .collect(Collectors.toMap(Quest::getId, Quest::getName)); for (final String questId : requirements.getNeededQuestIds()) { if (completed.containsKey(questId)) { String msg = BukkitLang.get("haveCompleted"); msg = msg.replace("", ChatColor.ITALIC + "" + ChatColor.DARK_PURPLE + completed.get(questId) + ChatColor.GREEN); finishedRequirements.add(ChatColor.GREEN + msg); } else { String msg = BukkitLang.get("mustComplete"); msg = msg.replace("", ChatColor.ITALIC + "" + ChatColor.DARK_PURPLE + plugin.getQuestById(questId).getName() + ChatColor.GREEN); unfinishedRequirements.add(ChatColor.GRAY + msg); } } for (final String questId : requirements.getBlockQuestIds()) { if (completed.containsKey(questId)) { String msg = BukkitLang.get("cannotComplete"); msg = msg.replace("", ChatColor.ITALIC + "" + ChatColor.DARK_PURPLE + quest.getName() + ChatColor.RED); current.add(ChatColor.RED + msg); } } for (final Quest q : currentQuests.keySet()) { if (q != null) { if (requirements.getBlockQuestIds().contains(q.getId())) { current.add(ChatColor.RED + quest.getName()); } } } for (final String s : requirements.getMcmmoSkills()) { final SkillType st = plugin.getDependencies().getMcMMOSkill(s); final int lvl = requirements.getMcmmoAmounts().get(requirements.getMcmmoSkills().indexOf(s)); if (UserManager.getOfflinePlayer(player).getProfile().getSkillLevel(st) >= lvl) { finishedRequirements.add(ChatColor.GREEN + "" + lvl + " " + s); } else { unfinishedRequirements.add(ChatColor.GRAY + "" + lvl + " " + s); } } if (requirements.getHeroesPrimaryClass() != null) { if (plugin.getDependencies() .testPrimaryHeroesClass(requirements.getHeroesPrimaryClass(), player.getUniqueId())) { finishedRequirements.add(ChatColor.GREEN + BukkitLang.get("reqHeroesPrimaryDisplay") + " " + requirements.getHeroesPrimaryClass()); } else { unfinishedRequirements.add(ChatColor.GRAY + BukkitLang.get("reqHeroesPrimaryDisplay") + " " + requirements.getHeroesPrimaryClass()); } } if (requirements.getHeroesSecondaryClass() != null) { if (plugin.getDependencies() .testSecondaryHeroesClass(requirements.getHeroesSecondaryClass(), player.getUniqueId())) { finishedRequirements.add(ChatColor.GREEN + BukkitLang.get("reqHeroesSecondaryDisplay") + " " + requirements.getHeroesSecondaryClass()); } else { finishedRequirements.add(ChatColor.GRAY + BukkitLang.get("reqHeroesSecondaryDisplay") + " " + requirements.getHeroesSecondaryClass()); } } if (player.isOnline()) { final Inventory fakeInv = Bukkit.createInventory(null, InventoryType.PLAYER); fakeInv.setContents(getPlayer().getInventory().getContents().clone()); int num = 0; for (final ItemStack is : requirements.getItems()) { if (BukkitInventoryUtil.canRemoveItem(fakeInv, is)) { BukkitInventoryUtil.removeItem(fakeInv, is); num += is.getAmount(); } if (num >= is.getAmount()) { finishedRequirements.add(ChatColor.GREEN + "" + is.getAmount() + " " + BukkitItemUtil.getName(is)); } else { unfinishedRequirements.add(ChatColor.GRAY + "" + is.getAmount() + " " + BukkitItemUtil.getName(is)); } num = 0; } for (final String perm :requirements.getPermissions()) { if (getPlayer().hasPermission(perm)) { finishedRequirements.add(ChatColor.GREEN + BukkitLang.get("permissionDisplay") + " " + perm); } else { unfinishedRequirements.add(ChatColor.GRAY + BukkitLang.get("permissionDisplay") + " " + perm); } } for (final Entry> m : requirements.getCustomRequirements().entrySet()) { for (final CustomRequirement cr : plugin.getCustomRequirements()) { if (cr.getName().equalsIgnoreCase(m.getKey())) { String message = cr.getDisplay() != null ? cr.getDisplay() : m.getKey(); for (Entry prompt : cr.getData().entrySet()) { final String replacement = "%" + prompt.getKey() + "%"; try { if (message.contains(replacement)) { message = message.replace(replacement, String.valueOf(m.getValue().get(prompt.getKey()))); } } catch (final NullPointerException ne) { plugin.getLogger().severe("Unable to gather display for " + cr.getName() + " on " + quest.getName()); ne.printStackTrace(); } } if (cr.testRequirement(getPlayer().getUniqueId(), m.getValue())) { finishedRequirements.add(ChatColor.GREEN + "" + message); } else { unfinishedRequirements.add(ChatColor.GRAY + "" + message); } } } } } current.addAll(unfinishedRequirements); current.addAll(finishedRequirements); return current; } /** * Get current objectives for a quest, both finished and unfinished * * @param quest The quest to get objectives of * @param ignoreOverrides Whether to ignore objective-overrides * @param formatNames Whether to format item/entity names, if applicable * @return List of detailed objectives */ @SuppressWarnings("deprecation") public LinkedList getCurrentObjectives(final Quest quest, final boolean ignoreOverrides, final boolean formatNames) { if (quest == null) { plugin.getLogger().severe("Quest was null when getting objectives for " + getLastKnownName()); return new LinkedList<>(); } if (getQuestDataOrDefault(quest) == null) { plugin.getLogger().warning("Quest data was null when getting objectives for " + quest.getName()); return new LinkedList<>(); } final BukkitStage stage = (BukkitStage) getCurrentStage(quest); if (stage == null) { //plugin.getLogger().warning("Current stage was null when getting objectives for " + quest.getName()); return new LinkedList<>(); } final BukkitDependencies depends = plugin.getDependencies(); if (!ignoreOverrides && !stage.getObjectiveOverrides().isEmpty()) { final LinkedList objectives = new LinkedList<>(); for (final String s: stage.getObjectiveOverrides()) { String message = ChatColor.GREEN + BukkitConfigUtil.parseString(s, quest, getPlayer()); if (depends.getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(getPlayer(), message); } // TODO is this acceptable? objectives.add(new BukkitObjective(ObjectiveType.CUSTOM, message, 0, 1)); } return objectives; } final BukkitQuestProgress data = (BukkitQuestProgress) getQuestDataOrDefault(quest); final LinkedList objectives = new LinkedList<>(); for (final ItemStack goal : stage.getBlocksToBreak()) { for (final ItemStack progress : data.blocksBroken) { if (progress.getType().equals(goal.getType()) && progress.getDurability() == goal.getDurability()) { final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "break"), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(progress)); } objectives.add(new BukkitObjective(ObjectiveType.BREAK_BLOCK, message, progress, goal)); } } } for (final ItemStack goal : stage.getBlocksToDamage()) { for (final ItemStack progress : data.blocksDamaged) { if (progress.getType().equals(goal.getType()) && progress.getDurability() == goal.getDurability()) { final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "damage"), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(progress)); } objectives.add(new BukkitObjective(ObjectiveType.DAMAGE_BLOCK, message, progress, goal)); } } } for (final ItemStack goal : stage.getBlocksToPlace()) { for (final ItemStack progress : data.blocksPlaced) { final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "place"), progress.getAmount(), goal.getAmount()); if (progress.getType().equals(goal.getType()) && progress.getDurability() == goal.getDurability()) { if (formatNames) { message = message.replace("", BukkitItemUtil.getName(progress)); } objectives.add(new BukkitObjective(ObjectiveType.PLACE_BLOCK, message, progress, goal)); } } } for (final ItemStack goal : stage.getBlocksToUse()) { for (final ItemStack progress : data.blocksUsed) { final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "use"), progress.getAmount(), goal.getAmount()); if (progress.getType().equals(goal.getType()) && progress.getDurability() == goal.getDurability()) { if (formatNames) { message = message.replace("", BukkitItemUtil.getName(progress)); } objectives.add(new BukkitObjective(ObjectiveType.USE_BLOCK, message, progress, goal)); } } } for (final ItemStack goal : stage.getBlocksToCut()) { for (final ItemStack progress : data.blocksCut) { final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "cut"), progress.getAmount(), goal.getAmount()); if (progress.getType().equals(goal.getType()) && progress.getDurability() == goal.getDurability()) { if (formatNames) { message = message.replace("", BukkitItemUtil.getName(progress)); } objectives.add(new BukkitObjective(ObjectiveType.CUT_BLOCK, message, progress, goal)); } } } int craftIndex = 0; for (final ItemStack goal : stage.getItemsToCraft()) { if (data.itemsCrafted.size() > craftIndex) { final ItemStack progress = data.itemsCrafted.get(craftIndex); final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "craftItem"), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(goal)); } objectives.add(new BukkitObjective(ObjectiveType.CRAFT_ITEM, message, progress, goal)); } craftIndex++; } int smeltIndex = 0; for (final ItemStack goal : stage.getItemsToSmelt()) { if (data.itemsSmelted.size() > smeltIndex) { final ItemStack progress = data.itemsSmelted.get(smeltIndex); final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "smeltItem"), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(goal)); } objectives.add(new BukkitObjective(ObjectiveType.SMELT_ITEM, message, progress, goal)); } smeltIndex++; } int enchantIndex = 0; for (final ItemStack goal : stage.getItemsToEnchant()) { if (data.itemsEnchanted.size() > enchantIndex) { final ItemStack progress = data.itemsEnchanted.get(enchantIndex); final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "enchItem"), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(goal)); } if (goal.getEnchantments().isEmpty()) { objectives.add(new BukkitObjective(ObjectiveType.ENCHANT_ITEM, message.replace("", "") .replace("", "") .replaceAll("\\s+", " "), progress, goal)); } else { for (final Entry e : goal.getEnchantments().entrySet()) { objectives.add(new BukkitObjective(ObjectiveType.ENCHANT_ITEM, message.replace("", BukkitItemUtil.getPrettyEnchantmentName(e.getKey())) .replace("", RomanNumeral.getNumeral(e.getValue())), progress, goal)); } } } enchantIndex++; } int brewIndex = 0; for (final ItemStack goal : stage.getItemsToBrew()) { if (data.itemsBrewed.size() > brewIndex) { final ItemStack progress = data.itemsBrewed.get(brewIndex); final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "brewItem"), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(goal)); } objectives.add(new BukkitObjective(ObjectiveType.BREW_ITEM, message, progress, goal)); } brewIndex++; } int consumeIndex = 0; for (final ItemStack goal : stage.getItemsToConsume()) { if (data.itemsConsumed.size() > consumeIndex) { final ItemStack progress = data.itemsConsumed.get(consumeIndex); final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "consumeItem"), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(goal)); } objectives.add(new BukkitObjective(ObjectiveType.CONSUME_ITEM, message, progress, goal)); } consumeIndex++; } int deliverIndex = 0; for (final ItemStack goal : stage.getItemsToDeliver()) { if (data.itemsDelivered.size() > deliverIndex) { final ItemStack progress = data.itemsDelivered.get(deliverIndex); final UUID npc = stage.getItemDeliveryTargets().get(deliverIndex); final ChatColor color = progress.getAmount() < goal.getAmount() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "deliver").replace("", depends.getNpcName(npc)), progress.getAmount(), goal.getAmount()); if (formatNames) { message = message.replace("", BukkitItemUtil.getName(goal)); } objectives.add(new BukkitObjective(ObjectiveType.DELIVER_ITEM, message, progress, goal)); } deliverIndex++; } int interactIndex = 0; for (final UUID n : stage.getNpcsToInteract()) { if (data.npcsInteracted.size() > interactIndex) { final boolean progress = data.npcsInteracted.get(interactIndex); final ChatColor color = !progress ? ChatColor.GREEN : ChatColor.GRAY; String message = color + BukkitLang.get(getPlayer(), "talkTo") .replace("", depends.getNpcName(n)); if (depends.getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(getPlayer(), message); } objectives.add(new BukkitObjective(ObjectiveType.TALK_TO_NPC, message, progress ? 1 : 0, 1)); } interactIndex++; } int npcKillIndex = 0; for (final UUID n : stage.getNpcsToKill()) { int progress = 0; if (data.npcsNumKilled.size() > npcKillIndex) { progress = data.npcsNumKilled.get(npcKillIndex); } final int goal = stage.getNpcNumToKill().get(npcKillIndex); final ChatColor color = progress < goal ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "kill"), progress, goal); if (message.contains("")) { message = message.replace("", depends.getNpcName(n)); } else { message += " " + depends.getNpcName(n); } objectives.add(new BukkitObjective(ObjectiveType.KILL_NPC, message, progress, goal)); npcKillIndex++; } int mobKillIndex = 0; for (final EntityType e : stage.getMobsToKill()) { int progress = 0; if (data.mobNumKilled.size() > mobKillIndex) { progress = data.mobNumKilled.get(mobKillIndex); } final int goal = stage.getMobNumToKill().get(mobKillIndex); final ChatColor color = progress < goal ? ChatColor.GREEN : ChatColor.GRAY; String message = color + ""; if (stage.getLocationsToKillWithin().isEmpty()) { message += BukkitLang.get(getPlayer(), "kill"); if (message.contains("")) { message = message.replace("", "" + color + progress + "/" + goal); } } else { message += BukkitLang.get(getPlayer(), "killAtLocation").replace("", stage.getKillNames().get(stage.getMobsToKill().indexOf(e))); if (message.contains("")) { message = message.replace("", "" + color + progress + "/" + goal); } else { message += color + ": " + progress + "/" + goal; } } if (depends.getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(getPlayer(), message); } if (formatNames) { message = message.replace("", BukkitMiscUtil.snakeCaseToUpperCamelCase(e.name())); } objectives.add(new BukkitObjective(ObjectiveType.KILL_MOB, message, new BukkitCountableMob(e, progress), new BukkitCountableMob(e, goal))); mobKillIndex++; } int tameIndex = 0; for (final EntityType e : stage.getMobsToTame()) { int progress = 0; if (data.mobsTamed.size() > tameIndex) { progress = data.mobsTamed.get(tameIndex); } final int goal = stage.getMobNumToTame().get(tameIndex); final ChatColor color = progress < goal ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "tame"), progress, goal); if (!message.contains("")) { message += " "; } if (formatNames) { message = message.replace("", BukkitMiscUtil.snakeCaseToUpperCamelCase(stage.getMobsToTame().get(tameIndex).name())); } objectives.add(new BukkitObjective(ObjectiveType.TAME_MOB, message, new BukkitCountableMob(e, progress), new BukkitCountableMob(e, goal))); tameIndex++; } if (stage.getFishToCatch() != null) { final int progress = data.getFishCaught(); final int goal = stage.getFishToCatch(); final ChatColor color = progress < goal ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "catchFish"), progress, goal); objectives.add(new BukkitObjective(ObjectiveType.CATCH_FISH, message, progress, goal)); } if (stage.getCowsToMilk() != null) { final int progress = data.getCowsMilked(); final int goal = stage.getCowsToMilk(); final ChatColor color = progress < goal ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "milkCow"), progress, goal); objectives.add(new BukkitObjective(ObjectiveType.MILK_COW, message, progress, goal)); } int shearIndex = 0; for (final int goal : stage.getSheepNumToShear()) { int progress = 0; if (data.sheepSheared.size() > shearIndex) { progress = data.sheepSheared.get(shearIndex); } final ChatColor color = progress < goal ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "shearSheep"), progress, goal); message = message.replace("", BukkitMiscUtil.getPrettyDyeColorName(stage.getSheepToShear() .get(shearIndex))); objectives.add(new BukkitObjective(ObjectiveType.SHEAR_SHEEP, message, progress, goal)); shearIndex++; } if (stage.getPlayersToKill() != null) { final int progress = data.getPlayersKilled(); final int goal = stage.getPlayersToKill(); final ChatColor color = data.getPlayersKilled() < stage.getPlayersToKill() ? ChatColor.GREEN : ChatColor.GRAY; String message = formatCurrentObjectiveMessage(color, BukkitLang.get(getPlayer(), "killPlayer"), progress, goal); objectives.add(new BukkitObjective(ObjectiveType.KILL_PLAYER, message, progress, goal)); } for (int i = 0 ; i < stage.getLocationsToReach().size(); i++) { if (i < data.locationsReached.size()) { final boolean progress = data.locationsReached.get(i); final ChatColor color = !progress ? ChatColor.GREEN : ChatColor.GRAY; String message = color + BukkitLang.get(getPlayer(), "goTo"); message = message.replace("", stage.getLocationNames().get(i)); objectives.add(new BukkitObjective(ObjectiveType.REACH_LOCATION, message, progress ? 1 : 0, 1)); } } int passIndex = 0; for (final String s : stage.getPasswordDisplays()) { boolean progress = false; if (data.passwordsSaid.size() > passIndex) { progress = data.passwordsSaid.get(passIndex); } final ChatColor color = !progress ? ChatColor.GREEN : ChatColor.GRAY; String message = color + s; objectives.add(new BukkitObjective(ObjectiveType.PASSWORD, message, progress ? 1 : 0, 1)); passIndex++; } int customIndex = 0; for (final CustomObjective co : stage.getCustomObjectives()) { int progress = 0; if (data.customObjectiveCounts.size() > customIndex) { progress = data.customObjectiveCounts.get(customIndex); } final int goal = stage.getCustomObjectiveCounts().get(customIndex); final ChatColor color = progress < goal ? ChatColor.GREEN : ChatColor.GRAY; String message = color + co.getDisplay(); for (final Entry prompt : co.getData()) { final String replacement = "%" + prompt.getKey() + "%"; try { for (final Entry e : stage.getCustomObjectiveData()) { if (e.getKey().equals(prompt.getKey())) { if (message.contains(replacement)) { message = message.replace(replacement, String.valueOf(e.getValue())); } } } } catch (final NullPointerException ne) { plugin.getLogger().severe("Unable to gather display for " + co.getName() + " on " + quest.getName()); ne.printStackTrace(); } } if (co.canShowCount()) { message = message.replace("%count%", progress + "/" + goal); } message = BukkitConfigUtil.parseString(message.trim().replaceAll("\\s{2,}", "")); objectives.add(new BukkitObjective(ObjectiveType.CUSTOM, message, progress, goal)); customIndex++; } return objectives; } /** * Format current objective message with count and placeholders * * @param color Color for text * @param message Text to format * @param progress Objective progress * @param goal Objective goal * @return Formatted message */ private String formatCurrentObjectiveMessage(ChatColor color, String message, int progress, int goal) { message = color + message; if (message.contains("")) { message = message.replace("", "" + color + progress + "/" + goal); } if (plugin.getDependencies().getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(getPlayer(), message); } return message; } /** * Show current objectives for a quest, if applicable

* * Respects PlaceholderAPI and translations, when enabled. * * @param quest The quest to get current stage objectives of * @param quester The player to show current stage objectives to * @param ignoreOverrides Whether to ignore objective-overrides */ public void showCurrentObjectives(final Quest quest, final Quester quester, final boolean ignoreOverrides) { if (quest == null) { plugin.getLogger().severe("Quest was null when showing objectives for " + quester.getLastKnownName()); return; } final BukkitQuester q = (BukkitQuester)quester; final Stage stage = quester.getCurrentStage(quest); if (stage == null) { plugin.getLogger().warning("Current stage was null when showing objectives for " + quest.getName()); return; } if (!ignoreOverrides && !stage.getObjectiveOverrides().isEmpty()) { for (final String s: stage.getObjectiveOverrides()) { String message = (s.trim().length() > 0 ? "- " : "") + ChatColor.GREEN + BukkitConfigUtil .parseString(s, quest, quester.getPlayer()); if (plugin.getDependencies().getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(quester.getPlayer(), message); } quester.sendMessage(message); } return; } final LocaleManager localeManager = plugin.getLocaleManager(); final BukkitConfigSettings settings = plugin.getConfigSettings(); if (localeManager == null && settings.canTranslateNames()) { settings.setTranslateNames(false); plugin.getLogger().severe("Problem with locale manager! Item name translation disabled."); } for (final Objective obj : q.getCurrentObjectives(quest, false, false)) { final BukkitObjective objective = (BukkitObjective) obj; final String message = "- " + BukkitLang.BukkitFormatToken.convertString(quester.getPlayer(), objective.getMessage()); if (objective.getProgressAsItem() != null && objective.getGoalAsItem() != null) { final ItemStack progress = objective.getProgressAsItem(); final ItemStack goal = objective.getGoalAsItem(); if (!settings.canShowCompletedObjs() && progress.getAmount() >= goal.getAmount()) { continue; } if (localeManager != null && settings.canTranslateNames() && !goal.hasItemMeta() && Material.getMaterial("LINGERING_POTION") == null) { // Bukkit version is below 1.9 and item has no metadata localeManager.sendMessage(quester.getPlayer(), message, goal); } else if (localeManager != null && settings.canTranslateNames() && goal.getItemMeta() != null && !goal.getItemMeta().hasDisplayName() && !goal.getType().equals(Material.WRITTEN_BOOK)) { // Bukkit version is 1.9+ and item lacks custom name localeManager.sendMessage(quester.getPlayer(), message, goal); } else { if (goal.getEnchantments().isEmpty()) { quester.sendMessage(message.replace("", BukkitItemUtil.getName(goal)) .replace("", "") .replace("", "") .replaceAll("\\s+", " ")); } else { for (final Entry e : goal.getEnchantments().entrySet()) { quester.sendMessage(message.replace("", BukkitItemUtil.getName(goal)) .replace("", BukkitItemUtil.getPrettyEnchantmentName(e.getKey())) .replace("", RomanNumeral.getNumeral(e.getValue()))); } } } } else if (objective.getProgressAsMob() != null && objective.getGoalAsMob() != null) { final BukkitCountableMob progress = objective.getProgressAsMob(); final BukkitCountableMob goal = objective.getGoalAsMob(); if (!settings.canShowCompletedObjs() && progress.getCount() >= goal.getCount()) { continue; } if (localeManager != null && settings.canTranslateNames()) { localeManager.sendMessage(quester.getPlayer(), message, goal.getEntityType(), null); } else { quester.sendMessage(message.replace("", BukkitMiscUtil.snakeCaseToUpperCamelCase(goal.getEntityType().name()))); } } else { if (!settings.canShowCompletedObjs() && objective.getProgress() >= objective.getGoal()) { continue; } if (obj.getType().equals(ObjectiveType.CUSTOM)) { quester.sendMessage(message.trim().replaceAll("\\s{2,}", "")); } else { quester.sendMessage(message); } } } } /** * Check if player's current stage has the specified objective * * @param quest The quest to check objectives of * @param type The type of objective to check for * @return true if stage contains specified objective */ public boolean hasObjective(final Quest quest, final ObjectiveType type) { if (quest == null || getCurrentStage(quest) == null || type == null) { return false; } return getCurrentStage(quest).containsObjective(type); } /** * Check if player's current stage has the specified custom objective * * @param quest The quest to check custom objectives of * @param name The exact name of custom objective to check for * @return true if stage contains specified objective */ public boolean hasCustomObjective(final Quest quest, final String name) { if (quest == null || getCurrentStage(quest) == null || name == null) { return false; } for (final CustomObjective co : getCurrentStage(quest).getCustomObjectives()) { if (co.getName().equals(name)) { return true; } } return false; } /** * Show all of a player's conditions for the current stage of a quest.

* * @param quest The quest to get current stage objectives of * @param quester The player to show current stage objectives to */ public void showCurrentConditions(final Quest quest, final Quester quester) { if (quest == null) { plugin.getLogger().severe("Quest was null when getting conditions for " + quester.getLastKnownName()); return; } if (quester.getQuestDataOrDefault(quest) == null) { plugin.getLogger().warning("Quest data was null when showing conditions for " + quest.getName()); return; } final Stage stage = quester.getCurrentStage(quest); if (stage == null) { plugin.getLogger().warning("Current stage was null when showing conditions for " + quest.getName()); return; } final BukkitCondition c = (BukkitCondition) stage.getCondition(); if (c != null && stage.getObjectiveOverrides().isEmpty()) { quester.sendMessage(ChatColor.LIGHT_PURPLE + BukkitLang.get("stageEditorConditions")); final StringBuilder msg = new StringBuilder("- " + ChatColor.YELLOW); if (!c.getEntitiesWhileRiding().isEmpty()) { msg.append(BukkitLang.get("conditionEditorRideEntity")); for (final String e : c.getEntitiesWhileRiding()) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(e); } quester.sendMessage(msg.toString()); } else if (!c.getNpcsWhileRiding().isEmpty()) { msg.append(BukkitLang.get("conditionEditorRideNPC")); for (final UUID u : c.getNpcsWhileRiding()) { if (plugin.getDependencies().getCitizens() != null) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(CitizensAPI.getNPCRegistry() .getByUniqueId(u).getName()); } else { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(u); } } quester.sendMessage(msg.toString()); } else if (!c.getPermissions().isEmpty()) { msg.append(BukkitLang.get("conditionEditorPermissions")); for (final String e : c.getPermissions()) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(e); } quester.sendMessage(msg.toString()); } else if (!c.getItemsWhileHoldingMainHand().isEmpty()) { msg.append(BukkitLang.get("conditionEditorItemsInMainHand")); for (final ItemStack is : c.getItemsWhileHoldingMainHand()) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(BukkitItemUtil.getPrettyItemName(is .getType().name())); } quester.sendMessage(msg.toString()); } else if (!c.getItemsWhileWearing().isEmpty()) { msg.append(BukkitLang.get("conditionEditorItemsWear")); for (final ItemStack is : c.getItemsWhileWearing()) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(BukkitItemUtil.getPrettyItemName(is .getType().name())); } quester.sendMessage(msg.toString()); } else if (!c.getWorldsWhileStayingWithin().isEmpty()) { msg.append(BukkitLang.get("conditionEditorStayWithinWorld")); for (final String w : c.getWorldsWhileStayingWithin()) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(w); } quester.sendMessage(msg.toString()); } else if (c.getTickStartWhileStayingWithin() > -1 && c.getTickEndWhileStayingWithin() > -1) { msg.append(BukkitLang.get("conditionEditorStayWithinTicks")); msg.append(ChatColor.AQUA).append("\n \u2515 ").append(c.getTickStartWhileStayingWithin()) .append(" - ").append(c.getTickEndWhileStayingWithin()); quester.sendMessage(msg.toString()); } else if (!c.getBiomesWhileStayingWithin().isEmpty()) { msg.append(BukkitLang.get("conditionEditorStayWithinBiome")); for (final String b : c.getBiomesWhileStayingWithin()) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(BukkitMiscUtil .snakeCaseToUpperCamelCase(b)); } quester.sendMessage(msg.toString()); } else if (!c.getRegionsWhileStayingWithin().isEmpty()) { msg.append(BukkitLang.get("conditionEditorStayWithinRegion")); for (final String r : c.getRegionsWhileStayingWithin()) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(r); } quester.sendMessage(msg.toString()); } else if (!c.getPlaceholdersCheckIdentifier().isEmpty()) { msg.append(BukkitLang.get("conditionEditorCheckPlaceholder")); int index = 0; for (final String r : c.getPlaceholdersCheckIdentifier()) { if (c.getPlaceholdersCheckValue().size() > index) { msg.append(ChatColor.AQUA).append("\n \u2515 ").append(r).append(ChatColor.GRAY) .append(" = ").append(ChatColor.AQUA).append(c.getPlaceholdersCheckValue() .get(index)); } index++; } quester.sendMessage(msg.toString()); } } } /** * Marks block as broken if Quester has such an objective * * @param quest The quest for which the block is being broken * @param itemStack The block being broken */ @SuppressWarnings("deprecation") public void breakBlock(final Quest quest, final ItemStack itemStack) { itemStack.setAmount(0); ItemStack broken = itemStack; ItemStack toBreak = itemStack; final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.blocksBroken) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { broken = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ broken = is; } } else if (itemStack.getData() instanceof Crops && is.getData() instanceof Crops) { if (is.getDurability() > 0) { // Age is specified so check for durability if (itemStack.getDurability() == is.getDurability()) { broken = is; } } else { // Age is unspecified so ignore durability broken = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { broken = is; } } else { // Blocks are not solid so ignore durability broken = is; } } } for (final ItemStack is : ((BukkitStage) getCurrentStage(quest)).getBlocksToBreak()) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { toBreak = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ toBreak = is; } } else if (itemStack.getData() instanceof Crops && is.getData() instanceof Crops) { if (is.getDurability() > 0) { // Age is specified so check for durability if (itemStack.getDurability() == is.getDurability()) { toBreak = is; } } else { // Age is unspecified so ignore durability toBreak = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { toBreak = is; } } else { // Blocks are not solid so ignore durability toBreak = is; } } } final ObjectiveType type = ObjectiveType.BREAK_BLOCK; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, broken.getAmount(), toBreak.getAmount())); plugin.getServer().getPluginManager().callEvent(preEvent); final ItemStack newBroken = broken; if (broken.getAmount() < toBreak.getAmount()) { newBroken.setAmount(broken.getAmount() + 1); if (bukkitQuestData.blocksBroken.contains(broken)) { bukkitQuestData.blocksBroken.set(bukkitQuestData.blocksBroken.indexOf(broken), newBroken); if (broken.getAmount() == toBreak.getAmount()) { finishObjective(quest, new BukkitObjective(type, null, itemStack, toBreak), null, null, null, null, null, null, null); // Multiplayer final ItemStack finalBroken = broken; final ItemStack finalToBreak = toBreak; dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).blocksBroken.set(bukkitQuestData.blocksBroken .indexOf(finalBroken), newBroken); q.finishObjective(quest, new BukkitObjective(type, null, itemStack, finalToBreak), null, null, null, null, null, null, null); return null; }); } } } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newBroken.getAmount(), toBreak.getAmount())); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Marks block as damaged if Quester has such an objective * * @param quest The quest for which the block is being damaged * @param itemStack The block being damaged */ @SuppressWarnings("deprecation") public void damageBlock(final Quest quest, final ItemStack itemStack) { itemStack.setAmount(0); ItemStack damaged = itemStack; ItemStack toDamage = itemStack; final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.blocksDamaged) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { damaged = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ damaged = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { damaged = is; } } else { // Blocks are not solid so ignore durability damaged = is; } } } for (final ItemStack is : ((BukkitStage) getCurrentStage(quest)).getBlocksToDamage()) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { toDamage = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ toDamage = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { toDamage = is; } } else { // Blocks are not solid so ignore durability toDamage = is; } } } final ObjectiveType type = ObjectiveType.DAMAGE_BLOCK; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, damaged.getAmount(), toDamage.getAmount())); plugin.getServer().getPluginManager().callEvent(preEvent); final ItemStack newDamaged = damaged; if (damaged.getAmount() < toDamage.getAmount()) { newDamaged.setAmount(damaged.getAmount() + 1); if (bukkitQuestData.blocksDamaged.contains(damaged)) { bukkitQuestData.blocksDamaged.set(bukkitQuestData.blocksDamaged.indexOf(damaged), newDamaged); if (damaged.getAmount() == toDamage.getAmount()) { finishObjective(quest, new BukkitObjective(type, null, itemStack, toDamage), null, null, null, null, null, null, null); // Multiplayer final ItemStack finalDamaged = damaged; final ItemStack finalToDamage = toDamage; dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).blocksDamaged.set(bukkitQuestData.blocksDamaged .indexOf(finalDamaged), newDamaged); q.finishObjective(quest, new BukkitObjective(type, null, itemStack, finalToDamage), null, null, null, null, null, null, null); return null; }); } } } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newDamaged.getAmount(), toDamage.getAmount())); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Marks block as placed if Quester has such an objective * * @param quest The quest for which the block is being placed * @param itemStack The block being placed */ @SuppressWarnings("deprecation") public void placeBlock(final Quest quest, final ItemStack itemStack) { itemStack.setAmount(0); ItemStack placed = itemStack; ItemStack toPlace = itemStack; final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.blocksPlaced) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { placed = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ placed = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { placed = is; } } else { // Blocks are not solid so ignore durability placed = is; } } } for (final ItemStack is : ((BukkitStage) getCurrentStage(quest)).getBlocksToPlace()) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { toPlace = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ toPlace = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { toPlace = is; } } else { // Blocks are not solid so ignore durability toPlace = is; } } } final ObjectiveType type = ObjectiveType.PLACE_BLOCK; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, placed.getAmount(), toPlace.getAmount())); plugin.getServer().getPluginManager().callEvent(preEvent); final ItemStack newPlaced = placed; if (placed.getAmount() < toPlace.getAmount()) { newPlaced.setAmount(placed.getAmount() + 1); if (bukkitQuestData.blocksPlaced.contains(placed)) { bukkitQuestData.blocksPlaced.set(bukkitQuestData.blocksPlaced.indexOf(placed), newPlaced); if (placed.getAmount() == toPlace.getAmount()) { finishObjective(quest, new BukkitObjective(type, null, itemStack, toPlace), null, null, null, null, null, null, null); // Multiplayer final ItemStack finalPlaced = placed; final ItemStack finalToPlace = toPlace; dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).blocksPlaced.set(bukkitQuestData.blocksPlaced .indexOf(finalPlaced), newPlaced); q.finishObjective(quest, new BukkitObjective(type, null, itemStack, finalToPlace), null, null, null, null, null, null, null); return null; }); } } } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newPlaced.getAmount(), toPlace.getAmount())); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Marks block as used if Quester has such an objective * * @param quest The quest for which the block is being used * @param itemStack The block being used */ @SuppressWarnings("deprecation") public void useBlock(final Quest quest, final ItemStack itemStack) { itemStack.setAmount(0); ItemStack used = itemStack; ItemStack toUse = itemStack; final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.blocksUsed) { if (itemStack.getType() == is.getType() ) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { used = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ used = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { used = is; } } else { // Blocks are not solid so ignore durability used = is; } } } for (final ItemStack is : ((BukkitStage) getCurrentStage(quest)).getBlocksToUse()) { if (itemStack.getType() == is.getType() ) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid, so check durability if (itemStack.getDurability() == is.getDurability()) { toUse = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ toUse = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { toUse = is; } } else { // Blocks are not solid, so ignore durability toUse = is; } } } final ObjectiveType type = ObjectiveType.USE_BLOCK; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, used.getAmount(), toUse.getAmount())); plugin.getServer().getPluginManager().callEvent(preEvent); final ItemStack newUsed = used; if (used.getAmount() < toUse.getAmount()) { newUsed.setAmount(used.getAmount() + 1); if (bukkitQuestData.blocksUsed.contains(used)) { bukkitQuestData.blocksUsed.set(bukkitQuestData.blocksUsed.indexOf(used), newUsed); if (used.getAmount() == toUse.getAmount()) { finishObjective(quest, new BukkitObjective(type, null, itemStack, toUse), null, null, null, null, null, null, null); // Multiplayer final ItemStack finalUsed = used; final ItemStack finalToUse = toUse; dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).blocksUsed.set(bukkitQuestData.blocksUsed .indexOf(finalUsed), newUsed); q.finishObjective(quest, new BukkitObjective(type, null, itemStack, finalToUse), null, null, null, null, null, null, null); return null; }); } } } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newUsed.getAmount(), toUse.getAmount())); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Marks block as cut if Quester has such an objective * * @param quest The quest for which the block is being cut * @param itemStack The block being cut */ @SuppressWarnings("deprecation") public void cutBlock(final Quest quest, final ItemStack itemStack) { itemStack.setAmount(0); ItemStack cut = itemStack; ItemStack toCut = itemStack; final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.blocksCut) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { cut = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ cut = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { cut = is; } } else { // Blocks are not solid so ignore durability cut = is; } } } for (final ItemStack is : ((BukkitStage) getCurrentStage(quest)).getBlocksToCut()) { if (itemStack.getType() == is.getType()) { if (itemStack.getType().isSolid() && is.getType().isSolid()) { // Blocks are solid so check for durability if (itemStack.getDurability() == is.getDurability()) { toCut = is; } else if (!plugin.getLocaleManager().isBelow113()) { // Ignore durability for 1.13+ toCut = is; } } else if (itemStack.getType().name().equals("RED_ROSE")) { // Flowers are unique so check for durability if (itemStack.getDurability() == is.getDurability()) { toCut = is; } } else { // Blocks are not solid so ignore durability toCut = is; } } } final ObjectiveType type = ObjectiveType.CUT_BLOCK; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, cut.getAmount(), toCut.getAmount())); plugin.getServer().getPluginManager().callEvent(preEvent); final ItemStack newCut = cut; if (cut.getAmount() < toCut.getAmount()) { newCut.setAmount(cut.getAmount() + 1); if (bukkitQuestData.blocksCut.contains(cut)) { bukkitQuestData.blocksCut.set(bukkitQuestData.blocksCut.indexOf(cut), newCut); if (cut.getAmount() == toCut.getAmount()) { finishObjective(quest, new BukkitObjective(type, null, itemStack, toCut), null, null, null, null, null, null, null); // Multiplayer final ItemStack finalCut = cut; final ItemStack finalToCut = toCut; dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).blocksCut.set(bukkitQuestData.blocksCut.indexOf(finalCut), newCut); q.finishObjective(quest, new BukkitObjective(type, null, itemStack, finalToCut), null, null, null, null, null, null, null); return null; }); } } } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newCut.getAmount(), toCut.getAmount())); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Mark item as crafted if Quester has such an objective * * @param quest The quest for which the item is being crafted * @param itemStack The item being crafted */ public void craftItem(final Quest quest, final ItemStack itemStack) { int currentIndex = -1; final LinkedList matches = new LinkedList<>(); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.itemsCrafted) { currentIndex++; if (BukkitItemUtil.compareItems(itemStack, is, true) == 0) { matches.add(currentIndex); } } if (matches.isEmpty()) { return; } for (final Integer match : matches) { final LinkedList items = new LinkedList<>(bukkitQuestData.itemsCrafted); final ItemStack found = items.get(match); final int amount = found.getAmount(); final int toCraft = ((BukkitStage) getCurrentStage(quest)).getItemsToCraft().get(match).getAmount(); final ObjectiveType type = ObjectiveType.CRAFT_ITEM; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, amount, toCraft)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newAmount = itemStack.getAmount() + amount; final Material m = itemStack.getType(); if (amount < toCraft) { if (newAmount >= toCraft) { found.setAmount(toCraft); bukkitQuestData.itemsCrafted.set(items.indexOf(found), found); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); // Multiplayer dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).itemsCrafted.set(items.indexOf(found), found); q.finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); return null; }); } else { found.setAmount(newAmount); bukkitQuestData.itemsCrafted.set(items.indexOf(found), found); } return; } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newAmount, toCraft)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Mark item as smelted if Quester has such an objective * * @param quest The quest for which the item is being smelted * @param itemStack The item being smelted */ public void smeltItem(final Quest quest, final ItemStack itemStack) { int currentIndex = -1; final LinkedList matches = new LinkedList<>(); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.itemsSmelted) { currentIndex++; if (BukkitItemUtil.compareItems(itemStack, is, true) == 0) { matches.add(currentIndex); } } if (matches.isEmpty()) { return; } for (final Integer match : matches) { final LinkedList items = new LinkedList<>(bukkitQuestData.itemsSmelted); final ItemStack found = items.get(match); final int amount = found.getAmount(); final int toSmelt = ((BukkitStage) getCurrentStage(quest)).getItemsToSmelt().get(match).getAmount(); final ObjectiveType type = ObjectiveType.SMELT_ITEM; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, amount, toSmelt)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newAmount = itemStack.getAmount() + amount; final Material m = itemStack.getType(); if (amount < toSmelt) { if (newAmount >= toSmelt) { found.setAmount(toSmelt); bukkitQuestData.itemsSmelted.set(items.indexOf(found), found); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); // Multiplayer dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).itemsSmelted.set(items.indexOf(found), found); q.finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); return null; }); } else { found.setAmount(newAmount); bukkitQuestData.itemsSmelted.set(items.indexOf(found), found); } return; } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newAmount, toSmelt)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Marks book as enchanted if Quester has such an objective * * @param quest The quest for which the item is being enchanted * @param itemStack The book being enchanted */ public void enchantBook(final Quest quest, final ItemStack itemStack, final Map enchantsToAdd) { int currentIndex = -1; final LinkedList matches = new LinkedList<>(); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.itemsEnchanted) { currentIndex++; if (is.getItemMeta() instanceof EnchantmentStorageMeta) { if (((EnchantmentStorageMeta)is.getItemMeta()).getStoredEnchants().equals(enchantsToAdd)) { matches.add(currentIndex); } } } if (matches.isEmpty()) { return; } for (final Integer match : matches) { final LinkedList items = new LinkedList<>(bukkitQuestData.itemsEnchanted); final ItemStack found = items.get(match); final int amount = found.getAmount(); final int toEnchant = ((BukkitStage) getCurrentStage(quest)).getItemsToEnchant().get(match).getAmount(); final ObjectiveType type = ObjectiveType.ENCHANT_ITEM; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, amount, toEnchant)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newAmount = itemStack.getAmount() + amount; final Material m = itemStack.getType(); if (amount < toEnchant) { if (newAmount >= toEnchant) { found.setAmount(toEnchant); bukkitQuestData.itemsEnchanted.set(items.indexOf(found), found); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); // Multiplayer dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).itemsEnchanted.set(items.indexOf(found), found); q.finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); return null; }); } else { found.setAmount(newAmount); bukkitQuestData.itemsEnchanted.set(items.indexOf(found), found); } return; } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, itemStack.getAmount() + amount, toEnchant)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Mark item as enchanted if Quester has such an objective * * @param quest The quest for which the item is being enchanted * @param itemStack The item being enchanted */ public void enchantItem(final Quest quest, final ItemStack itemStack) { int currentIndex = -1; final LinkedList matches = new LinkedList<>(); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (!itemStack.getType().equals(Material.BOOK)) { for (final ItemStack is : bukkitQuestData.itemsEnchanted) { currentIndex++; if (!is.getEnchantments().isEmpty()) { if (BukkitItemUtil.compareItems(itemStack, is, true) == 0) { matches.add(currentIndex); } } else { if (BukkitItemUtil.compareItems(itemStack, is, true) == -4) { matches.add(currentIndex); } } } } if (matches.isEmpty()) { return; } for (final Integer match : matches) { final LinkedList items = new LinkedList<>(bukkitQuestData.itemsEnchanted); final ItemStack found = items.get(match); final int amount = found.getAmount(); final int toEnchant = ((BukkitStage) getCurrentStage(quest)).getItemsToEnchant().get(match).getAmount(); final ObjectiveType type = ObjectiveType.ENCHANT_ITEM; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, amount, toEnchant)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newAmount = itemStack.getAmount() + amount; final Material m = itemStack.getType(); if (amount < toEnchant) { if (newAmount >= toEnchant) { found.setAmount(toEnchant); bukkitQuestData.itemsEnchanted.set(items.indexOf(found), found); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); // Multiplayer dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).itemsEnchanted.set(items.indexOf(found), found); q.finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); return null; }); } else { found.setAmount(newAmount); bukkitQuestData.itemsEnchanted.set(items.indexOf(found), found); } return; } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, itemStack.getAmount() + amount, toEnchant)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Mark item as brewed if Quester has such an objective * * @param quest The quest for which the item is being brewed * @param itemStack The item being brewed */ public void brewItem(final Quest quest, final ItemStack itemStack) { int currentIndex = -1; final LinkedList matches = new LinkedList<>(); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.itemsBrewed) { currentIndex++; if (BukkitItemUtil.compareItems(itemStack, is, true) == 0) { matches.add(currentIndex); } } if (matches.isEmpty()) { return; } for (final Integer match : matches) { final LinkedList items = new LinkedList<>(bukkitQuestData.itemsBrewed); final ItemStack found = items.get(match); final int amount = found.getAmount(); final int toBrew = ((BukkitStage) getCurrentStage(quest)).getItemsToBrew().get(match).getAmount(); final ObjectiveType type = ObjectiveType.BREW_ITEM; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, amount, toBrew)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newAmount = itemStack.getAmount() + amount; final Material m = itemStack.getType(); if (amount < toBrew) { if (newAmount >= toBrew) { found.setAmount(toBrew); bukkitQuestData.itemsBrewed.set(items.indexOf(found), found); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); // Multiplayer dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).itemsBrewed.set(items.indexOf(found), found); q.finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); return null; }); } else { found.setAmount(newAmount); bukkitQuestData.itemsBrewed.set(items.indexOf(found), found); } return; } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newAmount, toBrew)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Mark item as consumed if Quester has such an objective * * @param quest The quest for which the item is being consumed * @param itemStack The item being consumed */ public void consumeItem(final Quest quest, final ItemStack itemStack) { int currentIndex = -1; final LinkedList matches = new LinkedList<>(); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.itemsConsumed) { currentIndex++; if (BukkitItemUtil.compareItems(itemStack, is, true) == 0) { matches.add(currentIndex); } } if (matches.isEmpty()) { return; } for (final Integer match : matches) { final LinkedList items = new LinkedList<>(bukkitQuestData.itemsConsumed); final ItemStack found = items.get(match); final int amount = found.getAmount(); final int toConsume = ((BukkitStage) getCurrentStage(quest)).getItemsToConsume().get(match).getAmount(); final ObjectiveType type = ObjectiveType.CONSUME_ITEM; final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, amount, toConsume)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newAmount = itemStack.getAmount() + amount; final Material m = itemStack.getType(); if (amount < toConsume) { if (newAmount >= toConsume) { found.setAmount(toConsume); bukkitQuestData.itemsConsumed.set(items.indexOf(found), found); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); // Multiplayer dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).itemsConsumed.set(items.indexOf(found), found); q.finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); return null; }); } else { found.setAmount(newAmount); bukkitQuestData.itemsConsumed.set(items.indexOf(found), found); } return; } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newAmount, toConsume)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Mark item as delivered to a NPC if Quester has such an objective * * @param quest The quest for which the item is being delivered * @param npc UUID of the NPC being delivered to * @param itemStack The item being delivered */ public void deliverToNPC(final Quest quest, final UUID npc, final ItemStack itemStack) { if (npc == null) { return; } int currentIndex = -1; final LinkedList matches = new LinkedList<>(); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final ItemStack is : bukkitQuestData.itemsDelivered) { currentIndex++; if (BukkitItemUtil.compareItems(itemStack, is, true) == 0) { matches.add(currentIndex); } } if (matches.isEmpty()) { return; } final Player player = getPlayer(); for (final Integer match : matches) { final LinkedList items = new LinkedList<>(bukkitQuestData.itemsDelivered); if (!getCurrentStage(quest).getItemDeliveryTargets().get(match).equals(npc)) { continue; } final ItemStack found = items.get(match); final int amount = found.getAmount(); final int toDeliver = ((BukkitStage) getCurrentStage(quest)).getItemsToDeliver().get(match).getAmount(); final ObjectiveType type = ObjectiveType.DELIVER_ITEM; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, amount, toDeliver)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newAmount = itemStack.getAmount() + amount; final Material m = itemStack.getType(); if (amount < toDeliver) { final int index = player.getInventory().first(itemStack); if (index == -1) { // Already delivered in previous loop return; } if (newAmount >= toDeliver) { found.setAmount(toDeliver); bukkitQuestData.itemsDelivered.set(items.indexOf(found), found.clone()); if ((itemStack.getAmount() + amount) >= toDeliver) { // Take away remaining amount to be delivered final ItemStack clone = itemStack.clone(); clone.setAmount(itemStack.getAmount() - (toDeliver - amount)); player.getInventory().setItem(index, clone); } else { player.getInventory().setItem(index, null); } player.updateInventory(); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(m, 1), found), null, null, null, null, null, null, null); } else { found.setAmount(newAmount); bukkitQuestData.itemsDelivered.set(items.indexOf(found), found.clone()); player.getInventory().setItem(index, null); player.updateInventory(); final String[] message = BukkitConfigUtil.parseStringWithPossibleLineBreaks(getCurrentStage(quest) .getDeliverMessages().get(new Random().nextInt(getCurrentStage(quest).getDeliverMessages() .size())), getCurrentStage(quest).getItemDeliveryTargets().get(items.indexOf(found)), toDeliver - newAmount); player.sendMessage(message); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, ObjectiveType.DELIVER_ITEM, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).itemsDelivered.set(items.indexOf(found), found.clone()); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newAmount, toDeliver)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Mark NPC as interacted with if Quester has such an objective * * @param quest The quest for which the NPC is being interacted with * @param npc UUID of the NPC being interacted with */ public void interactWithNPC(final Quest quest, final UUID npc) { if (!getCurrentStage(quest).getNpcsToInteract().contains(npc)) { return; } final int index = getCurrentStage(quest).getNpcsToInteract().indexOf(npc); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); final boolean npcsInteracted = bukkitQuestData.npcsInteracted.get(index); final ObjectiveType type = ObjectiveType.TALK_TO_NPC; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, 1, 1)); plugin.getServer().getPluginManager().callEvent(preEvent); if (!npcsInteracted) { bukkitQuestData.npcsInteracted.set(index, true); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, 1)), null, null, npc, null, null, null, null); dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).npcsInteracted.set(index, true); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, 1, 1)); plugin.getServer().getPluginManager().callEvent(postEvent); } } /** * Mark NPC as killed if the Quester has such an objective * * @param quest The quest for which the NPC is being killed * @param npc UUID of the NPC being killed */ public void killNPC(final Quest quest, final UUID npc) { if (!getCurrentStage(quest).getNpcsToKill().contains(npc)) { return; } final int index = getCurrentStage(quest).getNpcsToKill().indexOf(npc); final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); final int npcsKilled = bukkitQuestData.npcsNumKilled.get(index); final int npcsToKill = getCurrentStage(quest).getNpcNumToKill().get(index); final ObjectiveType type = ObjectiveType.KILL_NPC; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, npcsKilled, npcsToKill)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newNpcsKilled = bukkitQuestData.npcsNumKilled.get(index) + 1; if (npcsKilled < npcsToKill) { bukkitQuestData.npcsNumKilled.set(index, newNpcsKilled); if (newNpcsKilled >= npcsToKill) { finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, npcsToKill)), null, null, npc, null, null, null, null); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).npcsNumKilled.set(index, newNpcsKilled); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newNpcsKilled, npcsToKill)); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Marks cow as milked if Quester has such an objective * * @param quest The quest for which the fish is being caught */ public void milkCow(final Quest quest) { final BukkitQuestProgress questData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (questData == null) { return; } final Stage currentStage = getCurrentStage(quest); if (currentStage == null) { return; } if (currentStage.getCowsToMilk() == null) { return; } final int cowsMilked = questData.getCowsMilked(); final int cowsToMilk = currentStage.getCowsToMilk(); final ObjectiveType type = ObjectiveType.MILK_COW; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, cowsMilked, cowsToMilk)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newCowsMilked = cowsMilked + 1; if (cowsMilked < cowsToMilk) { questData.setCowsMilked(newCowsMilked); if (newCowsMilked >= cowsToMilk) { finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, cowsToMilk)), null, null, null, null, null, null, null); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { q.getQuestDataOrDefault(quest).setCowsMilked(newCowsMilked); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newCowsMilked, cowsToMilk)); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Marks fish as caught if Quester has such an objective * * @param quest The quest for which the fish is being caught */ public void catchFish(final Quest quest) { final BukkitQuestProgress questData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (questData == null) { return; } final Stage currentStage = getCurrentStage(quest); if (currentStage == null) { return; } if (currentStage.getFishToCatch() == null) { return; } final int fishCaught = questData.getFishCaught(); final int fishToCatch = currentStage.getFishToCatch(); final ObjectiveType type = ObjectiveType.CATCH_FISH; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, fishCaught, fishToCatch)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newFishCaught = fishCaught + 1; if (fishCaught < fishToCatch) { questData.setFishCaught(newFishCaught); if (newFishCaught >= fishToCatch) { finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, fishToCatch)), null, null, null, null, null, null, null); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { q.getQuestDataOrDefault(quest).setFishCaught(newFishCaught); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newFishCaught, fishToCatch)); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Mark mob as killed if Quester has such an objective * * @param quest The quest for which the mob is being killed * @param killedLocation The optional location to kill at * @param entityType The mob to be killed */ public void killMob(final Quest quest, final Location killedLocation, final EntityType entityType) { final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (entityType == null) { return; } final BukkitStage currentStage = ((BukkitStage) getCurrentStage(quest)); if (currentStage.getMobsToKill() == null) { return; } final int index = currentStage.getMobsToKill().indexOf(entityType); if (index == -1) { return; } final int mobsKilled = bukkitQuestData.mobNumKilled.get(index); final int mobsToKill = currentStage.getMobNumToKill().get(index); if (!currentStage.getLocationsToKillWithin().isEmpty()) { final Location locationToKillWithin = currentStage.getLocationsToKillWithin().get(index); final double radius = currentStage.getRadiiToKillWithin().get(index); if (killedLocation.getWorld() == null || locationToKillWithin.getWorld() == null) { return; } if (!(killedLocation.getWorld().getName().equals(locationToKillWithin.getWorld().getName()))) { return; } if (!(killedLocation.getX() < (locationToKillWithin.getX() + radius) && killedLocation.getX() > (locationToKillWithin.getX() - radius))) { return; } if (!(killedLocation.getZ() < (locationToKillWithin.getZ() + radius) && killedLocation.getZ() > (locationToKillWithin.getZ() - radius))) { return; } if (!(killedLocation.getY() < (locationToKillWithin.getY() + radius) && killedLocation.getY() > (locationToKillWithin.getY() - radius))) { return; } } final ObjectiveType type = ObjectiveType.KILL_MOB; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, mobsKilled, mobsToKill)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newMobsKilled = mobsKilled + 1; if (mobsKilled < mobsToKill) { bukkitQuestData.mobNumKilled.set(index, newMobsKilled); if (newMobsKilled >= mobsToKill) { finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, mobsToKill)), entityType, null, null, null, null, null, null); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { final int i = q.getCurrentStage(cq).getMobsToKill().indexOf(entityType); if (i == -1) { return null; } final int kills = q.getQuestDataOrDefault(quest).getMobNumKilled().get(i); ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).mobNumKilled.set(index, kills + 1); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newMobsKilled, mobsToKill)); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Mark player as killed if Quester has such an objective * * @param quest The quest for which the player is being killed * @param player The player to be killed */ public void killPlayer(final Quest quest, final Player player) { final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (bukkitQuestData == null) { return; } final Stage currentStage = getCurrentStage(quest); if (currentStage == null) { return; } if (currentStage.getPlayersToKill() == null) { return; } final int playersKilled = bukkitQuestData.getPlayersKilled(); final int playersToKill = currentStage.getPlayersToKill(); final ObjectiveType type = ObjectiveType.KILL_PLAYER; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, playersKilled, playersToKill)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newPlayersKilled = playersKilled + 1; if (playersKilled < playersToKill) { bukkitQuestData.setPlayersKilled(newPlayersKilled); if (newPlayersKilled >= playersToKill) { finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, playersToKill)), null, null, null, null, null, null, null); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { final int kills = q.getQuestDataOrDefault(quest).getPlayersKilled(); q.getQuestDataOrDefault(quest).setPlayersKilled(kills + 1); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newPlayersKilled, playersToKill)); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Mark location as reached if the Quester has such an objective * * @param quest The quest for which the location is being reached * @param location The location being reached */ public void reachLocation(final Quest quest, final Location location) { // TODO - redo this method final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (bukkitQuestData == null || bukkitQuestData.locationsReached == null || getCurrentStage(quest) == null || getCurrentStage(quest).getLocationsToReach() == null) { return; } int locationsReached = 0; for (final Boolean b : bukkitQuestData.locationsReached) { if (b) { locationsReached++; } } final int locationsToReach = getCurrentStage(quest).getLocationsToReach().size(); int index = 0; try { for (final Location toReach : ((BukkitStage) getCurrentStage(quest)).getLocationsToReach()) { if (location.getWorld() == null || toReach.getWorld() == null) { index++; continue; } if (!location.getWorld().getName().equals(toReach.getWorld().getName())) { index++; continue; } final double radius = getCurrentStage(quest).getRadiiToReachWithin().get(index); if (toReach.distanceSquared(location) <= radius * radius) { if (!bukkitQuestData.locationsReached.get(index)) { final ObjectiveType type = ObjectiveType.REACH_LOCATION; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, locationsReached, locationsToReach)); plugin.getServer().getPluginManager().callEvent(preEvent); bukkitQuestData.locationsReached.set(index, true); finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, 1)), null, null, null, toReach, null, null, null); int finalIndex = index; dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).locationsReached.set(finalIndex, true); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, locationsReached + 1, locationsToReach)); plugin.getServer().getPluginManager().callEvent(postEvent); break; } } index++; } } catch (final Exception e) { plugin.getLogger().severe("An error has occurred with Quests. Please report on Github with info below"); plugin.getLogger().warning("quest = " + quest.getId()); plugin.getLogger().warning("index = " + index); plugin.getLogger().warning("location = " + location.toString()); plugin.getLogger().warning("locationsToReach = " + getCurrentStage(quest).getLocationsToReach().size()); plugin.getLogger().warning("locationsReached = " + bukkitQuestData.locationsReached.size()); plugin.getLogger().warning("hasReached = " + bukkitQuestData.locationsReached.size()); e.printStackTrace(); } } /** * Mark mob as tamed if the Quester has such an objective * * @param quest The quest for which the mob is being tamed * @param entityType The type of mob being tamed */ public void tameMob(final Quest quest, final EntityType entityType) { final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (entityType == null) { return; } final Stage currentStage = getCurrentStage(quest); if (currentStage.getMobsToTame() == null) { return; } final int index = currentStage.getMobsToTame().indexOf(entityType); if (index == -1) { return; } final int mobsToTame = currentStage.getMobNumToTame().get(index); final int mobsTamed = bukkitQuestData.mobsTamed.get(index); final ObjectiveType type = ObjectiveType.TAME_MOB; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, mobsToTame, mobsTamed)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newMobsToTame = mobsTamed + 1; if (mobsTamed < mobsToTame) { bukkitQuestData.mobsTamed.set(index, newMobsToTame); if (newMobsToTame >= mobsToTame) { finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, mobsToTame)), entityType, null, null, null, null, null, null); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).mobsTamed.set(index, newMobsToTame); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newMobsToTame, mobsTamed)); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Mark sheep as sheared if the Quester has such an objective * * @param quest The quest for which the sheep is being sheared * @param color The wool color of the sheep being sheared */ public void shearSheep(final Quest quest, final DyeColor color) { final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (color == null) { return; } final Stage currentStage = getCurrentStage(quest); if (currentStage.getSheepToShear() == null) { return; } final int index = currentStage.getSheepToShear().indexOf(color); if (index == -1) { return; } final int sheepToShear = getCurrentStage(quest).getSheepNumToShear().get(index); final int sheepSheared = bukkitQuestData.sheepSheared.get(index); final ObjectiveType type = ObjectiveType.SHEAR_SHEEP; final Set dispatchedQuestIDs = new HashSet<>(); final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, sheepSheared, sheepToShear)); plugin.getServer().getPluginManager().callEvent(preEvent); final int newSheepSheared = sheepSheared + 1; if (sheepSheared < sheepToShear) { bukkitQuestData.sheepSheared.set(index, newSheepSheared); if (newSheepSheared >= sheepToShear) { finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, sheepToShear)), null, null, null, null, color, null, null); } dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).sheepSheared.set(index, newSheepSheared); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, newSheepSheared, sheepToShear)); plugin.getServer().getPluginManager().callEvent(postEvent); } /** * Mark password as entered if the Quester has such an objective * * @param quest The quest for which the password is being entered * @param evt The event during which the password was entered */ public void sayPassword(final Quest quest, final AsyncPlayerChatEvent evt) { final ObjectiveType type = ObjectiveType.PASSWORD; final Set dispatchedQuestIDs = new HashSet<>(); plugin.getServer().getScheduler().runTask(plugin, () -> { final BukkitQuesterPreUpdateObjectiveEvent preEvent = new BukkitQuesterPreUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, 1, 1)); plugin.getServer().getPluginManager().callEvent(preEvent); int index = 0; final BukkitQuestProgress bukkitQuestData = (BukkitQuestProgress) getQuestDataOrDefault(quest); for (final String pass : getCurrentStage(quest).getPasswordPhrases()) { if (pass.equalsIgnoreCase(evt.getMessage())) { final String display = getCurrentStage(quest).getPasswordDisplays().get(index); bukkitQuestData.passwordsSaid.set(index, true); plugin.getServer().getScheduler().runTask(plugin, () -> finishObjective(quest, new BukkitObjective(type, null, new ItemStack(Material.AIR, 1), new ItemStack(Material.AIR, 1)), null, null, null, null, null, display, null)); final int finalIndex = index; dispatchedQuestIDs.addAll(dispatchMultiplayerEverything(quest, type, (final Quester q, final Quest cq) -> { if (!dispatchedQuestIDs.contains(cq.getId())) { ((BukkitQuestProgress) q.getQuestDataOrDefault(quest)).passwordsSaid.set(finalIndex, true); if (q.testComplete(quest)) { quest.nextStage(q, false); } } return null; })); break; } index++; } final BukkitQuesterPostUpdateObjectiveEvent postEvent = new BukkitQuesterPostUpdateObjectiveEvent(this, quest, new BukkitObjective(type, null, 1, 1)); plugin.getServer().getPluginManager().callEvent(postEvent); }); } /** * Complete a quest objective * * @param quest * Quest containing the objective * @param objective * Objective for type, progress and goal * @param mob * Mob being killed or tamed, if any * @param extra * Extra mob enum like career or ocelot type, if any * @param npc * UUID of NPC being talked to or killed, if any * @param location * Location for user to reach, if any * @param color * Shear color, if any * @param pass * Password, if any * @param co * Custom objective, if any. See {@link CustomObjective} */ @SuppressWarnings("deprecation") public void finishObjective(final Quest quest, final Objective objective, final EntityType mob, final String extra, final UUID npc, final Location location, final DyeColor color, final String pass, final CustomObjective co) { if (objective == null) { return; } final Player p = getPlayer(); final ObjectiveType type = objective.getType(); final ItemStack increment = objective.getGoalObject() instanceof ItemStack ? (ItemStack) objective.getProgressObject() : new ItemStack(Material.AIR, objective.getProgress()); final ItemStack goal = objective.getGoalObject() instanceof ItemStack ? (ItemStack) objective.getGoalObject() : new ItemStack(Material.AIR, objective.getGoal()); if (!getCurrentStage(quest).getObjectiveOverrides().isEmpty()) { for (final String s: getCurrentStage(quest).getObjectiveOverrides()) { String message = ChatColor.GREEN + "(" + BukkitLang.get(p, "completed") + ") " + BukkitConfigUtil.parseString(ChatColor.translateAlternateColorCodes('&', s), quest, p); if (plugin.getDependencies().getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(p, message); } sendMessage(message); } } else if (type.equals(ObjectiveType.BREAK_BLOCK)) { final String message = formatCompletedObjectiveMessage("break", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, increment.getType(), increment.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else if (type.equals(ObjectiveType.DAMAGE_BLOCK)) { final String message = formatCompletedObjectiveMessage("damage", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, increment.getType(), increment.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else if (type.equals(ObjectiveType.PLACE_BLOCK)) { final String message = formatCompletedObjectiveMessage("place", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, increment.getType(), increment.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else if (type.equals(ObjectiveType.USE_BLOCK)) { final String message = formatCompletedObjectiveMessage("use", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, increment.getType(), increment.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else if (type.equals(ObjectiveType.CUT_BLOCK)) { final String message = formatCompletedObjectiveMessage("cut", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, increment.getType(), increment.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(increment))); } } else if (type.equals(ObjectiveType.CRAFT_ITEM)) { final ItemStack is = ((BukkitStage) getCurrentStage(quest)).getItemsToCraft().get(getCurrentStage(quest).getItemsToCraft() .indexOf(goal)); final String message = formatCompletedObjectiveMessage("craftItem", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, goal.getType(), goal.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else if (type.equals(ObjectiveType.SMELT_ITEM)) { final ItemStack is = ((BukkitStage) getCurrentStage(quest)).getItemsToSmelt().get(getCurrentStage(quest).getItemsToSmelt() .indexOf(goal)); final String message = formatCompletedObjectiveMessage("smeltItem", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, goal.getType(), goal.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else if (type.equals(ObjectiveType.ENCHANT_ITEM)) { final ItemStack is = ((BukkitStage) getCurrentStage(quest)).getItemsToEnchant().get(getCurrentStage(quest) .getItemsToEnchant().indexOf(goal)); final String message = formatCompletedObjectiveMessage("enchItem", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && is.hasItemMeta() && !is.getItemMeta().hasDisplayName()) { // Bukkit version is 1.9+ if (!plugin.getLocaleManager().sendMessage(p, message, goal.getType(), goal.getDurability(), goal.getEnchantments(), goal.getItemMeta())) { for (final Entry e : is.getEnchantments().entrySet()) { sendMessage(message.replace("", BukkitItemUtil.getName(is)) .replace("", BukkitItemUtil.getPrettyEnchantmentName(e.getKey())) .replace("", RomanNumeral.getNumeral(e.getValue()))); } } } else if (plugin.getConfigSettings().canTranslateNames() && !is.hasItemMeta() && Material.getMaterial("LINGERING_POTION") == null) { // Bukkit version is below 1.9 if (!plugin.getLocaleManager().sendMessage(p, message, goal.getType(), goal.getDurability(), goal.getEnchantments())) { for (final Entry e : is.getEnchantments().entrySet()) { sendMessage(message.replace("", BukkitItemUtil.getName(is)) .replace("", BukkitItemUtil.getPrettyEnchantmentName(e.getKey())) .replace("", RomanNumeral.getNumeral(e.getValue()))); } } } else { for (final Entry e : is.getEnchantments().entrySet()) { sendMessage(message.replace("", BukkitItemUtil.getName(is)) .replace("", BukkitItemUtil.getPrettyEnchantmentName(e.getKey())) .replace("", RomanNumeral.getNumeral(e.getValue()))); } } } else if (type.equals(ObjectiveType.BREW_ITEM)) { final ItemStack is = ((BukkitStage) getCurrentStage(quest)).getItemsToBrew().get(getCurrentStage(quest).getItemsToBrew() .indexOf(goal)); final String message = formatCompletedObjectiveMessage("brewItem", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && is.hasItemMeta() && !is.getItemMeta().hasDisplayName()) { // Bukkit version is 1.9+ if (!plugin.getLocaleManager().sendMessage(p, message, goal.getType(), goal.getDurability(), goal.getEnchantments(), goal.getItemMeta())) { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else if (plugin.getConfigSettings().canTranslateNames() && !is.hasItemMeta() && Material.getMaterial("LINGERING_POTION") == null) { // Bukkit version is below 1.9 if (!plugin.getLocaleManager().sendMessage(p, message, goal.getType(), goal.getDurability(), goal.getEnchantments())) { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else if (type.equals(ObjectiveType.CONSUME_ITEM)) { final ItemStack is = ((BukkitStage) getCurrentStage(quest)).getItemsToConsume().get(getCurrentStage(quest) .getItemsToConsume().indexOf(goal)); final String message = formatCompletedObjectiveMessage("consumeItem", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, goal.getType(), goal.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else if (type.equals(ObjectiveType.DELIVER_ITEM)) { final ItemStack is = ((BukkitStage) getCurrentStage(quest)).getItemsToDeliver().get(getCurrentStage(quest) .getItemsToDeliver().indexOf(goal)); final String message = formatCompletedObjectiveMessage("deliver", goal.getAmount()) .replace("", plugin.getDependencies().getNpcName(getCurrentStage(quest) .getItemDeliveryTargets().get(getCurrentStage(quest).getItemsToDeliver().indexOf(goal)))); if (plugin.getConfigSettings().canTranslateNames() && !goal.hasItemMeta() && !goal.getItemMeta().hasDisplayName()) { if (!plugin.getLocaleManager().sendMessage(p, message, is.getType(), is.getDurability(), null)) { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else { sendMessage(message.replace("", BukkitItemUtil.getName(is))); } } else if (type.equals(ObjectiveType.MILK_COW)) { final String message = formatCompletedObjectiveMessage("milkCow", goal.getAmount()); sendMessage(message); } else if (type.equals(ObjectiveType.CATCH_FISH)) { final String message = formatCompletedObjectiveMessage("catchFish", goal.getAmount()); sendMessage(message); } else if (type.equals(ObjectiveType.KILL_MOB)) { final String message = formatCompletedObjectiveMessage("kill", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames()) { if (!plugin.getLocaleManager().sendMessage(p, message, mob, extra)) { sendMessage(message.replace("", BukkitMiscUtil.snakeCaseToUpperCamelCase(mob.name()))); } } else { sendMessage(message.replace("", BukkitMiscUtil.snakeCaseToUpperCamelCase(mob.name()))); } } else if (type.equals(ObjectiveType.KILL_PLAYER)) { final String message = formatCompletedObjectiveMessage("killPlayer", goal.getAmount()); sendMessage(message); } else if (type.equals(ObjectiveType.TALK_TO_NPC)) { final String message = ChatColor.GREEN + "(" + BukkitLang.get(p, "completed") + ") " + BukkitLang.get(p, "talkTo").replace("", plugin.getDependencies().getNpcName(npc)); sendMessage(message); } else if (type.equals(ObjectiveType.KILL_NPC)) { final String message = formatCompletedObjectiveMessage("kill", goal.getAmount()); sendMessage(message.replace("", plugin.getDependencies().getNpcName(npc))); } else if (type.equals(ObjectiveType.TAME_MOB)) { final String message = formatCompletedObjectiveMessage("tame", goal.getAmount()); if (plugin.getConfigSettings().canTranslateNames()) { if (!plugin.getLocaleManager().sendMessage(p, message, mob, extra)) { sendMessage(message.replace("", BukkitMiscUtil.snakeCaseToUpperCamelCase(mob.name()))); } } else { sendMessage(message.replace("", BukkitMiscUtil.snakeCaseToUpperCamelCase(mob.name()))); } } else if (type.equals(ObjectiveType.SHEAR_SHEEP)) { final String message = formatCompletedObjectiveMessage("shearSheep", goal.getAmount()) .replace("", BukkitMiscUtil.getPrettyDyeColorName(color)); sendMessage(message); } else if (type.equals(ObjectiveType.REACH_LOCATION)) { String obj = BukkitLang.get(p, "goTo"); try { obj = obj.replace("", getCurrentStage(quest).getLocationNames().get(getCurrentStage(quest) .getLocationsToReach().indexOf(location))); } catch (final IndexOutOfBoundsException e) { plugin.getLogger().severe("Unable to get final location " + location + " for quest ID " + quest.getId() + ", please report on Github"); obj = obj.replace("", "ERROR"); } final String message = ChatColor.GREEN + "(" + BukkitLang.get(p, "completed") + ") " + obj; sendMessage(message); } else if (type.equals(ObjectiveType.PASSWORD)) { sendMessage(ChatColor.GREEN + "(" + BukkitLang.get(p, "completed") + ") " + pass); } else if (co != null) { String message = ChatColor.GREEN + "(" + BukkitLang.get(p, "completed") + ") " + co.getDisplay(); int index = -1; for (int i = 0; i < getCurrentStage(quest).getCustomObjectives().size(); i++) { if (getCurrentStage(quest).getCustomObjectives().get(i).getName().equals(co.getName())) { index = i; break; } } final List> sub = new LinkedList<>(getCurrentStage(quest).getCustomObjectiveData() .subList(index, getCurrentStage(quest).getCustomObjectiveData().size())); final List> end = new LinkedList<>(sub); sub.clear(); // Since sub is backed by end, this removes all sub-list items from end for (final Entry dataMap : end) { message = message.replace("%" + (dataMap.getKey()) + "%", String.valueOf(dataMap.getValue())); } if (co.canShowCount()) { message = message.replace("%count%", goal.getAmount() + "/" + goal.getAmount()); } sendMessage(BukkitConfigUtil.parseString(ChatColor.translateAlternateColorCodes('&', message))); } dispatchMultiplayerObjectives(quest, getCurrentStage(quest), (final Quester q) -> { q.finishObjective(quest, objective, mob, extra, npc, location, color, pass, co); return null; }); if (testComplete(quest)) { quest.nextStage(this, true); } } /** * Format completed objective message with count and placeholders * * @param langKey Label as it appears in lang file * @param goal Objective goal * @return Formatted message */ private String formatCompletedObjectiveMessage(final String langKey, final int goal) { String message = ChatColor.GREEN + "(" + BukkitLang.get("completed") + ") " + BukkitLang.get(langKey); if (message.contains("")) { message = message.replace("", "" + ChatColor.GREEN + goal + "/" + goal); } if (plugin.getDependencies().getPlaceholderApi() != null) { message = PlaceholderAPI.setPlaceholders(getPlayer(), message); } return message; } /** * Check whether this Quester has completed all objectives for their current stage * * @param quest The quest with the current stage being checked * @return true if all stage objectives are marked complete */ public boolean testComplete(final Quest quest) { for (final Objective objective : getCurrentObjectives(quest, true, false)) { if (objective.getProgress() < objective.getGoal()) { return false; } } return true; } /** * Add empty map values per Quest stage * * @param quest Quest with at least one stage * @param stage Where first stage is '0' */ @SuppressWarnings("deprecation") public void addEmptiesFor(final Quest quest, final int stage) { final BukkitQuestProgress data = new BukkitQuestProgress(this); data.setDoJournalUpdate(false); if (quest == null) { plugin.getLogger().warning("Unable to find quest for player " + this.lastKnownName); return; } if (quest.getStage(stage) == null) { plugin.getLogger().severe("Unable to find Stage " + stage + " of quest ID " + quest.getId()); return; } final BukkitStage bukkitStage = (BukkitStage) quest.getStage(stage); if (!bukkitStage.getBlocksToBreak().isEmpty()) { for (final ItemStack toBreak : bukkitStage.getBlocksToBreak()) { final ItemStack temp = new ItemStack(toBreak.getType(), 0, toBreak.getDurability()); if (data.blocksBroken.contains(toBreak)) { data.blocksBroken.set(data.blocksBroken.indexOf(temp), temp); } else { data.blocksBroken.add(temp); } } } if (!bukkitStage.getBlocksToDamage().isEmpty()) { for (final ItemStack toDamage : bukkitStage.getBlocksToDamage()) { final ItemStack temp = new ItemStack(toDamage.getType(), 0, toDamage.getDurability()); if (data.blocksDamaged.contains(toDamage)) { data.blocksDamaged.set(data.blocksDamaged.indexOf(temp), temp); } else { data.blocksDamaged.add(temp); } } } if (!bukkitStage.getBlocksToPlace().isEmpty()) { for (final ItemStack toPlace : bukkitStage.getBlocksToPlace()) { final ItemStack temp = new ItemStack(toPlace.getType(), 0, toPlace.getDurability()); if (data.blocksPlaced.contains(toPlace)) { data.blocksPlaced.set(data.blocksPlaced.indexOf(temp), temp); } else { data.blocksPlaced.add(temp); } } } if (!bukkitStage.getBlocksToUse().isEmpty()) { for (final ItemStack toUse : bukkitStage.getBlocksToUse()) { final ItemStack temp = new ItemStack(toUse.getType(), 0, toUse.getDurability()); if (data.blocksUsed.contains(toUse)) { data.blocksUsed.set(data.blocksUsed.indexOf(temp), temp); } else { data.blocksUsed.add(temp); } } } if (!bukkitStage.getBlocksToCut().isEmpty()) { for (final ItemStack toCut : bukkitStage.getBlocksToCut()) { final ItemStack temp = new ItemStack(toCut.getType(), 0, toCut.getDurability()); if (data.blocksCut.contains(toCut)) { data.blocksCut.set(data.blocksCut.indexOf(temp), temp); } else { data.blocksCut.add(temp); } } } if (!bukkitStage.getItemsToCraft().isEmpty()) { for (final ItemStack toCraft : bukkitStage.getItemsToCraft()) { final ItemStack temp = new ItemStack(toCraft.getType(), 0, toCraft.getDurability()); temp.addUnsafeEnchantments(toCraft.getEnchantments()); temp.setItemMeta(toCraft.getItemMeta()); data.itemsCrafted.add(temp); } } if (!bukkitStage.getItemsToSmelt().isEmpty()) { for (final ItemStack toSmelt : bukkitStage.getItemsToSmelt()) { final ItemStack temp = new ItemStack(toSmelt.getType(), 0, toSmelt.getDurability()); temp.addUnsafeEnchantments(toSmelt.getEnchantments()); temp.setItemMeta(toSmelt.getItemMeta()); data.itemsSmelted.add(temp); } } if (!bukkitStage.getItemsToEnchant().isEmpty()) { for (final ItemStack toEnchant : bukkitStage.getItemsToEnchant()) { final ItemStack temp = new ItemStack(toEnchant.getType(), 0, toEnchant.getDurability()); temp.addUnsafeEnchantments(toEnchant.getEnchantments()); temp.setItemMeta(toEnchant.getItemMeta()); data.itemsEnchanted.add(temp); } } if (!bukkitStage.getItemsToBrew().isEmpty()) { for (final ItemStack toBrew : bukkitStage.getItemsToBrew()) { final ItemStack temp = new ItemStack(toBrew.getType(), 0, toBrew.getDurability()); temp.addUnsafeEnchantments(toBrew.getEnchantments()); temp.setItemMeta(toBrew.getItemMeta()); data.itemsBrewed.add(temp); } } if (!bukkitStage.getItemsToConsume().isEmpty()) { for (final ItemStack toConsume : bukkitStage.getItemsToConsume()) { final ItemStack temp = new ItemStack(toConsume.getType(), 0, toConsume.getDurability()); temp.addUnsafeEnchantments(toConsume.getEnchantments()); temp.setItemMeta(toConsume.getItemMeta()); data.itemsConsumed.add(temp); } } if (!bukkitStage.getItemsToDeliver().isEmpty()) { for (final ItemStack toDeliver : bukkitStage.getItemsToDeliver()) { final ItemStack temp = new ItemStack(toDeliver.getType(), 0, toDeliver.getDurability()); temp.addUnsafeEnchantments(toDeliver.getEnchantments()); temp.setItemMeta(toDeliver.getItemMeta()); data.itemsDelivered.add(temp); } } if (!quest.getStage(stage).getNpcsToInteract().isEmpty()) { for (final UUID ignored : quest.getStage(stage).getNpcsToInteract()) { data.npcsInteracted.add(false); } } if (!quest.getStage(stage).getNpcsToKill().isEmpty()) { for (final UUID ignored : quest.getStage(stage).getNpcsToKill()) { data.npcsNumKilled.add(0); } } if (!quest.getStage(stage).getMobsToKill().isEmpty()) { for (final EntityType ignored : bukkitStage.getMobsToKill()) { data.mobNumKilled.add(0); } } data.setCowsMilked(0); data.setFishCaught(0); data.setPlayersKilled(0); if (!quest.getStage(stage).getLocationsToReach().isEmpty()) { for (final Location ignored : bukkitStage.getLocationsToReach()) { data.locationsReached.add(false); } } if (!quest.getStage(stage).getMobsToTame().isEmpty()) { for (final EntityType ignored : bukkitStage.getMobsToTame()) { data.mobsTamed.add(0); } } if (!quest.getStage(stage).getSheepToShear().isEmpty()) { for (final DyeColor ignored : bukkitStage.getSheepToShear()) { data.sheepSheared.add(0); } } if (!quest.getStage(stage).getPasswordDisplays().isEmpty()) { for (final String ignored : bukkitStage.getPasswordDisplays()) { data.passwordsSaid.add(false); } } if (!quest.getStage(stage).getCustomObjectives().isEmpty()) { for (final CustomObjective ignored : quest.getStage(stage).getCustomObjectives()) { data.customObjectiveCounts.add(0); } } data.setDoJournalUpdate(true); hardDataPut(quest, data); } /** * Save data of the Quester to file * * @return true if successful */ public boolean saveData() { try { plugin.getStorage().saveQuester(this).get(); } catch (final Exception e) { return false; } return true; } /** * Get the difference between System.currentTimeMillis() and the last completed time for a quest * * @param quest The quest to get the last completed time of * @return Difference between now and then in milliseconds */ public long getCompletionDifference(final Quest quest) { final long currentTime = System.currentTimeMillis(); final long lastTime; if (!completedTimes.containsKey(quest)) { lastTime = System.currentTimeMillis(); completedTimes.put(quest, System.currentTimeMillis()); } else { lastTime = completedTimes.get(quest); } return currentTime - lastTime; } /** * Get the amount of time left before Quester may take a completed quest again * * @param quest The quest to calculate the cooldown for * @return Length of time in milliseconds */ public long getRemainingCooldown(final Quest quest) { return quest.getPlanner().getCooldown() - getCompletionDifference(quest); } public FileConfiguration getBaseData() { final FileConfiguration data = new YamlConfiguration(); if (!currentQuests.isEmpty()) { final ArrayList questIds = new ArrayList<>(); final ArrayList questStages = new ArrayList<>(); for (final Quest quest : currentQuests.keySet()) { questIds.add(quest.getId()); questStages.add(currentQuests.get(quest)); } data.set("currentQuests", questIds); data.set("currentStages", questStages); data.set("quest-points", questPoints); final ConfigurationSection dataSec = data.createSection("questData"); for (final Quest quest : currentQuests.keySet()) { if (quest.getName() == null || quest.getName().isEmpty()) { plugin.getLogger().severe("Quest name was null or empty while loading data"); return null; } final ConfigurationSection questSec = dataSec.createSection(quest.getId()); final BukkitQuestProgress questData = (BukkitQuestProgress) getQuestDataOrDefault(quest); if (questData == null) { continue; } if (!questData.blocksBroken.isEmpty()) { final LinkedList blockAmounts = new LinkedList<>(); for (final ItemStack m : questData.blocksBroken) { blockAmounts.add(m.getAmount()); } questSec.set("blocks-broken-amounts", blockAmounts); } if (!questData.blocksDamaged.isEmpty()) { final LinkedList blockAmounts = new LinkedList<>(); for (final ItemStack m : questData.blocksDamaged) { blockAmounts.add(m.getAmount()); } questSec.set("blocks-damaged-amounts", blockAmounts); } if (!questData.blocksPlaced.isEmpty()) { final LinkedList blockAmounts = new LinkedList<>(); for (final ItemStack m : questData.blocksPlaced) { blockAmounts.add(m.getAmount()); } questSec.set("blocks-placed-amounts", blockAmounts); } if (!questData.blocksUsed.isEmpty()) { final LinkedList blockAmounts = new LinkedList<>(); for (final ItemStack m : questData.blocksUsed) { blockAmounts.add(m.getAmount()); } questSec.set("blocks-used-amounts", blockAmounts); } if (!questData.blocksCut.isEmpty()) { final LinkedList blockAmounts = new LinkedList<>(); for (final ItemStack m : questData.blocksCut) { blockAmounts.add(m.getAmount()); } questSec.set("blocks-cut-amounts", blockAmounts); } if (!questData.itemsCrafted.isEmpty()) { final LinkedList craftAmounts = new LinkedList<>(); for (final ItemStack m : questData.itemsCrafted) { craftAmounts.add(m.getAmount()); } questSec.set("item-craft-amounts", craftAmounts); } if (!questData.itemsSmelted.isEmpty()) { final LinkedList smeltAmounts = new LinkedList<>(); for (final ItemStack m : questData.itemsSmelted) { smeltAmounts.add(m.getAmount()); } questSec.set("item-smelt-amounts", smeltAmounts); } if (!questData.itemsEnchanted.isEmpty()) { final LinkedList enchantAmounts = new LinkedList<>(); for (final ItemStack m : questData.itemsEnchanted) { enchantAmounts.add(m.getAmount()); } questSec.set("item-enchant-amounts", enchantAmounts); } if (!questData.itemsBrewed.isEmpty()) { final LinkedList brewAmounts = new LinkedList<>(); for (final ItemStack m : questData.itemsBrewed) { brewAmounts.add(m.getAmount()); } questSec.set("item-brew-amounts", brewAmounts); } if (!questData.itemsConsumed.isEmpty()) { final LinkedList consumeAmounts = new LinkedList<>(); for (final ItemStack m : questData.itemsConsumed) { consumeAmounts.add(m.getAmount()); } questSec.set("item-consume-amounts", consumeAmounts); } if (!questData.itemsDelivered.isEmpty()) { final LinkedList deliveryAmounts = new LinkedList<>(); for (final ItemStack m : questData.itemsDelivered) { deliveryAmounts.add(m.getAmount()); } questSec.set("item-delivery-amounts", deliveryAmounts); } if (!questData.npcsInteracted.isEmpty()) { questSec.set("has-talked-to", questData.npcsInteracted); } if (!questData.npcsNumKilled.isEmpty()) { questSec.set("npc-killed-amounts", questData.npcsNumKilled); } if (!questData.mobNumKilled.isEmpty()) { questSec.set("mobs-killed-amounts", questData.mobNumKilled); } if (!questData.mobsTamed.isEmpty()) { questSec.set("mob-tame-amounts", questData.mobsTamed); } final Stage stage = getCurrentStage(quest); if (stage != null) { if (stage.getFishToCatch() != null) { questSec.set("fish-caught", questData.getFishCaught()); } if (stage.getCowsToMilk() != null) { questSec.set("cows-milked", questData.getCowsMilked()); } if (stage.getPlayersToKill() != null) { questSec.set("players-killed", questData.getPlayersKilled()); } } if (!questData.sheepSheared.isEmpty()) { questSec.set("sheep-sheared", questData.sheepSheared); } if (!questData.locationsReached.isEmpty()) { questSec.set("has-reached-location", questData.locationsReached); } if (!questData.passwordsSaid.isEmpty()) { questSec.set("passwords-said", questData.passwordsSaid); } if (!questData.customObjectiveCounts.isEmpty()) { questSec.set("custom-objective-counts", questData.customObjectiveCounts); } if (questData.getDelayTimeLeft() > 0) { questSec.set("stage-delay", questData.getDelayTimeLeft()); } } } else { data.set("currentQuests", "none"); data.set("currentStages", "none"); data.set("quest-points", questPoints); } if (completedQuests.isEmpty()) { data.set("completed-Quests", "none"); } else { final List questIds = new LinkedList<>(); for (final Quest quest : completedQuests) { questIds.add(quest.getId()); } data.set("completed-Quests", questIds); } if (!completedTimes.isEmpty()) { final List questIds = new LinkedList<>(); final List questTimes = new LinkedList<>(); for (final Entry entry : completedTimes.entrySet()) { questIds.add(entry.getKey().getId()); questTimes.add(entry.getValue()); } data.set("completedRedoableQuests", questIds); data.set("completedQuestTimes", questTimes); } if (!amountsCompleted.isEmpty()) { final List questIds = new LinkedList<>(); final List questAmounts = new LinkedList<>(); for (final Entry entry : amountsCompleted.entrySet()) { questIds.add(entry.getKey().getId()); questAmounts.add(entry.getValue()); } data.set("amountsCompletedQuests", questIds); data.set("amountsCompleted", questAmounts); } // #getPlayer is faster OfflinePlayer representedPlayer = getPlayer(); if (representedPlayer == null) { representedPlayer = getOfflinePlayer(); } data.set("lastKnownName", representedPlayer.getName()); return data; } /** * Load data of the Quester from storage * * @deprecated Use {@link #hasData()} * @return true if successful */ @Deprecated public boolean loadData() { return plugin.getStorage().loadQuester(id) != null; } /** * Check whether the Quester has data saved to hard storage * * @return true if successful */ public boolean hasData() { return plugin.getStorage().loadQuester(id) != null; } /** * Check whether the Quester has base data in memory, indicating they have participated in quests * * @return false if empty */ public boolean hasBaseData() { return !currentQuests.isEmpty() || !questProgress.isEmpty() || !completedQuests.isEmpty(); } /** * Initiate the stage timer * @param quest The quest of which the timer is for */ public void startStageTimer(final Quest quest) { if (getQuestDataOrDefault(quest).getDelayTimeLeft() > -1) { plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new BukkitStageTimer(plugin, this, quest), (long) (getQuestDataOrDefault(quest).getDelayTimeLeft() * 0.02)); } else { plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new BukkitStageTimer(plugin, this, quest), (long) (getCurrentStage(quest).getDelay() * 0.02)); if (getCurrentStage(quest).getDelayMessage() != null) { final Player p = plugin.getServer().getPlayer(id); if (p != null) { p.sendMessage(BukkitConfigUtil.parseStringWithPossibleLineBreaks((getCurrentStage(quest) .getDelayMessage()), quest, p)); } } } getQuestDataOrDefault(quest).setDelayStartTime(System.currentTimeMillis()); } /** * Pause the stage timer. Useful when a player quits * @param quest The quest of which the timer is for */ public void stopStageTimer(final Quest quest) { if (getQuestDataOrDefault(quest).getDelayTimeLeft() > -1) { getQuestDataOrDefault(quest).setDelayTimeLeft(getQuestDataOrDefault(quest).getDelayTimeLeft() - (System.currentTimeMillis() - getQuestDataOrDefault(quest).getDelayStartTime())); } else { getQuestDataOrDefault(quest).setDelayTimeLeft(getCurrentStage(quest).getDelay() - (System.currentTimeMillis() - getQuestDataOrDefault(quest).getDelayStartTime())); } } /** * Get remaining stage delay time * @param quest The quest of which the timer is for * @return Remaining time in milliseconds */ public long getStageTime(final Quest quest) { if (getQuestDataOrDefault(quest).getDelayTimeLeft() > -1) { return getQuestDataOrDefault(quest).getDelayTimeLeft() - (System.currentTimeMillis() - getQuestDataOrDefault(quest).getDelayStartTime()); } else { return getCurrentStage(quest).getDelay() - (System.currentTimeMillis() - getQuestDataOrDefault(quest).getDelayStartTime()); } } /** * Check whether the provided quest is valid and, if not, inform the Quester * * @param quest The quest to check */ public void checkQuest(final Quest quest) { if (quest != null) { boolean exists = false; for (final Quest q : plugin.getLoadedQuests()) { if (q.getId().equalsIgnoreCase(quest.getId())) { final Stage stage = getCurrentStage(quest); if (stage != null) { quest.updateCompass(this, stage); // TODO - decide whether or not to handle this /*if (q.equals(quest) == false) { if (getPlayer() != null && getPlayer().isOnline()) { quitQuest(quest, ChatColor.GOLD + Lang.get("questModified") .replace("", ChatColor.DARK_PURPLE + quest.getName() + ChatColor.GOLD)); } }*/ } exists = true; break; } } if (!exists) { sendMessage(ChatColor.RED + BukkitLang.get("questNotExist").replace("", ChatColor.DARK_PURPLE + quest.getName() + ChatColor.RED)); } } } /** * Show an inventory GUI with quest items to the specified player * * @param npc UUID of the NPC from which the GUI is bound * @param quests List of quests to use for displaying items */ public void showGUIDisplay(final UUID npc, final LinkedList quests) { if (npc == null || quests == null) { return; } if (plugin.getDependencies().getCitizens() == null) { return; } final String name = plugin.getDependencies().getNpcName(npc); final LinkedList qs = new LinkedList<>(); for (Quest q : quests) { qs.add((BukkitQuest) q); } final BukkitQuesterPreOpenGUIEvent preEvent = new BukkitQuesterPreOpenGUIEvent(this, npc, qs); plugin.getServer().getPluginManager().callEvent(preEvent); if (preEvent.isCancelled()) { return; } final Player player = getPlayer(); final Inventory inv = plugin.getServer().createInventory(player, ((quests.size() / 9) + 1) * 9, BukkitLang.get(player, "quests") + " | " + name); int i = 0; for (final Quest quest : quests) { final BukkitQuest bukkitQuest = (BukkitQuest)quest; if (bukkitQuest.getGUIDisplay() != null) { if (i > 53) { // Protocol-enforced size limit has been exceeded break; } inv.setItem(i, bukkitQuest.prepareDisplay(this)); i++; } } player.openInventory(inv); } /** * Force Quester to quit the specified quest (canceling any timers), then update Quest Journal

* * Does not save changes to disk. Consider {@link #quitQuest(Quest, String)} or {@link #quitQuest(Quest, String[])} * * @param quest The quest to quit */ public void hardQuit(final Quest quest) { try { if (compassTargetQuestId != null && compassTargetQuestId.equals(quest.getId())) { compassTargetQuestId = null; } currentQuests.remove(quest); questProgress.remove(quest); if (!timers.isEmpty()) { for (final Map.Entry entry : timers.entrySet()) { if (entry.getValue().getName().equals(quest.getName())) { plugin.getServer().getScheduler().cancelTask(entry.getKey()); timers.remove(entry.getKey()); } } } } catch (final Exception ex) { ex.printStackTrace(); } } /** * Forcibly remove quest from Quester's list of completed quests, then update Quest Journal

* * Does not save changes to disk. Consider calling {@link #saveData()} followed by {@link #loadData()} * * @param quest The quest to remove */ public void hardRemove(final Quest quest) { try { completedQuests.remove(quest); } catch (final Exception ex) { ex.printStackTrace(); } } /** * Forcibly clear Quester's list of current quests and data, then update Quest Journal

* * Does not save changes to disk. Consider calling {@link #saveData()} followed by {@link #loadData()} */ public void hardClear() { try { currentQuests.clear(); questProgress.clear(); amountsCompleted.clear(); } catch (final Exception ex) { ex.printStackTrace(); } } /** * Forcibly set Quester's current stage, then update Quest Journal
* Does not save changes to disk. Consider calling {@link #saveData()} followed by {@link #loadData()} * * @param key The quest to set stage of * @param val The stage number to set */ public void hardStagePut(final Quest key, final Integer val) { try { currentQuests.put(key, val); } catch (final Exception ex) { ex.printStackTrace(); } } /** * Forcibly set Quester's quest data, then update Quest Journal

* * Does not save changes to disk. Consider calling {@link #saveData()} followed by {@link #loadData()} * * @param key The quest to set stage of * @param val The data to set */ public void hardDataPut(final Quest key, final QuestProgress val) { try { questProgress.put(key, (BukkitQuestProgress) val); } catch (final Exception ex) { ex.printStackTrace(); } } public boolean canUseCompass() { if (getPlayer() != null) { if (!getPlayer().hasPermission("worldedit.navigation.jumpto")) { return getPlayer().hasPermission("quests.compass"); } } return false; } /** * Reset compass target to Quester's bed spawn location

* * Will set to Quester's spawn location if bed spawn does not exist */ public void resetCompass() { compassTargetQuestId = null; final Player player = getPlayer(); if (player == null) { return; } if (!canUseCompass()) { return; } Location defaultLocation = player.getBedSpawnLocation(); if (defaultLocation == null) { defaultLocation = player.getWorld().getSpawnLocation(); } player.setCompassTarget(defaultLocation); } /** * Update compass target to current stage of first available current quest, if possible */ public void findCompassTarget() { // Here we apply this method to OPs by not checking #canUseCompass if (getPlayer() == null || !getPlayer().hasPermission("quests.compass")) { return; } for (final Quest quest : currentQuests.keySet()) { final Stage stage = getCurrentStage(quest); if (stage != null) { if (stage.hasLocatableObjective()) { quest.updateCompass(this, stage); } else { resetCompass(); setCompassTarget(quest); } break; } } } /** * Update compass target to current stage of next available current quest, if possible * * @param notify Whether to notify this quester of result */ public void findNextCompassTarget(final boolean notify) { // Here we apply this method to OPs by not checking #canUseCompass if (getPlayer() == null || !getPlayer().hasPermission("quests.compass")) { return; } Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { final LinkedList list = currentQuests.keySet().stream() .sorted(Comparator.comparing(Quest::getName)).map(Quest::getId) .collect(Collectors.toCollection(LinkedList::new)); int index = 0; if (compassTargetQuestId != null) { if (!list.contains(compassTargetQuestId) && notify) { return; } index = list.indexOf(compassTargetQuestId) + 1; if (index >= list.size()) { index = 0; } } if (list.size() > 0) { final Quest quest = plugin.getQuestById(list.get(index)); compassTargetQuestId = quest.getId(); final Stage stage = getCurrentStage(quest); if (stage != null) { if (stage.hasLocatableObjective()) { quest.updateCompass(BukkitQuester.this, stage); if (notify) { sendMessage(ChatColor.YELLOW + BukkitLang.get(getPlayer(), "compassSet") .replace("", ChatColor.GOLD + quest.getName() + ChatColor.YELLOW)); } } else { resetCompass(); setCompassTarget(quest); if (notify) { sendMessage(ChatColor.RED + BukkitLang.get(getPlayer(), "compassNone") .replace("", ChatColor.GRAY + quest.getName() + ChatColor.RED)); } } } } else { sendMessage(ChatColor.RED + BukkitLang.get(getPlayer(), "journalNoQuests") .replace("", BukkitLang.get(getPlayer(), "journalTitle"))); } }); } /** * Check whether the Quester's inventory contains the specified item * * @param is The item with a specified amount to check * @return true if the inventory contains at least the amount of the specified stack */ public boolean hasItem(final ItemStack is) { final Inventory inv = getPlayer().getInventory(); int playerAmount = 0; for (final ItemStack stack : inv.getContents()) { if (stack != null) { if (BukkitItemUtil.compareItems(is, stack, false) == 0) { playerAmount += stack.getAmount(); } } } return playerAmount >= is.getAmount(); } /** * Dispatch player event to fellow questers

* * @param type The type of objective to progress * @param fun The function to execute, the event call */ public Set dispatchMultiplayerEverything(final Quest quest, final ObjectiveType type, final BiFunction fun) { final Set appliedQuestIDs = new HashSet<>(); if (quest != null) { try { if (quest.getOptions().getShareProgressLevel() == 1) { final List mq = getMultiplayerQuesters(quest); if (mq == null) { return appliedQuestIDs; } for (final Quester q : mq) { if (q == null) { continue; } if (quest.getOptions().canShareSameQuestOnly()) { if (q.getCurrentStage(quest) != null) { fun.apply(q, quest); appliedQuestIDs.add(quest.getId()); } } q.getCurrentQuests().forEach((otherQuest, i) -> { if (otherQuest.getStage(i).containsObjective(type)) { if (!otherQuest.getOptions().canShareSameQuestOnly()) { fun.apply(q, otherQuest); appliedQuestIDs.add(otherQuest.getId()); } } }); } } } catch (final Exception e) { plugin.getLogger().severe("Error occurred while dispatching " + type.name() + " for quest ID " + quest.getId()); e.printStackTrace(); } } return appliedQuestIDs; } /** * Dispatch finish objective to fellow questers * * @param quest The current quest * @param currentStage The current stage of the quest * @param fun The function to execute, the event call */ public Set dispatchMultiplayerObjectives(final Quest quest, final Stage currentStage, final Function fun) { final Set appliedQuestIDs = new HashSet<>(); if (quest.getOptions().getShareProgressLevel() == 2) { final List mq = getMultiplayerQuesters(quest); if (mq == null) { return appliedQuestIDs; } for (final Quester q : mq) { if (q == null) { continue; } if ((q.getCurrentQuests().containsKey(quest) && currentStage.equals(q.getCurrentStage(quest)))) { fun.apply(q); } } } return appliedQuestIDs; } /** * Get a list of fellow Questers in a party or group * * @param quest The quest which uses a linked plugin, i.e. Parties or DungeonsXL * @return Potentially empty list of Questers or null for invalid quest */ public List getMultiplayerQuesters(final Quest quest) { if (quest == null) { return null; } final List mq = new LinkedList<>(); if (plugin.getDependencies().getPartyProvider() != null) { final PartyProvider partyProvider = plugin.getDependencies().getPartyProvider(); if (partyProvider != null) { if (quest.getOptions().canUsePartiesPlugin() || quest.getOptions().getExternalPartyPlugin() != null) { if (getUUID() != null && partyProvider.getPartyId(getUUID()) != null) { final String partyId = partyProvider.getPartyId(getUUID()); final double distanceSquared = quest.getOptions().getShareDistance() * quest.getOptions().getShareDistance(); final boolean offlinePlayers = quest.getOptions().canHandleOfflinePlayers(); if (offlinePlayers) { for (final UUID id : partyProvider.getMembers(partyId)) { if (!id.equals(getUUID())) { mq.add(plugin.getQuester(id)); } } } else { for (final UUID id : partyProvider.getOnlineMembers(partyId)) { if (!id.equals(getUUID())) { if (distanceSquared > 0) { final Player player = Bukkit.getPlayer(id); if (player != null) { final Location locationOne = getPlayer().getLocation(); final Location locationTwo = player.getLocation(); if (locationOne.getWorld() != null && locationTwo.getWorld() != null) { if (locationOne.getWorld().getName().equals(locationTwo.getWorld() .getName())) { if (distanceSquared >= getPlayer().getLocation() .distanceSquared(player.getLocation())) { mq.add(plugin.getQuester(id)); } } } } } else { mq.add(plugin.getQuester(id)); } } } } if (plugin.getConfigSettings().getConsoleLogging() > 3) { plugin.getLogger().info("Found " + mq.size() + " party members for quest ID " + quest.getId() + " via Unite plugin"); } return mq; } } } } else if (plugin.getDependencies().getPartiesApi() != null) { if (quest.getOptions().canUsePartiesPlugin()) { final PartyPlayer partyPlayer = plugin.getDependencies().getPartiesApi().getPartyPlayer(getUUID()); if (partyPlayer != null && partyPlayer.getPartyId() != null) { final Party party = plugin.getDependencies().getPartiesApi().getParty(partyPlayer.getPartyId()); if (party != null) { final double distanceSquared = quest.getOptions().getShareDistance() * quest.getOptions().getShareDistance(); final boolean offlinePlayers = quest.getOptions().canHandleOfflinePlayers(); if (offlinePlayers) { for (final UUID id : party.getMembers()) { if (!id.equals(getUUID())) { mq.add(plugin.getQuester(id)); } } } else { for (final PartyPlayer pp : party.getOnlineMembers(true)) { if (!pp.getPlayerUUID().equals(getUUID())) { if (distanceSquared > 0) { final Player player = Bukkit.getPlayer(pp.getPlayerUUID()); if (player != null) { final Location locationOne = getPlayer().getLocation(); final Location locationTwo = player.getLocation(); if (locationOne.getWorld() != null && locationTwo.getWorld() != null) { if (locationOne.getWorld().getName().equals(locationTwo.getWorld() .getName())) { if (distanceSquared >= getPlayer().getLocation() .distanceSquared(player.getLocation())) { mq.add(plugin.getQuester(pp.getPlayerUUID())); } } } } } else { mq.add(plugin.getQuester(pp.getPlayerUUID())); } } } } if (plugin.getConfigSettings().getConsoleLogging() > 3) { plugin.getLogger().info("Found " + mq.size() + " party members for quest ID " + quest.getId() + " via Parties plugin"); } return mq; } } } } return mq; } /** * Whether this Quester meets condition of given quest * * @param quest The quest to check * @return Whether to send Quester reason for failure */ public boolean meetsCondition(final Quest quest, final boolean giveReason) { final Stage stage = getCurrentStage(quest); if (stage != null && stage.getCondition() != null && !stage.getCondition().check(this, quest)) { if (stage.getCondition().isFailQuest()) { if (giveReason) { getPlayer().sendMessage(ChatColor.RED + BukkitLang.get(getPlayer(), "conditionFailQuit") .replace("", quest.getName())); } if (stage.getFailAction() != null) { plugin.getServer().getScheduler().runTask(plugin, () -> getCurrentStage(quest).getFailAction().fire(this, quest)); } hardQuit(quest); } else if (giveReason) { if (System.currentTimeMillis() - lastNotifiedCondition > (plugin.getConfigSettings().getConditionInterval() * 1000L)) { BukkitActionBarProvider.sendActionBar(getPlayer(), ChatColor.YELLOW + BukkitLang.get(getPlayer(), "conditionFailRetry").replace("", quest.getName())); lastNotifiedCondition = System.currentTimeMillis(); } } return false; } return true; } /** * Whether this Quester is currently selecting a block in editor * * @return true if selecting */ public boolean isSelectingBlock() { final UUID uuid = getPlayer().getUniqueId(); return plugin.getQuestFactory().getSelectedBlockStarts().containsKey(uuid) || plugin.getQuestFactory().getSelectedKillLocations().containsKey(uuid) || plugin.getQuestFactory().getSelectedReachLocations().containsKey(uuid) || plugin.getActionFactory().getSelectedExplosionLocations().containsKey(uuid) || plugin.getActionFactory().getSelectedEffectLocations().containsKey(uuid) || plugin.getActionFactory().getSelectedMobLocations().containsKey(uuid) || plugin.getActionFactory().getSelectedLightningLocations().containsKey(uuid) || plugin.getActionFactory().getSelectedTeleportLocations().containsKey(uuid); } /** * Whether this Quester is in the specified WorldGuard region * * @return true if in specified WorldGuard region */ public boolean isInRegion(final String regionID) { if (getPlayer() == null) { return false; } return plugin.getDependencies().getWorldGuardApi().getApplicableRegionsIDs(getPlayer().getWorld(), getPlayer().getLocation()).contains(regionID); } /** * Checks whether an NPC can offer a quest that the player may accept * * @param npc the giver NPC UUID to check * @return true if at least one available quest has not yet been completed */ public boolean canAcceptQuest(final UUID npc) { for (final Quest q : plugin.getLoadedQuests()) { if (q.getNpcStart() != null && !getCompletedQuests().contains(q)) { if (q.getNpcStart().equals(npc)) { final boolean ignoreLockedQuests = plugin.getConfigSettings().canIgnoreLockedQuests(); if (!ignoreLockedQuests || q.testRequirements(this)) { return true; } } } } return false; } /** * Checks whether an NPC can offer a quest that the player has already completed * * @param npc The giver NPC UUID to check * @return true if at least one available quest has been completed */ public boolean canAcceptCompletedQuest(final UUID npc) { for (final Quest q : plugin.getLoadedQuests()) { if (q.getNpcStart() != null && getCompletedQuests().contains(q)) { if (q.getNpcStart().equals(npc)) { final boolean ignoreLockedQuests = plugin.getConfigSettings().canIgnoreLockedQuests(); if (!ignoreLockedQuests || q.testRequirements(this)) { return true; } } } } return false; } /** * Checks whether an NPC can offer a repeatable quest that the player has already completed * * @param npc The giver NPC UUID to check * @return true if at least one available, redoable quest has been completed */ public boolean canAcceptCompletedRedoableQuest(final UUID npc) { for (final Quest q : plugin.getLoadedQuests()) { if (q.getNpcStart() != null && getCompletedQuests().contains(q) && q.getPlanner().getCooldown() > -1) { if (q.getNpcStart().equals(npc)) { final boolean ignoreLockedQuests = plugin.getConfigSettings().canIgnoreLockedQuests(); if (!ignoreLockedQuests || q.testRequirements(this)) { return true; } } } } return false; } }