diff options
author | Cow <cow@volloeko.de> | 2020-09-27 23:49:09 +0200 |
---|---|---|
committer | Cow <cow@volloeko.de> | 2020-09-27 23:49:09 +0200 |
commit | fbf84843e290b720c43feb766dc72190d56b929c (patch) | |
tree | aa67f8b515d3e27fefbb707b5b4cd34956e79c60 /src/main | |
parent | f5b35b7a064fa8c144c2ddcb5f94a4790398506d (diff) | |
download | Cowlection-fbf84843e290b720c43feb766dc72190d56b929c.tar.gz Cowlection-fbf84843e290b720c43feb766dc72190d56b929c.tar.bz2 Cowlection-fbf84843e290b720c43feb766dc72190d56b929c.zip |
Show item quality + obtained floor by default
Diffstat (limited to 'src/main')
4 files changed, 93 insertions, 68 deletions
diff --git a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java index 1d8b4ee..728631e 100644 --- a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java +++ b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java @@ -64,8 +64,9 @@ public class MooConfig { private static String tooltipItemTimestamp; private static String numeralSystem; // Category: SkyBlock Dungeons - public static int dungItemToolTipToggleKeyBinding; + private static String showItemQualityAndFloor; private static String dungItemQualityPos; + public static int dungItemToolTipToggleKeyBinding; public static boolean dungOverlayEnabled; public static int dungOverlayPositionX; public static int dungOverlayPositionY; @@ -77,7 +78,7 @@ public class MooConfig { public static boolean dungFilterPartiesWithHealerDupes; public static boolean dungFilterPartiesWithMageDupes; public static boolean dungFilterPartiesWithTankDupes; - private static String dungPartyFinderArmorLookup; + private static String dungPartyFinderPlayerLookup; private static Configuration cfg = null; private static final List<MooConfigCategory> configCategories = new ArrayList<>(); @@ -304,16 +305,18 @@ public class MooConfig { // Sub-Category: Tooltip enhancements subCat = configCat.addSubCategory("Dungeon item tooltip enhancements"); - subCat.addExplanations("Hold left " + EnumChatFormatting.YELLOW + "SHIFT " + EnumChatFormatting.RESET + "while hovering over a dungeon item.", - "Shows " + EnumChatFormatting.YELLOW + "item quality " + EnumChatFormatting.RESET + "and " + EnumChatFormatting.YELLOW + "dungeon floor" + EnumChatFormatting.RESET + ", also remove stats from reforges and essences (✪)", + subCat.addExplanations("Shows " + EnumChatFormatting.YELLOW + "item quality " + EnumChatFormatting.RESET + "and " + EnumChatFormatting.YELLOW + "dungeon floor" + EnumChatFormatting.RESET + " (if enabled)", + "", + "Hold left " + EnumChatFormatting.YELLOW + "SHIFT " + EnumChatFormatting.RESET + "while hovering over a dungeon item,", + "to remove stats from reforges and essences (✪)", "which normally makes the comparison of dungeon items difficult.", "Instead, the tooltip shows...", " ‣ base/default stats " + EnumChatFormatting.GRAY + "(outside dungeons; 1st value - usually red or green)", " ‣ stats inside dungeons " + EnumChatFormatting.GRAY + "(including dungeon level stat boost, but without essences [₀ₓ✪])", " ‣ stats inside dungeons with 5x essence upgrades " + EnumChatFormatting.GRAY + "(₅ₓ✪)"); - Property propDungItemToolTipToggleKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), - "dungItemToolTipToggleKeyBinding", Keyboard.KEY_LSHIFT, "Key to toggle dungeon item tooltip")); + Property propShowItemQualityAndFloor = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "showItemQualityAndFloor", "always", "show item quality + obtained floor?", new String[]{"always", "key press", "never"})); Property propDungItemQualityPos = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungItemQualityPos", "top", "Position of item quality in tooltip", new String[]{"top", "bottom"}), @@ -321,6 +324,9 @@ public class MooConfig { MooConfigPreview.createDungeonItem("light", "7/17/20 7:22 PM", "§7Gear Score: §d336 §8(526)", "§7Crit Chance: §c+5% §9(Light +2%)", "§7Crit Damage: §c+30% §9(Light +4%) §8(+48.9%)", "§7Bonus Attack Speed: §c+4% §9(Light +4%)", "", "§7Health: §a+126 HP §9(Light +15 HP) §8(+205.38 HP)", "§7Defense: §a+76 §9(Light +4) §8(+123.88)", "§7Speed: §a+4 §9(Light +4) §8(+6.52)", "", "§9Growth V, §9Protection V", "§9Thorns III", "", "§7Increase the damage you deal", "§7with arrows by §c5%§7.", "", "§6Full Set Bonus: Skeleton Soldier", "§7Increase the damage you deal", "§7with arrows by an extra §c25%§7.", "", "§aPerfect 52500 / 52500", "§5§lEPIC DUNGEON LEGGINGS"), MooConfigPreview.createDungeonItem("clean", "7/11/20 12:27 PM", "§7Gear Score: §d359 §8(561)", "§7Crit Chance: §c+11% §9(Clean +8%)", "§7Crit Damage: §c+26% §8(+42.38%)", "", "§7Health: §a+126 HP §9(Clean +15 HP) §8(+205.38 HP)", "§7Defense: §a+87 §9(Clean +15) §8(+141.81)", "", "§9Growth V, §9Protection V", "§9Thorns III", "", "§7Increase the damage you deal", "§7with arrows by §c5%§7.", "", "§6Full Set Bonus: Skeleton Soldier", "§7Increase the damage you deal", "§7with arrows by an extra §c25%§7.", "", "§aPerfect 52500 / 52500", "§5§lEPIC DUNGEON LEGGINGS"))); + Property propDungItemToolTipToggleKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungItemToolTipToggleKeyBinding", Keyboard.KEY_LSHIFT, "Key to toggle dungeon item tooltip")); + // Sub-Category: Performance Overlay subCat = configCat.addSubCategory("Performance Overlay"); subCat.addExplanations(EnumChatFormatting.UNDERLINE + "Keeps track of:", @@ -395,8 +401,8 @@ public class MooConfig { "dungFilterPartiesWithTankDupes", false, "Mark parties with duplicated Tank class?"), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺T").gray())); - Property propDungPartyFinderArmorLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), - "dungPartyFinderArmorLookup", "as a tooltip", "Show armor of player joining via party finder as a tooltip or in chat?", new String[]{"as a tooltip", "in chat", "disabled"})); + Property propDungPartyFinderPlayerLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungPartyFinderPlayerLookup", "as a tooltip", "Show armor + dungeons stats of player joining via party finder as a tooltip or in chat?", new String[]{"as a tooltip", "in chat", "disabled"})); boolean modifiedTabCompletableCommandsList = false; String[] tabCompletableCommandsPreChange = tabCompletableNamesCommands != null ? tabCompletableNamesCommands.clone() : null; @@ -420,8 +426,9 @@ public class MooConfig { tooltipItemTimestamp = propTooltipItemTimestamp.getString(); numeralSystem = propNumeralSystem.getString(); // Category: SkyBlock Dungeons - dungItemToolTipToggleKeyBinding = propDungItemToolTipToggleKeyBinding.getInt(); + showItemQualityAndFloor = propShowItemQualityAndFloor.getString(); dungItemQualityPos = propDungItemQualityPos.getString(); + dungItemToolTipToggleKeyBinding = propDungItemToolTipToggleKeyBinding.getInt(); dungOverlayEnabled = propDungOverlayEnabled.getBoolean(); dungOverlayPositionX = propDungOverlayPositionX.getInt(); dungOverlayPositionY = propDungOverlayPositionY.getInt(); @@ -433,7 +440,7 @@ public class MooConfig { dungFilterPartiesWithHealerDupes = propDungFilterPartiesWithHealerDupes.getBoolean(); dungFilterPartiesWithMageDupes = propDungFilterPartiesWithMageDupes.getBoolean(); dungFilterPartiesWithTankDupes = propDungFilterPartiesWithTankDupes.getBoolean(); - dungPartyFinderArmorLookup = propDungPartyFinderArmorLookup.getString(); + dungPartyFinderPlayerLookup = propDungPartyFinderPlayerLookup.getString(); if (!Arrays.equals(tabCompletableCommandsPreChange, tabCompletableNamesCommands)) { @@ -460,8 +467,9 @@ public class MooConfig { propTooltipItemTimestamp.set(tooltipItemTimestamp); propNumeralSystem.set(numeralSystem); // Category: SkyBlock Dungeons - propDungItemToolTipToggleKeyBinding.set(dungItemToolTipToggleKeyBinding); + propShowItemQualityAndFloor.set(showItemQualityAndFloor); propDungItemQualityPos.set(dungItemQualityPos); + propDungItemToolTipToggleKeyBinding.set(dungItemToolTipToggleKeyBinding); propDungOverlayEnabled.set(dungOverlayEnabled); propDungOverlayPositionX.set(dungOverlayPositionX); propDungOverlayPositionY.set(dungOverlayPositionY); @@ -473,7 +481,7 @@ public class MooConfig { propDungFilterPartiesWithHealerDupes.set(dungFilterPartiesWithHealerDupes); propDungFilterPartiesWithMageDupes.set(dungFilterPartiesWithMageDupes); propDungFilterPartiesWithTankDupes.set(dungFilterPartiesWithTankDupes); - propDungPartyFinderArmorLookup.set(dungPartyFinderArmorLookup); + propDungPartyFinderPlayerLookup.set(dungPartyFinderPlayerLookup); if (saveToFile && cfg.hasChanged()) { boolean isPlayerIngame = Minecraft.getMinecraft().thePlayer != null; @@ -578,12 +586,16 @@ public class MooConfig { } // Category: SkyBlock Dungeons + public static Setting getShowItemQualityAndFloorDisplay() { + return Setting.get(showItemQualityAndFloor); + } + public static boolean isDungItemQualityAtTop() { return dungItemQualityPos.equals("top"); } - public static Setting getDungPartyFinderArmorLookupDisplay() { - return Setting.get(dungPartyFinderArmorLookup); + public static Setting getDungPartyFinderPlayerLookupDisplay() { + return Setting.get(dungPartyFinderPlayerLookup); } public static boolean filterDungPartiesWithDupes(DataHelper.DungeonClass dungeonClass) { diff --git a/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java b/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java index 9f7bb93..e7bec03 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java @@ -166,7 +166,7 @@ public class ChatListener { messageSender = partyOrGameInviteMatcher.group(1); } else if (dungeonPartyFinderJoinedMatcher.find()) { messageSender = dungeonPartyFinderJoinedMatcher.group(1); - if (MooConfig.getDungPartyFinderArmorLookupDisplay() != MooConfig.Setting.DISABLED && !messageSender.equals(Minecraft.getMinecraft().thePlayer.getName())) { + if (MooConfig.getDungPartyFinderPlayerLookupDisplay() != MooConfig.Setting.DISABLED && !messageSender.equals(Minecraft.getMinecraft().thePlayer.getName())) { String dungeonClass = dungeonPartyFinderJoinedMatcher.group(2) + " Lvl " + dungeonPartyFinderJoinedMatcher.group(3); getDungeonPartyMemberDetails(messageSender, dungeonClass); } @@ -191,13 +191,13 @@ public class ChatListener { HySkyBlockStats.Profile.Member member = activeProfile.getMember(stalkedPlayer.getUuid()); MooChatComponent armorLookupComponent; String armorLookupPrefix = " ❈ " + EnumChatFormatting.DARK_GREEN + playerName; - MooConfig.Setting dungPartyFinderArmorLookupDisplay = MooConfig.getDungPartyFinderArmorLookupDisplay(); - String delimiter = "\n" + (dungPartyFinderArmorLookupDisplay == MooConfig.Setting.TEXT ? " " : ""); + MooConfig.Setting dungPartyFinderPlayerLookupDisplay = MooConfig.getDungPartyFinderPlayerLookupDisplay(); + String delimiter = "\n" + (dungPartyFinderPlayerLookupDisplay == MooConfig.Setting.TEXT ? " " : ""); String armorLookupResult = EnumChatFormatting.LIGHT_PURPLE + " ➜ " + EnumChatFormatting.GRAY + dungeonClass + delimiter + String.join(delimiter, member.getArmor()); HySkyBlockStats.Profile.Dungeons dungeons = member.getDungeons(); String highestFloorCompletions = "\n" + EnumChatFormatting.GRAY + "Completed no dungeons yet"; - if (dungPartyFinderArmorLookupDisplay == MooConfig.Setting.TEXT) { + if (dungPartyFinderPlayerLookupDisplay == MooConfig.Setting.TEXT) { // highest floor completions: if (dungeons != null && dungeons.hasPlayed()) { highestFloorCompletions = dungeons.getHighestFloorCompletions(1, true).toString(); @@ -212,7 +212,7 @@ public class ChatListener { armorLookupComponent = new MooChatComponent(armorLookupPrefix + EnumChatFormatting.GREEN + (playerName.endsWith("s") ? "'" : "'s") + " dungeons info (hover me)").green() .setHover(new MooChatComponent(EnumChatFormatting.BOLD + playerName + armorLookupResult + highestFloorCompletions)); } - main.getChatHelper().sendMessage(armorLookupComponent.setSuggestCommand("/p kick " + playerName, dungPartyFinderArmorLookupDisplay == MooConfig.Setting.TEXT)); + main.getChatHelper().sendMessage(armorLookupComponent.setSuggestCommand("/p kick " + playerName, dungPartyFinderPlayerLookupDisplay == MooConfig.Setting.TEXT)); } }); } diff --git a/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java b/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java index e4fea69..6a0913b 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java @@ -121,13 +121,22 @@ public class DungeonsListener { if (e.itemStack == null || e.toolTip == null) { return; } - if (MooConfig.isDungeonItemTooltipToggleKeyBindingPressed() && isDungeonItem(e.toolTip)) { - // simplify dungeon armor stats - String originalItemName = e.itemStack.getDisplayName(); + boolean toggleKeyBindingPressed = MooConfig.isDungeonItemTooltipToggleKeyBindingPressed(); + MooConfig.Setting showItemQualityAndFloorDisplay = MooConfig.getShowItemQualityAndFloorDisplay(); + boolean showItemQualityAndFloor = showItemQualityAndFloorDisplay == MooConfig.Setting.ALWAYS + || (showItemQualityAndFloorDisplay == MooConfig.Setting.SPECIAL && toggleKeyBindingPressed); + if ((showItemQualityAndFloor || toggleKeyBindingPressed) + && isDungeonItem(e.toolTip)) { NBTTagCompound extraAttributes = e.itemStack.getSubCompound("ExtraAttributes", false); - if (extraAttributes != null) { + if (extraAttributes == null) { + // no extra attributes? no SkyBlock item! + return; + } + String reforge = ""; + if (toggleKeyBindingPressed) { + // simplify dungeon armor stats + String originalItemName = e.itemStack.getDisplayName(); StringBuilder modifiedItemName = new StringBuilder(originalItemName); - String reforge = ""; String grayedOutFormatting = "" + EnumChatFormatting.GRAY + EnumChatFormatting.STRIKETHROUGH; if (extraAttributes.hasKey("modifier")) { @@ -178,43 +187,45 @@ public class DungeonsListener { essenceModifier = modifiedItemName.indexOf(essenceUpgradeIndicator); } e.toolTip.set(0, modifiedItemName.toString()); // replace item name - - // subtract stat boosts from reforge and update stats for dungeons - ListIterator<String> tooltipIterator = e.toolTip.listIterator(); - - String itemQualityBottom = null; - while (tooltipIterator.hasNext()) { - String line = tooltipIterator.next(); - Matcher lineMatcher = TOOLTIP_LINE_PATTERN.matcher(line); - String lineWithoutFormatting = EnumChatFormatting.getTextWithoutFormattingCodes(line); - if (lineMatcher.matches()) { - if (EnumChatFormatting.getTextWithoutFormattingCodes(lineMatcher.group("prefix")).equals("Gear Score: ")) { - // replace meaningless gear score with item quality (gear score includes reforges etc) - StringBuilder customGearScore = new StringBuilder(EnumChatFormatting.GRAY.toString()).append("Item Quality: "); - boolean hasCustomGearScore = false; - if (extraAttributes.hasKey("baseStatBoostPercentage")) { - int itemQuality = extraAttributes.getInteger("baseStatBoostPercentage") * 2; // value between 0 and 50 => *2 == in % - customGearScore.append(EnumChatFormatting.LIGHT_PURPLE).append(itemQuality).append("%"); - hasCustomGearScore = true; - } - if (extraAttributes.hasKey("item_tier", Constants.NBT.TAG_INT)) { - int obtainedFromFloor = extraAttributes.getInteger("item_tier"); - customGearScore.append(EnumChatFormatting.GRAY).append(" (Floor ").append(EnumChatFormatting.LIGHT_PURPLE).append(obtainedFromFloor).append(EnumChatFormatting.GRAY).append(")"); - hasCustomGearScore = true; - } - if (!hasCustomGearScore) { - customGearScore.append("―"); - } + } + // add item quality/floor and (if key bind is pressed: subtract stat boosts from reforge and update stats for dungeons) + ListIterator<String> tooltipIterator = e.toolTip.listIterator(); + + String itemQualityBottom = null; + while (tooltipIterator.hasNext()) { + String line = tooltipIterator.next(); + Matcher lineMatcher = TOOLTIP_LINE_PATTERN.matcher(line); + String lineWithoutFormatting = EnumChatFormatting.getTextWithoutFormattingCodes(line); + if (lineMatcher.matches()) { + if (EnumChatFormatting.getTextWithoutFormattingCodes(lineMatcher.group("prefix")).equals("Gear Score: ")) { + // replace meaningless gear score with item quality (gear score includes reforges etc) + StringBuilder customGearScore = new StringBuilder(EnumChatFormatting.GRAY.toString()).append("Item Quality: "); + boolean hasCustomGearScore = false; + if (extraAttributes.hasKey("baseStatBoostPercentage")) { + int itemQuality = extraAttributes.getInteger("baseStatBoostPercentage") * 2; // value between 0 and 50 => *2 == in % + customGearScore.append(EnumChatFormatting.LIGHT_PURPLE).append(itemQuality).append("%"); + hasCustomGearScore = true; + } + if (extraAttributes.hasKey("item_tier", Constants.NBT.TAG_INT)) { + int obtainedFromFloor = extraAttributes.getInteger("item_tier"); + customGearScore.append(EnumChatFormatting.GRAY).append(" (Floor ").append(EnumChatFormatting.LIGHT_PURPLE).append(obtainedFromFloor).append(EnumChatFormatting.GRAY).append(")"); + hasCustomGearScore = true; + } + if (!hasCustomGearScore) { + customGearScore.append("―"); + } + if (showItemQualityAndFloor) { if (MooConfig.isDungItemQualityAtTop()) { - // replace 'Gear Score' line + // replace gear score with item quality + obtained floor to top of tooltip tooltipIterator.set(customGearScore.toString()); } else { - // delete 'Gear Score' line and add item quality to bottom - tooltipIterator.remove(); + // add item quality + obtained floor to bottom itemQualityBottom = customGearScore.toString(); } - continue; } + continue; + } + if (toggleKeyBindingPressed) { try { int statNonDungeon = Integer.parseInt(lineMatcher.group("statNonDungeon")); @@ -249,15 +260,15 @@ public class DungeonsListener { tooltipIterator.set(newToolTipLine); } catch (NumberFormatException ignored) { } - } else if (lineWithoutFormatting.startsWith("Item Ability: ") || lineWithoutFormatting.startsWith("Full Set Bonus: ")) { - // stop replacing tooltip entries once we reach item ability or full set bonus - break; } + } else if (lineWithoutFormatting.startsWith("Item Ability: ") || lineWithoutFormatting.startsWith("Full Set Bonus: ")) { + // stop replacing tooltip entries once we reach item ability or full set bonus + break; } - if (itemQualityBottom != null) { - int index = Math.max(0, e.toolTip.size() - (e.showAdvancedItemTooltips ? /* item name & nbt info */ 2 : 0)); - e.toolTip.add(index, itemQualityBottom); - } + } + if (itemQualityBottom != null) { + int index = Math.max(0, e.toolTip.size() - (e.showAdvancedItemTooltips ? /* item name & nbt info */ 2 : 0)); + e.toolTip.add(index, itemQualityBottom); } } } diff --git a/src/main/resources/assets/cowlection/lang/en_US.lang b/src/main/resources/assets/cowlection/lang/en_US.lang index 14bb5f7..516d997 100644 --- a/src/main/resources/assets/cowlection/lang/en_US.lang +++ b/src/main/resources/assets/cowlection/lang/en_US.lang @@ -34,10 +34,12 @@ cowlection.config.tooltipItemTimestamp=Show item creation date cowlection.config.tooltipItemTimestamp.tooltip=Show item creation date? Only works for non-stackable items cowlection.config.numeralSystem=Numeral system cowlection.config.numeralSystem.tooltip=Use Roman or Arabic numeral system?\nThis is currently used to display numbers in the commands /moo stalkSkyBlock and /moo analyzeIsland -cowlection.config.dungItemToolTipToggleKeyBinding=Key binding: toggle dungeon item tooltip -cowlection.config.dungItemToolTipToggleKeyBinding.tooltip=Hold down this key to toggle dungeon item tooltip -cowlection.config.dungItemQualityPos=Item Quality position (hold above keybind) -cowlection.config.dungItemQualityPos.tooltip=Position of item quality in dungeon item tooltips +cowlection.config.showItemQualityAndFloor=Show item quality + obtained floor +cowlection.config.showItemQualityAndFloor.tooltip=Should the item quality (in %%) and the obtained floor be added to the dungeon items' tooltips?\n§e'top' replaces the default 'gear score' entry §rwhich normally includes reforges and essence upgrades. +cowlection.config.dungItemQualityPos=Item quality + obtained floor position +cowlection.config.dungItemQualityPos.tooltip=Position of item quality and otained floor in dungeon item tooltips +cowlection.config.dungItemToolTipToggleKeyBinding=Key binding: Show dungeon item base stats +cowlection.config.dungItemToolTipToggleKeyBinding.tooltip=Hold down this key to toggle dungeon item tooltip.\nDisplays the base stats of an item without reforges and without essence upgrades. cowlection.config.dungOverlayEnabled=Show overlay inside dungeons? cowlection.config.dungOverlayEnabled.tooltip=Show the performance overlay while inside a dungeon? cowlection.config.dungOverlayPositionX=Overlay x position (⇦/⇨ keys to fine-tune) @@ -60,8 +62,8 @@ cowlection.config.dungFilterPartiesWithMageDupes=Mark duplicated Mage class? cowlection.config.dungFilterPartiesWithMageDupes.tooltip=Mark parties with duplicated Mage class? cowlection.config.dungFilterPartiesWithTankDupes=Mark duplicated Tank class? cowlection.config.dungFilterPartiesWithTankDupes.tooltip=Mark parties with duplicated Tank class? -cowlection.config.dungPartyFinderArmorLookup=Show armor of joining player... §d§l⚷ -cowlection.config.dungPartyFinderArmorLookup.tooltip=Show armor of player joining via party finder as a tooltip or in chat?\n§d§l⚷ §eRequires a valid API key! +cowlection.config.dungPartyFinderPlayerLookup=Show armor + dungeons stats of joining player... §d§l⚷ +cowlection.config.dungPartyFinderPlayerLookup.tooltip=Show armor and dungeons stats of player joining via party finder as a tooltip or in chat?\n§d§l⚷ §eRequires a valid API key! cowlection.commands.generic.exception=%s key.cowlection.category=Cowlection key.cowlection.moo=Open Command |