diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/Plugin.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/Plugin.java index f3073e6e..335f127b 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/Plugin.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/Plugin.java @@ -52,6 +52,7 @@ public class Plugin extends JavaPlugin { public @Nullable Boolean mShowTimerNames = null; public boolean mShowZonesDynmap = false; public boolean mFallbackZoneLookup = false; + public ConfigurationSection mQuestCompassGUIItems; public QuestCompassManager mQuestCompassManager; public QuestNpcManager mNpcManager; @@ -289,6 +290,8 @@ private void reloadConfigYaml(CommandSender sender) { sender.sendMessage("default_music_category: " + Constants.SOUND_CATEGORY_NAMES.get(mDefaultMusicSoundCategory)); } + + mQuestCompassGUIItems = mConfig.getConfigurationSection("quest_compass_gui_items"); } public static Plugin getInstance() { diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/commands/Waypoint.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/commands/Waypoint.java index 6e5c72aa..cdbafa03 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/commands/Waypoint.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/commands/Waypoint.java @@ -10,7 +10,6 @@ import dev.jorel.commandapi.arguments.TextArgument; import java.util.ArrayList; import java.util.List; -import net.md_5.bungee.api.ChatColor; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -20,8 +19,9 @@ public static void register(Plugin plugin) { EntitySelectorArgument.OnePlayer playerArg = new EntitySelectorArgument.OnePlayer("player"); TextArgument titleArg = new TextArgument("title"); - TextArgument labelArg = new TextArgument("label"); + TextArgument messageArg = new TextArgument("message"); LocationArgument locationArg = new LocationArgument("location", LocationType.PRECISE_POSITION); + TextArgument regexArg = new TextArgument("world name regex"); //Sets command waypoint new CommandAPICommand("waypoint") @@ -29,8 +29,9 @@ public static void register(Plugin plugin) { .withSubcommand(new CommandAPICommand("set") .withArguments(playerArg) .withArguments(titleArg) - .withArguments(labelArg) + .withArguments(messageArg) .withArguments(locationArg) + .withOptionalArguments(regexArg) .executes((sender, args) -> { Player targetPlayer = args.getByArgument(playerArg); if (sender instanceof Player player @@ -41,7 +42,7 @@ public static void register(Plugin plugin) { if (plugin.mQuestCompassManager != null) { List waypoint = new ArrayList<>(); waypoint.add(args.getByArgument(locationArg)); - plugin.mQuestCompassManager.setCommandWaypoint(targetPlayer, waypoint, args.getByArgument(titleArg) + ChatColor.RESET, args.getByArgument(labelArg)); + plugin.mQuestCompassManager.setCommandWaypoint(targetPlayer, waypoint, args.getByArgument(titleArg) + "§r", args.getByArgument(messageArg), args.getByArgumentOrDefault(regexArg, targetPlayer != null ? targetPlayer.getWorld().getName() : ".*")); } else { throw CommandAPI.failWithString("Quest Compass Manager does not exist!"); } diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/listeners/PlayerListener.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/listeners/PlayerListener.java index 4c39392d..77fd432a 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/listeners/PlayerListener.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/listeners/PlayerListener.java @@ -2,6 +2,7 @@ import com.playmonumenta.scriptedquests.Constants; import com.playmonumenta.scriptedquests.Plugin; +import com.playmonumenta.scriptedquests.managers.QuestCompassGui; import com.playmonumenta.scriptedquests.managers.SongManager; import com.playmonumenta.scriptedquests.point.Point; import com.playmonumenta.scriptedquests.quests.QuestDeath.DeathActions; @@ -66,10 +67,12 @@ public void playerInteractEvent(PlayerInteractEvent event) { // compass if (useItem != Event.Result.DENY && item != null - && item.getType() == Material.COMPASS && !player.isSneaking()) { - if (action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK) { + && item.getType() == Material.COMPASS) { + if ((action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK) && !player.isSneaking()) { mPlugin.mQuestCompassManager.showCurrentQuest(player); - } else if (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) { + } else if ((action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) && !player.isSneaking()) { + new QuestCompassGui(player, mPlugin.mQuestCompassManager).openInventory(player, mPlugin); + } else if ((action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK) && player.isSneaking()) { mPlugin.mQuestCompassManager.cycleQuestTracker(player); } } diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/managers/QuestCompassGui.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/managers/QuestCompassGui.java new file mode 100644 index 00000000..794744bc --- /dev/null +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/managers/QuestCompassGui.java @@ -0,0 +1,290 @@ +package com.playmonumenta.scriptedquests.managers; + +import com.playmonumenta.scriptedquests.Plugin; +import com.playmonumenta.scriptedquests.utils.CustomInventory; +import com.playmonumenta.scriptedquests.utils.InventoryUtils; +import com.playmonumenta.scriptedquests.managers.QuestCompassManager.ValidCompassEntry; +import com.playmonumenta.scriptedquests.utils.NmsUtils; +import de.tr7zw.nbtapi.NBTItem; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public class QuestCompassGui extends CustomInventory { + private final Player mPlayer; + private final Plugin mPlugin = Plugin.getInstance(); + private final QuestCompassManager mManager; + private final int mRows = mInventory.getSize() / 9; + private final List mDeathSlots = List.of(32 + 9 * (mRows - 4), 33 + 9 * (mRows - 4), 34 + 9 * (mRows - 4)); + private final int mCustomSlot = 30 + 9 * (mRows - 4); + private final int mDeselectSlot = 28 + 9 * (mRows - 4); + private final int mNextSlot = 44; + private final int mPrevSlot = 36; + private int mPage = 0; + private final Map mItemToActions = new HashMap<>(); + private final String mNBTTag = "quest_index"; + + public QuestCompassGui(Player player, QuestCompassManager manager) { + super(player, Math.min(36 + 9 * (manager.getCurrentMarkerTitles(player).stream().filter( + q -> q.mType != QuestCompassManager.CompassEntryType.Death && q.mType != QuestCompassManager.CompassEntryType.Waypoint).toList().size() / 7), 54), + "Quest Compass"); + mPlayer = player; + mManager = manager; + } + + @Override + public void openInventory(Player player, org.bukkit.plugin.Plugin owner) { + super.openInventory(player, owner); + player.playSound(player.getLocation(), Sound.UI_LOOM_SELECT_PATTERN, SoundCategory.PLAYERS, 1f, 1f); + setupInventory(mPage); + } + + private void setupInventory(int page) { + mInventory.clear(); + mPage = page; + + for (String key : mPlugin.mQuestCompassGUIItems.getKeys(false)) { + ConfigurationSection itemConfig = mPlugin.mQuestCompassGUIItems.getConfigurationSection(key); + if (itemConfig != null) { + String name = itemConfig.getString("name"); + String materialName = itemConfig.getString("material"); + String nameColorHex = itemConfig.getString("name_color"); + String loreColorHex = itemConfig.getString("lore_color"); + + name = name != null ? name : "Name Unset"; + Material material = materialName != null ? Material.getMaterial(materialName) : Material.BARRIER; + if (material == null) { + material = Material.BARRIER; + } + TextColor nameColor = nameColorHex != null ? TextColor.fromHexString(nameColorHex) : NamedTextColor.LIGHT_PURPLE; + TextColor loreColor = loreColorHex != null ? TextColor.fromHexString(loreColorHex) : NamedTextColor.DARK_PURPLE; + + List lores = new ArrayList<>(); + ConfigurationSection loresConfig = itemConfig.getConfigurationSection("lore"); + if (loresConfig != null) { + for (String line : loresConfig.getKeys(false)) { + String text = loresConfig.getString(line); + if (text == null) { + text = "Lore unset"; + } + lores.add(Component.text(text, loreColor).decoration(TextDecoration.ITALIC, false)); + } + } + + int slot = itemConfig.getInt("slot"); + if (itemConfig.getBoolean("slot_scale_with_rows", false)) { + slot += 9 * (mRows - 4); + } + + ItemStack item = new ItemStack(material); + ItemMeta itemMeta = item.getItemMeta(); + itemMeta.displayName(Component.text(name, nameColor).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false)); + itemMeta.lore(lores); + item.setItemMeta(itemMeta); + mInventory.setItem(slot, item); + + if (itemConfig.isConfigurationSection("actions")) { + mItemToActions.put(item, itemConfig.getConfigurationSection("actions")); + } + } + } + + ItemStack death = new ItemStack(Material.SKULL_POTTERY_SHERD); + ItemMeta deathMeta = death.getItemMeta(); + deathMeta.displayName(Component.text("Death Waypoints", NamedTextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false)); + deathMeta.lore(List.of(Component.text("No recent death to show here.", NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false))); + death.setItemMeta(deathMeta); + mInventory.setItem(mDeathSlots.get(0), death); + mInventory.setItem(mDeathSlots.get(1), death); + mInventory.setItem(mDeathSlots.get(2), death); + + ItemStack custom = new ItemStack(Material.HOPPER); + ItemMeta customMeta = custom.getItemMeta(); + customMeta.displayName(Component.text("Custom Waypoint", NamedTextColor.DARK_GREEN).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false)); + customMeta.lore(List.of(Component.text("No custom waypoint set. Waypoints can be set by", NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false), + Component.text("the /waypoint command or other special means.", NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false))); + custom.setItemMeta(customMeta); + mInventory.setItem(mCustomSlot, custom); + + ItemStack deselect = new ItemStack(Material.TNT_MINECART); + ItemMeta deselectMeta = deselect.getItemMeta(); + deselectMeta.displayName(Component.text("Stop Tracking", NamedTextColor.DARK_RED).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false)); + deselectMeta.lore(List.of(Component.text("Click to stop tracking the selected quest.", NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false))); + deselect.setItemMeta(deselectMeta); + mInventory.setItem(mDeselectSlot, deselect); + + List quests = mManager.getCurrentMarkerTitles(mPlayer); + + if (quests.stream().filter(q -> q.mType == QuestCompassManager.CompassEntryType.Quest).toList().size() > 21 * (1 + mPage)) { + ItemStack next = new ItemStack(Material.ARROW); + ItemMeta nextMeta = next.getItemMeta(); + nextMeta.displayName(Component.text("Next Page", NamedTextColor.WHITE).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false)); + next.setItemMeta(nextMeta); + mInventory.setItem(mNextSlot, next); + } + if (mPage > 0) { + ItemStack prev = new ItemStack(Material.ARROW); + ItemMeta prevMeta = prev.getItemMeta(); + prevMeta.displayName(Component.text("Previous Page", NamedTextColor.WHITE).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false)); + prev.setItemMeta(prevMeta); + mInventory.setItem(mPrevSlot, prev); + } + + for (int i = 21 * mPage; i < quests.size(); i++) { + ValidCompassEntry quest = quests.get(i); + if (i >= 21 * (1 + mPage) && quest.mType == QuestCompassManager.CompassEntryType.Quest) { + // Death and waypoint quests are sorted last - don't skip these, so they stay on every page + continue; + } + + NamedTextColor titleColor; + NamedTextColor selectedColor = NamedTextColor.DARK_AQUA; + if (quest.mType == QuestCompassManager.CompassEntryType.Death) { + titleColor = NamedTextColor.RED; + } else if (quest.mType == QuestCompassManager.CompassEntryType.Waypoint) { + titleColor = NamedTextColor.GREEN; + } else { + titleColor = NamedTextColor.AQUA; + } + + String title = quest.mTitle.replaceAll("&.", ""); + + Component itemName = Component.text(title, titleColor).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false); + + int slot = i + 10 - 21 * mPage; + Material material; + if (mManager.mCurrentIndex.getOrDefault(mPlayer, 0) == i) { + switch (quest.mType) { + default -> material = Material.ENCHANTED_BOOK; + case Death -> material = Material.TOTEM_OF_UNDYING; + case Waypoint -> material = Material.BAMBOO_RAFT; + } + itemName = itemName.append(Component.text(" (Currently Tracking)", selectedColor).decoration(TextDecoration.BOLD, false).decoration(TextDecoration.ITALIC, false)); + } else { + switch (quest.mType) { + default -> material = Material.BOOK; + case Death -> material = Material.ARMOR_STAND; + case Waypoint -> material = Material.KNOWLEDGE_BOOK; + } + } + + slot += (int) (2 * Math.floor((double) (slot - 10) / 7)); + if (quest.mType == QuestCompassManager.CompassEntryType.Death) { + slot = 31 + quest.mMarkersIndex[0] + 9 * (mRows - 4); + } else if (quest.mType == QuestCompassManager.CompassEntryType.Waypoint) { + slot = 30 + 9 * (mRows - 4); + } + + ItemStack displayItem = new ItemStack(material); + ItemMeta meta = displayItem.getItemMeta(); + meta.displayName(itemName); + + // Compile all steps and highlight the one that matches this quest index (j == i) + List lore = new ArrayList<>(); + for (int j = i + 1 - quest.mMarkersIndex[0]; j < i + 1 - quest.mMarkersIndex[0] + quest.mMarkersIndex[1]; j++) { + int splitIndex = 0; + ValidCompassEntry q = quests.get(j); + String qLore = q.mLocation.getMessage().replaceAll("&.", ""); + while (splitIndex < qLore.length() && !(j != i && splitIndex > 0)) { + lore.add(Component.text(StringUtils.substring(qLore, splitIndex, qLore.indexOf(" ", splitIndex + 45) < 0 ? 1000 : qLore.indexOf(" ", splitIndex + 45)) + (j != i && qLore.indexOf(" ", splitIndex + 45) > 0 ? " {...}" : ""), j == i ? NamedTextColor.WHITE : NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false)); + if (qLore.indexOf(" ", splitIndex + 45) < 0) { + splitIndex += 1000; + } else { + splitIndex = 1 + qLore.indexOf(" ", splitIndex + 45); + } + } + boolean differentWorld = !mPlayer.getWorld().getName().matches(q.mLocation.getWorldRegex()); + Location qLoc = q.mLocation.getLocation(); + lore.add(Component.text((int) qLoc.getX() + ", " + (int) qLoc.getY() + ", " + (int) qLoc.getZ() + " ", NamedTextColor.DARK_GRAY).decoration(TextDecoration.ITALIC, false) + .append(differentWorld ? Component.text("(Different World!)", NamedTextColor.RED) : Component.text(("("+ (int) mPlayer.getLocation().distance(new Location(mPlayer.getWorld(), qLoc.getX(), qLoc.getY(), qLoc.getZ())) + "m away)")))); + if (q.mType == QuestCompassManager.CompassEntryType.Waypoint) { + lore.add(Component.text("(Shift Click to remove this waypoint.)", NamedTextColor.GRAY).decoration(TextDecoration.ITALIC, false)); + } + if (j != i - quest.mMarkersIndex[0] + quest.mMarkersIndex[1]) { + lore.add(Component.text("")); + } + } + + meta.lore(lore); + displayItem.setItemMeta(meta); + // Use NBT as a quest index tag for inventoryClick to easily identify quest index + NBTItem nbtItem = new NBTItem(displayItem); + nbtItem.setInteger(mNBTTag, i); + displayItem = nbtItem.getItem(); + + mInventory.setItem(slot, displayItem); + } + } + + @Override + protected void inventoryClick(InventoryClickEvent event) { + event.setCancelled(true); + InventoryUtils.refreshOffhand(event); + int slot = event.getSlot(); + if (slot < 0) { + return; + } + ItemStack item = event.getInventory().getItem(slot); + if (!(event.getWhoClicked() instanceof Player player) + || event.getClickedInventory() != mInventory + || item == null) { + return; + } + NBTItem nbtItem = new NBTItem(item); + if (mItemToActions.containsKey(item)) { + String command = mItemToActions.get(item).getString("command"); + if (command != null) { + NmsUtils.getVersionAdapter().runConsoleCommandSilently(command.replace("@S", mPlayer.getName())); + } + if (mItemToActions.get(item).getBoolean("close_gui", false)) { + close(); + } + return; + } else if (slot == mDeselectSlot) { + mManager.mCurrentIndex.put(player, mManager.showCurrentQuest(player, -1)); + player.playSound(player.getLocation(), Sound.BLOCK_WOODEN_TRAPDOOR_CLOSE, SoundCategory.PLAYERS, 1f, 0.8f); + close(); + return; + } else if (slot == mCustomSlot && event.isShiftClick() && nbtItem.getInteger(mNBTTag) > 0) { + mManager.removeCommandWaypoint(player); + player.playSound(player.getLocation(), "minecraft:entity.armadillo.scute_drop", SoundCategory.PLAYERS, 1f, 1f); + setupInventory(mPage); + return; + } else if (slot == mNextSlot) { + setupInventory(mPage + 1); + player.playSound(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, SoundCategory.PLAYERS, 1f, 1.5f); + return; + } else if (slot == mPrevSlot) { + setupInventory(mPage - 1); + player.playSound(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, SoundCategory.PLAYERS, 1f, 1.5f); + return; + } else if (!nbtItem.hasTag(mNBTTag)) { + // Item has no function and is not a quest + return; + } + + int index = nbtItem.getInteger(mNBTTag); + mManager.mCurrentIndex.put(player, mManager.showCurrentQuest(player, index)); + player.playSound(player.getLocation(), Sound.UI_LOOM_TAKE_RESULT, SoundCategory.PLAYERS, 1f, 1f); + if (slot == mCustomSlot) { + player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.PLAYERS, 0.5f, 0.8f); + } + close(); + } +} + diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/managers/QuestCompassManager.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/managers/QuestCompassManager.java index 5de435da..f8589057 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/managers/QuestCompassManager.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/managers/QuestCompassManager.java @@ -16,8 +16,9 @@ import java.util.WeakHashMap; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; -import org.bukkit.ChatColor; import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; @@ -26,21 +27,33 @@ public class QuestCompassManager { private final List mQuests = new ArrayList(); private final Map mCompassCache = new HashMap(); - private final Map mCurrentIndex = new WeakHashMap<>(); + public final Map mCurrentIndex = new WeakHashMap<>(); private final Plugin mPlugin; /* One command-specified waypoint per player */ - public final Map mCommandWaypoints = new HashMap(); + private final Map mCommandWaypoints = new HashMap(); - private static class ValidCompassEntry { - private final QuestLocation mLocation; - private final String mTitle; - private final boolean mAllowTranslations; + public static class ValidCompassEntry { + public final QuestLocation mLocation; + public final String mTitle; + public final boolean mAllowTranslations; + public final CompassEntryType mType; + public final int[] mMarkersIndex; - private ValidCompassEntry(QuestLocation loc, String title, boolean allowTranslations) { + private ValidCompassEntry(QuestLocation loc, String title, boolean allowTranslations, CompassEntryType type) { mLocation = loc; mTitle = title; mAllowTranslations = allowTranslations; + mType = type; + mMarkersIndex = new int[]{1, 1}; + } + + private ValidCompassEntry(QuestLocation loc, String title, boolean allowTranslations, CompassEntryType type, int[] markersIndex) { + mLocation = loc; + mTitle = title; + mAllowTranslations = allowTranslations; + mType = type; + mMarkersIndex = markersIndex; } private void directPlayer(WaypointManager mgr, Player player, boolean isRemovable) { @@ -49,13 +62,23 @@ private void directPlayer(WaypointManager mgr, Player player, boolean isRemovabl } else { MessagingUtils.sendRawMessage(player, mTitle + ": " + mLocation.getMessage(), mAllowTranslations); } + if (!player.getWorld().getName().matches(mLocation.getWorldRegex())) { + MessagingUtils.sendRawMessage(player, "&7(This location is on a &cdifferent world!&7 Find a way to the correct world before following the compass.)", mAllowTranslations); + } + mgr.setWaypoint(player, mLocation); } } + public enum CompassEntryType { + Quest(), + Death(), + Waypoint() + } + private static class CompassCacheEntry { - private final int mLastRefresh; - private final List mEntries; + public final int mLastRefresh; + public final List mEntries; private CompassCacheEntry(Player player, List entries) { mLastRefresh = player.getTicksLived(); @@ -85,7 +108,7 @@ public void reload(Plugin plugin, @Nullable CommandSender sender) { } @SuppressWarnings("unchecked") - private List getCurrentMarkerTitles(Player player) { + public List getCurrentMarkerTitles(Player player) { /* * First check the cache - if it is still valid, returned the cached data * This dramatically improves performance when there are many compass entries @@ -105,14 +128,13 @@ private List getCurrentMarkerTitles(Player player) { // Add all the valid markers/titles to the list for (int i = 0; i < questMarkers.size(); i++) { - String title = ChatColor.AQUA + "" + ChatColor.BOLD + quest.getQuestName() - + ChatColor.RESET + "" + ChatColor.AQUA; + String title = "§b§l" + quest.getQuestName() + "§r§b"; if (questMarkers.size() > 1) { title += " [" + (i + 1) + "/" + questMarkers.size() + "]"; } - entries.add(new ValidCompassEntry(questMarkers.get(i), title, true)); + entries.add(new ValidCompassEntry(questMarkers.get(i), title, true, CompassEntryType.Quest, new int[]{i + 1, questMarkers.size()})); } } @@ -121,14 +143,13 @@ private List getCurrentMarkerTitles(Player player) { List deathEntries = (List)player.getMetadata(Constants.PLAYER_DEATH_LOCATION_METAKEY).get(0).value(); for (int i = 0; i < deathEntries.size(); i++) { - String title = ChatColor.RED + "" + ChatColor.BOLD + "Death" - + ChatColor.RESET + "" + ChatColor.AQUA; + String title = "§c§lDeath§r§b"; if (deathEntries.size() > 1) { title += " [" + (i + 1) + "/" + deathEntries.size() + "]"; } - entries.add(new ValidCompassEntry(deathEntries.get(i), title, false)); + entries.add(new ValidCompassEntry(deathEntries.get(i), title, false, CompassEntryType.Death, new int[]{i + 1, deathEntries.size()})); } } @@ -144,14 +165,15 @@ private List getCurrentMarkerTitles(Player player) { return entries; } - private int showCurrentQuest(Player player, int index) { + public int showCurrentQuest(Player player, int index) { List entries = getCurrentMarkerTitles(player); if (index >= entries.size()) { index = 0; + mCurrentIndex.put(player, index); } - if (entries.isEmpty()) { + if (entries.isEmpty() || index == -1) { MessagingUtils.sendActionBarMessage(player, "You have no active quest."); mPlugin.mWaypointManager.setWaypoint(player, null); } else { @@ -169,18 +191,31 @@ public void showCurrentQuest(Player player) { public void cycleQuestTracker(Player player) { Integer index = mCurrentIndex.getOrDefault(player, 0); - index += 1; + if (index < 0) { + showCurrentQuest(player, index); + return; + } - index = showCurrentQuest(player, index); + QuestCompassManager.CompassCacheEntry cacheEntryMap = mCompassCache.get(player.getUniqueId()); + if (cacheEntryMap != null) { + ValidCompassEntry quest = cacheEntryMap.mEntries.get(index); + if (quest.mMarkersIndex[0] == quest.mMarkersIndex[1]) { + index += 1 - quest.mMarkersIndex[1]; + } else { + index += 1; + } + } - mCurrentIndex.put(player, index); + mCurrentIndex.put(player, showCurrentQuest(player, index)); + player.playSound(player.getLocation(), Sound.ITEM_BOOK_PAGE_TURN, SoundCategory.PLAYERS, 1f, 1.5f); } /* One command-specified waypoint per player */ - public void setCommandWaypoint(Player player, List steps, String title, String message) { - ValidCompassEntry entry = new ValidCompassEntry(new CompassLocation(null, message, steps), title, false); + public void setCommandWaypoint(Player player, List steps, String title, String message, String worldRegex) { + invalidateCache(player); + ValidCompassEntry entry = new ValidCompassEntry(new CompassLocation(null, message, steps, worldRegex), title, false, CompassEntryType.Waypoint); mCommandWaypoints.put(player.getUniqueId(), entry); - getCurrentMarkerTitles(player); + mCurrentIndex.put(player, getCurrentMarkerTitles(player).indexOf(mCommandWaypoints.get(player.getUniqueId()))); entry.directPlayer(mPlugin.mWaypointManager, player, true); } @@ -188,8 +223,8 @@ public void setCommandWaypoint(Player player, List steps, String title public void removeCommandWaypoint(Player player) { if (mCommandWaypoints.containsKey(player.getUniqueId())) { mCommandWaypoints.remove(player.getUniqueId()); + invalidateCache(player); getCurrentMarkerTitles(player); - mPlugin.mWaypointManager.setWaypoint(player, null); } } diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/CompassLocation.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/CompassLocation.java index 0dfb975d..9a1e40e6 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/CompassLocation.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/CompassLocation.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; @@ -17,6 +18,7 @@ public class CompassLocation implements QuestLocation { private final @Nullable QuestPrerequisites mPrerequisites; private final String mMessage; private final List mWaypoints = new ArrayList(); + private final String mWorldRegex; public CompassLocation(World world, JsonElement element) throws Exception { JsonObject object = element.getAsJsonObject(); @@ -29,6 +31,9 @@ public CompassLocation(World world, JsonElement element) throws Exception { if (prereq == null) { throw new Exception("Failed to parse location prerequisites!"); } + + mWorldRegex = object.has("world_name") ? object.get("world_name").toString().replaceAll("\"", "") : Bukkit.getWorlds().get(0).getName(); + mPrerequisites = new QuestPrerequisites(prereq); if (object.has("waypoints")) { @@ -101,10 +106,11 @@ public CompassLocation(World world, JsonElement element) throws Exception { } //If QuestPrerequisites is null, prerequisites always met - public CompassLocation(@Nullable QuestPrerequisites questPrereq, String message, List waypoints) { + public CompassLocation(@Nullable QuestPrerequisites questPrereq, String message, List waypoints, String worldRegex) { mPrerequisites = questPrereq; mMessage = message; mWaypoints.addAll(waypoints); + mWorldRegex = worldRegex; } @Override @@ -122,6 +128,11 @@ public String getMessage() { return mMessage; } + @Override + public String getWorldRegex() { + return mWorldRegex; + } + @Override public boolean prerequisiteMet(Player player) { return mPrerequisites == null || mPrerequisites.prerequisiteMet(new QuestContext(Plugin.getInstance(), player, null)); diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/DeathLocation.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/DeathLocation.java index 07be73c6..817994b7 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/DeathLocation.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/DeathLocation.java @@ -7,10 +7,12 @@ public class DeathLocation implements QuestLocation { private final long mDeathTime; private final List mWaypoints = new ArrayList<>(1); + private final String mWorld; public DeathLocation(Location loc, long deathTime) { mDeathTime = deathTime; mWaypoints.add(loc); + mWorld = loc.getWorld().getName().replaceAll("\\d+", ".+"); } public String getTimeDifference(long compareTime) { @@ -79,4 +81,9 @@ public Location getLocation() { public String getMessage() { return getTimeDifference(System.currentTimeMillis()) + " ago"; } + + @Override + public String getWorldRegex() { + return mWorld; + } } diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/QuestLocation.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/QuestLocation.java index 1bc4ea8d..025675ba 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/QuestLocation.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/quests/components/QuestLocation.java @@ -19,6 +19,8 @@ public interface QuestLocation { String getMessage(); + String getWorldRegex(); + default boolean prerequisiteMet(Player player) { return true; } diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/utils/CustomInventory.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/utils/CustomInventory.java index cc07dd80..8e211935 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/utils/CustomInventory.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/utils/CustomInventory.java @@ -108,7 +108,7 @@ public CustomInventory(Player owner, int size, Component title) { mInventory = Bukkit.createInventory(owner, size, title); } - public final void openInventory(Player player, Plugin owner) { + public void openInventory(Player player, Plugin owner) { if (mOwner == null) { player.openInventory(mInventory); mOpenedInvsByPlayer.put(player, this); diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/zones/WorldRegexMatcher.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/utils/WorldRegexMatcher.java similarity index 74% rename from plugin/src/main/java/com/playmonumenta/scriptedquests/zones/WorldRegexMatcher.java rename to plugin/src/main/java/com/playmonumenta/scriptedquests/utils/WorldRegexMatcher.java index 3acf5e49..cdb9af10 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/zones/WorldRegexMatcher.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/utils/WorldRegexMatcher.java @@ -1,4 +1,4 @@ -package com.playmonumenta.scriptedquests.zones; +package com.playmonumenta.scriptedquests.utils; import java.util.HashMap; import java.util.HashSet; @@ -10,10 +10,10 @@ import org.bukkit.World; public class WorldRegexMatcher { - private static final Map mPatterns = new HashMap<>(); - private static final Map> mWorldPatternMatches = new HashMap<>(); + private final Map mPatterns = new HashMap<>(); + private final Map> mWorldPatternMatches = new HashMap<>(); - protected WorldRegexMatcher(Set worldRegexes) throws PatternSyntaxException { + public WorldRegexMatcher(Set worldRegexes) throws PatternSyntaxException { for (String worldRegexStr : worldRegexes) { mPatterns.put(worldRegexStr, Pattern.compile(worldRegexStr)); } @@ -23,7 +23,7 @@ protected WorldRegexMatcher(Set worldRegexes) throws PatternSyntaxExcept } } - protected void onLoadWorld(World world) { + public void onLoadWorld(World world) { Set matchingPatterns = new HashSet<>(); mWorldPatternMatches.put(world, matchingPatterns); @@ -41,7 +41,7 @@ protected void onLoadWorld(World world) { } } - protected void onUnloadWorld(World world) { + public void onUnloadWorld(World world) { mWorldPatternMatches.remove(world); } @@ -55,7 +55,11 @@ public boolean matches(World world, String worldRegex) { } public boolean matches(String worldName, String worldRegex) { - if (worldRegex == null || worldRegex.isEmpty() || worldRegex.equals(".*")) { + if (worldRegex == null + || worldRegex.isEmpty() + || worldRegex.equals(".*") + || worldRegex.equals(worldName) + ) { return true; } diff --git a/plugin/src/main/java/com/playmonumenta/scriptedquests/zones/ZoneManager.java b/plugin/src/main/java/com/playmonumenta/scriptedquests/zones/ZoneManager.java index dfa31dab..85ccaa5d 100644 --- a/plugin/src/main/java/com/playmonumenta/scriptedquests/zones/ZoneManager.java +++ b/plugin/src/main/java/com/playmonumenta/scriptedquests/zones/ZoneManager.java @@ -4,6 +4,7 @@ import com.playmonumenta.scriptedquests.utils.ArgUtils; import com.playmonumenta.scriptedquests.utils.MMLog; import com.playmonumenta.scriptedquests.utils.MessagingUtils; +import com.playmonumenta.scriptedquests.utils.WorldRegexMatcher; import dev.jorel.commandapi.arguments.Argument; import dev.jorel.commandapi.arguments.ArgumentSuggestions; import dev.jorel.commandapi.arguments.TextArgument; diff --git a/tools/schema/quest_location.json b/tools/schema/quest_location.json index 1a24de5e..817f550f 100644 --- a/tools/schema/quest_location.json +++ b/tools/schema/quest_location.json @@ -27,10 +27,17 @@ }, "message": { "required": true, - "propertyOrder": 4, + "propertyOrder": 3, "title": "message", "description": "Text to display in chat when checking a quest", "type": "string" - } + }, + "world_name": { + "required": false, + "propertyOrder": 4, + "title": "world_name", + "description": "A regular expression (regex) that matches one or more world names. Using /worlds in game will tell you the name of your current world if you are unsure. If not set at all, this defaults to using Bukkit.getWorlds().get(0).getName(). See regex101.com for help.", + "type": "string" + } } }