diff --git a/api/src/main/java/me/blackvein/quests/quests/Options.java b/api/src/main/java/me/blackvein/quests/quests/Options.java index 23b441562..0b0cb1896 100644 --- a/api/src/main/java/me/blackvein/quests/quests/Options.java +++ b/api/src/main/java/me/blackvein/quests/quests/Options.java @@ -48,4 +48,8 @@ public interface Options { boolean canHandleOfflinePlayers(); void setHandleOfflinePlayers(final boolean handleOfflinePlayers); + + boolean canIgnoreBlockReplace(); + + void setIgnoreBlockReplace(final boolean ignoreBlockReplace); } diff --git a/core/src/main/java/me/blackvein/quests/Quests.java b/core/src/main/java/me/blackvein/quests/Quests.java index a57df9295..6bf99693e 100644 --- a/core/src/main/java/me/blackvein/quests/Quests.java +++ b/core/src/main/java/me/blackvein/quests/Quests.java @@ -2389,6 +2389,9 @@ public class Quests extends JavaPlugin implements QuestsAPI { if (config.contains("quests." + questKey + ".options.handle-offline-players")) { opts.setHandleOfflinePlayers(config.getBoolean("quests." + questKey + ".options.handle-offline-players")); } + if (config.contains("quests." + questKey + ".options.ignore-block-replace")) { + opts.setIgnoreBlockReplace(config.getBoolean("quests." + questKey + ".options.ignore-block-replace")); + } } @SuppressWarnings({ "unchecked", "unused", "deprecation" }) diff --git a/core/src/main/java/me/blackvein/quests/convo/quests/options/OptionsPrompt.java b/core/src/main/java/me/blackvein/quests/convo/quests/options/OptionsPrompt.java index 20d635d85..44b3ad3f1 100644 --- a/core/src/main/java/me/blackvein/quests/convo/quests/options/OptionsPrompt.java +++ b/core/src/main/java/me/blackvein/quests/convo/quests/options/OptionsPrompt.java @@ -411,7 +411,7 @@ public class OptionsPrompt extends QuestsEditorNumericPrompt { super(context); } - private final int size = 4; + private final int size = 5; @Override public int getSize() { @@ -429,8 +429,9 @@ public class OptionsPrompt extends QuestsEditorNumericPrompt { case 1: case 2: case 3: - return ChatColor.BLUE; case 4: + return ChatColor.BLUE; + case 5: return ChatColor.GREEN; default: return null; @@ -447,6 +448,8 @@ public class OptionsPrompt extends QuestsEditorNumericPrompt { case 3: return ChatColor.YELLOW + Lang.get("optIgnoreSilkTouch"); case 4: + return ChatColor.YELLOW + Lang.get("optIgnoreBlockReplace"); + case 5: return ChatColor.YELLOW + Lang.get("done"); default: return null; @@ -487,6 +490,16 @@ public class OptionsPrompt extends QuestsEditorNumericPrompt { : ChatColor.RED + Lang.get("false")) + ChatColor.GRAY + ")"; } case 4: + final Boolean ignoreBlockReplaceOpt = (Boolean) context.getSessionData(CK.OPT_IGNORE_BLOCK_REPLACE); + if (ignoreBlockReplaceOpt == null) { + final boolean defaultOpt = new BukkitOptions().canIgnoreBlockReplace(); + return ChatColor.GRAY + "(" + (defaultOpt ? ChatColor.GREEN + Lang.get("true") + : ChatColor.RED + Lang.get("false")) + ChatColor.GRAY + ")"; + } else { + return ChatColor.GRAY + "(" + (ignoreBlockReplaceOpt ? ChatColor.GREEN + Lang.get("true") + : ChatColor.RED + Lang.get("false")) + ChatColor.GRAY + ")"; + } + case 5: return ""; default: return null; @@ -526,6 +539,10 @@ public class OptionsPrompt extends QuestsEditorNumericPrompt { tempPrompt = new OptionsGeneralPrompt(context); return new OptionsTrueFalsePrompt(context); case 4: + tempKey = CK.OPT_IGNORE_BLOCK_REPLACE; + tempPrompt = new OptionsGeneralPrompt(context); + return new OptionsTrueFalsePrompt(context); + case 5: tempKey = null; tempPrompt = null; try { diff --git a/core/src/main/java/me/blackvein/quests/listeners/BlockListener.java b/core/src/main/java/me/blackvein/quests/listeners/BlockListener.java index 8600811b9..4a09a35b2 100644 --- a/core/src/main/java/me/blackvein/quests/listeners/BlockListener.java +++ b/core/src/main/java/me/blackvein/quests/listeners/BlockListener.java @@ -93,64 +93,68 @@ public class BlockListener implements Listener { })); } } - if (currentStage.containsObjective(placeType)) { - for (final ItemStack is : quester.getQuestData(quest).blocksPlaced) { - if (evt.getBlock().getType().equals(is.getType()) && is.getAmount() > 0) { - ItemStack toPlace = new ItemStack(is.getType(), 64); - for (final ItemStack stack : currentStage.getBlocksToPlace()) { - if (ItemUtil.compareItems(is, stack, true) == 0) { - toPlace = stack; - } - } - - final QuesterPreUpdateObjectiveEvent preEvent - = new QuesterPreUpdateObjectiveEvent(quester, quest, - new BukkitObjective(placeType, is.getAmount(), toPlace.getAmount())); - plugin.getServer().getPluginManager().callEvent(preEvent); - - final int index = quester.getQuestData(quest).blocksPlaced.indexOf(is); - final int newAmount = is.getAmount() - 1; - is.setAmount(newAmount); - quester.getQuestData(quest).blocksPlaced.set(index, is); - - final QuesterPostUpdateObjectiveEvent postEvent - = new QuesterPostUpdateObjectiveEvent(quester, quest, - new BukkitObjective(placeType, newAmount, toPlace.getAmount())); - plugin.getServer().getPluginManager().callEvent(postEvent); - } - } - } - dispatchedPlaceQuestIDs.addAll(quester.dispatchMultiplayerEverything(quest, placeType, - (final IQuester q, final IQuest cq) -> { - if (!dispatchedPlaceQuestIDs.contains(cq.getId())) { - for (final ItemStack is : q.getQuestData(cq).blocksPlaced) { - if (evt.getBlock().getType().equals(is.getType()) && is.getAmount() > 0) { - ItemStack toPlace = new ItemStack(is.getType(), 64); - for (final ItemStack stack : quester.getCurrentStage(cq).getBlocksToPlace()) { - if (ItemUtil.compareItems(is, stack, true) == 0) { - toPlace = stack; - } - } - - final QuesterPreUpdateObjectiveEvent preEvent - = new QuesterPreUpdateObjectiveEvent((Quester) q, cq, - new BukkitObjective(placeType, is.getAmount(), toPlace.getAmount())); - plugin.getServer().getPluginManager().callEvent(preEvent); - - final int index = q.getQuestData(cq).blocksPlaced.indexOf(is); - final int newAmount = is.getAmount() - 1; - is.setAmount(newAmount); - q.getQuestData(cq).blocksPlaced.set(index, is); - - final QuesterPostUpdateObjectiveEvent postEvent - = new QuesterPostUpdateObjectiveEvent((Quester) q, cq, - new BukkitObjective(placeType, newAmount, toPlace.getAmount())); - plugin.getServer().getPluginManager().callEvent(postEvent); + if (quest.getOptions().canIgnoreBlockReplace()) { + // Ignore blocks broken once replaced (self) + if (currentStage.containsObjective(placeType)) { + for (final ItemStack is : quester.getQuestData(quest).blocksPlaced) { + if (evt.getBlock().getType().equals(is.getType()) && is.getAmount() > 0) { + ItemStack toPlace = new ItemStack(is.getType(), 64); + for (final ItemStack stack : currentStage.getBlocksToPlace()) { + if (ItemUtil.compareItems(is, stack, true) == 0) { + toPlace = stack; } } + + final QuesterPreUpdateObjectiveEvent preEvent + = new QuesterPreUpdateObjectiveEvent(quester, quest, + new BukkitObjective(placeType, is.getAmount(), toPlace.getAmount())); + plugin.getServer().getPluginManager().callEvent(preEvent); + + final int index = quester.getQuestData(quest).blocksPlaced.indexOf(is); + final int newAmount = is.getAmount() - 1; + is.setAmount(newAmount); + quester.getQuestData(quest).blocksPlaced.set(index, is); + + final QuesterPostUpdateObjectiveEvent postEvent + = new QuesterPostUpdateObjectiveEvent(quester, quest, + new BukkitObjective(placeType, newAmount, toPlace.getAmount())); + plugin.getServer().getPluginManager().callEvent(postEvent); } - return null; - })); + } + } + // Ignore blocks broken once replaced (party support) + dispatchedPlaceQuestIDs.addAll(quester.dispatchMultiplayerEverything(quest, placeType, + (final IQuester q, final IQuest cq) -> { + if (!dispatchedPlaceQuestIDs.contains(cq.getId())) { + for (final ItemStack is : q.getQuestData(cq).blocksPlaced) { + if (evt.getBlock().getType().equals(is.getType()) && is.getAmount() > 0) { + ItemStack toPlace = new ItemStack(is.getType(), 64); + for (final ItemStack stack : quester.getCurrentStage(cq).getBlocksToPlace()) { + if (ItemUtil.compareItems(is, stack, true) == 0) { + toPlace = stack; + } + } + + final QuesterPreUpdateObjectiveEvent preEvent + = new QuesterPreUpdateObjectiveEvent((Quester) q, cq, + new BukkitObjective(placeType, is.getAmount(), toPlace.getAmount())); + plugin.getServer().getPluginManager().callEvent(preEvent); + + final int index = q.getQuestData(cq).blocksPlaced.indexOf(is); + final int newAmount = is.getAmount() - 1; + is.setAmount(newAmount); + q.getQuestData(cq).blocksPlaced.set(index, is); + + final QuesterPostUpdateObjectiveEvent postEvent + = new QuesterPostUpdateObjectiveEvent((Quester) q, cq, + new BukkitObjective(placeType, newAmount, toPlace.getAmount())); + plugin.getServer().getPluginManager().callEvent(postEvent); + } + } + } + return null; + })); + } if (currentStage.containsObjective(cutType)) { if (player.getItemInHand().getType().equals(Material.SHEARS)) { quester.cutBlock(quest, blockItemStack); @@ -212,7 +216,8 @@ public class BlockListener implements Listener { final Quester quester = plugin.getQuester(player.getUniqueId()); final ObjectiveType placeType = ObjectiveType.PLACE_BLOCK; final ObjectiveType breakType = ObjectiveType.BREAK_BLOCK; - final Set dispatchedQuestIDs = new HashSet<>(); + final Set dispatchedPlaceQuestIDs = new HashSet<>(); + final Set dispatchedBreakQuestIDs = new HashSet<>(); for (final IQuest quest : plugin.getLoadedQuests()) { if (!evt.isCancelled()) { if (!quester.meetsCondition(quest, true)) { @@ -226,38 +231,73 @@ public class BlockListener implements Listener { quester.placeBlock(quest, blockItemStack); } - if (currentStage.containsObjective(breakType)) { - for (final ItemStack is : quester.getQuestData(quest).blocksBroken) { - if (evt.getBlock().getType().equals(is.getType()) && is.getAmount() > 0) { - ItemStack toBreak = new ItemStack(is.getType(), 64); - for (final ItemStack stack : currentStage.getBlocksToBreak()) { - if (ItemUtil.compareItems(is, stack, true) == 0) { - toBreak = stack; + if (quest.getOptions().canIgnoreBlockReplace()) { + // Ignore blocks replaced once broken (self) + if (currentStage.containsObjective(breakType)) { + for (final ItemStack is : quester.getQuestData(quest).blocksBroken) { + if (evt.getBlock().getType().equals(is.getType()) && is.getAmount() > 0) { + ItemStack toBreak = new ItemStack(is.getType(), 64); + for (final ItemStack stack : currentStage.getBlocksToBreak()) { + if (ItemUtil.compareItems(is, stack, true) == 0) { + toBreak = stack; + } } + + final QuesterPreUpdateObjectiveEvent preEvent + = new QuesterPreUpdateObjectiveEvent(quester, quest, + new BukkitObjective(placeType, is.getAmount(), toBreak.getAmount())); + plugin.getServer().getPluginManager().callEvent(preEvent); + + final int index = quester.getQuestData(quest).blocksBroken.indexOf(is); + final int newAmount = is.getAmount() - 1; + is.setAmount(newAmount); + quester.getQuestData(quest).blocksBroken.set(index, is); + + final QuesterPostUpdateObjectiveEvent postEvent + = new QuesterPostUpdateObjectiveEvent(quester, quest, + new BukkitObjective(placeType, newAmount, toBreak.getAmount())); + plugin.getServer().getPluginManager().callEvent(postEvent); } - - final QuesterPreUpdateObjectiveEvent preEvent - = new QuesterPreUpdateObjectiveEvent(quester, quest, - new BukkitObjective(placeType, is.getAmount(), toBreak.getAmount())); - plugin.getServer().getPluginManager().callEvent(preEvent); - - final int index = quester.getQuestData(quest).blocksBroken.indexOf(is); - final int newAmount = is.getAmount() - 1; - is.setAmount(newAmount); - quester.getQuestData(quest).blocksBroken.set(index, is); - - final QuesterPostUpdateObjectiveEvent postEvent - = new QuesterPostUpdateObjectiveEvent(quester, quest, - new BukkitObjective(placeType, newAmount, toBreak.getAmount())); - plugin.getServer().getPluginManager().callEvent(postEvent); } } + // Ignore blocks replaced once broken (party support) + dispatchedBreakQuestIDs.addAll(quester.dispatchMultiplayerEverything(quest, breakType, + (final IQuester q, final IQuest cq) -> { + if (!dispatchedBreakQuestIDs.contains(cq.getId())) { + for (final ItemStack is : q.getQuestData(cq).blocksBroken) { + if (evt.getBlock().getType().equals(is.getType()) && is.getAmount() > 0) { + ItemStack toBreak = new ItemStack(is.getType(), 64); + for (final ItemStack stack : quester.getCurrentStage(cq).getBlocksToBreak()) { + if (ItemUtil.compareItems(is, stack, true) == 0) { + toBreak = stack; + } + } + + final QuesterPreUpdateObjectiveEvent preEvent + = new QuesterPreUpdateObjectiveEvent((Quester) q, cq, + new BukkitObjective(breakType, is.getAmount(), toBreak.getAmount())); + plugin.getServer().getPluginManager().callEvent(preEvent); + + final int index = q.getQuestData(cq).blocksBroken.indexOf(is); + final int newAmount = is.getAmount() - 1; + is.setAmount(newAmount); + q.getQuestData(cq).blocksBroken.set(index, is); + + final QuesterPostUpdateObjectiveEvent postEvent + = new QuesterPostUpdateObjectiveEvent((Quester) q, cq, + new BukkitObjective(breakType, newAmount, toBreak.getAmount())); + plugin.getServer().getPluginManager().callEvent(postEvent); + } + } + } + return null; + })); } } - dispatchedQuestIDs.addAll(quester.dispatchMultiplayerEverything(quest, placeType, + dispatchedPlaceQuestIDs.addAll(quester.dispatchMultiplayerEverything(quest, placeType, (final IQuester q, final IQuest cq) -> { - if (!dispatchedQuestIDs.contains(cq.getId())) { + if (!dispatchedPlaceQuestIDs.contains(cq.getId())) { q.placeBlock(cq, blockItemStack); } return null; diff --git a/core/src/main/java/me/blackvein/quests/quests/BukkitOptions.java b/core/src/main/java/me/blackvein/quests/quests/BukkitOptions.java index 7b6f03f90..e36897bc7 100644 --- a/core/src/main/java/me/blackvein/quests/quests/BukkitOptions.java +++ b/core/src/main/java/me/blackvein/quests/quests/BukkitOptions.java @@ -22,6 +22,7 @@ public class BukkitOptions implements Options { private double shareDistance = 0.0D; private int shareProgressLevel = 1; private boolean shareSameQuestOnly = true; + private boolean ignoreBlockReplace = true; public boolean canAllowCommands() { return allowCommands; @@ -94,4 +95,12 @@ public class BukkitOptions implements Options { public void setHandleOfflinePlayers(final boolean handleOfflinePlayers) { this.handleOfflinePlayers = handleOfflinePlayers; } + + public boolean canIgnoreBlockReplace() { + return ignoreBlockReplace; + } + + public void setIgnoreBlockReplace(final boolean ignoreBlockReplace) { + this.ignoreBlockReplace = ignoreBlockReplace; + } } diff --git a/core/src/main/java/me/blackvein/quests/quests/BukkitQuestFactory.java b/core/src/main/java/me/blackvein/quests/quests/BukkitQuestFactory.java index 44a787e23..24afa62e5 100644 --- a/core/src/main/java/me/blackvein/quests/quests/BukkitQuestFactory.java +++ b/core/src/main/java/me/blackvein/quests/quests/BukkitQuestFactory.java @@ -283,6 +283,7 @@ public class BukkitQuestFactory implements QuestFactory, ConversationAbandonedLi context.setSessionData(CK.OPT_SHARE_SAME_QUEST_ONLY, opt.canShareSameQuestOnly()); context.setSessionData(CK.OPT_SHARE_DISTANCE, opt.getShareDistance()); context.setSessionData(CK.OPT_HANDLE_OFFLINE_PLAYERS, opt.canHandleOfflinePlayers()); + context.setSessionData(CK.OPT_IGNORE_BLOCK_REPLACE, opt.canIgnoreBlockReplace()); // Stages (Objectives) int index = 1; for (final IStage stage : q.getStages()) { @@ -925,6 +926,8 @@ public class BukkitQuestFactory implements QuestFactory, ConversationAbandonedLi ? context.getSessionData(CK.OPT_SHARE_DISTANCE) : null); opts.set("handle-offline-players", context.getSessionData(CK.OPT_HANDLE_OFFLINE_PLAYERS) != null ? context.getSessionData(CK.OPT_HANDLE_OFFLINE_PLAYERS) : null); + opts.set("ignore-block-replace", context.getSessionData(CK.OPT_IGNORE_BLOCK_REPLACE) != null + ? context.getSessionData(CK.OPT_IGNORE_BLOCK_REPLACE) : null); if (opts.getKeys(false).isEmpty()) { section.set("options", null); } diff --git a/core/src/main/java/me/blackvein/quests/util/CK.java b/core/src/main/java/me/blackvein/quests/util/CK.java index 1ecfdfbd6..c498a1f76 100644 --- a/core/src/main/java/me/blackvein/quests/util/CK.java +++ b/core/src/main/java/me/blackvein/quests/util/CK.java @@ -150,6 +150,7 @@ public class CK { public static final String OPT_SHARE_SAME_QUEST_ONLY = "shareSameQuestOnlyOpt"; public static final String OPT_SHARE_DISTANCE = "shareDistance"; public static final String OPT_HANDLE_OFFLINE_PLAYERS = "handleOfflinePlayers"; + public static final String OPT_IGNORE_BLOCK_REPLACE = "ignoreBlockReplace"; // Actions public static final String E_OLD_EVENT = "oldEvent"; public static final String E_NAME = "evtName"; diff --git a/core/src/main/resources/lang/en-US/strings.yml b/core/src/main/resources/lang/en-US/strings.yml index 4effdf41d..a8c138ed4 100644 --- a/core/src/main/resources/lang/en-US/strings.yml +++ b/core/src/main/resources/lang/en-US/strings.yml @@ -486,6 +486,7 @@ optDistancePrompt: "Enter a distance (number) for share radius, , ." optPluginListTitle: "- Available Plugins -" optExternalPartyPlugin: "Set provider via Unite" diff --git a/core/src/main/resources/strings.yml b/core/src/main/resources/strings.yml index e2a154a9d..856b3abc8 100644 --- a/core/src/main/resources/strings.yml +++ b/core/src/main/resources/strings.yml @@ -482,6 +482,7 @@ optDistancePrompt: "Enter a distance (number) for share radius, , ." optPluginListTitle: "- Available Plugins -" optExternalPartyPlugin: "Set provider via Unite"