diff options
9 files changed, 241 insertions, 59 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6601a31..30a9330 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Improved 'being on SkyBlock' detection - gave scoreboard more time to get detected - also added config option to always (or never) enable SkyBlock event listeners +- Dungeon Party Finder (overlay): + - Made party indicators clearer (current, suitable, unideal, unjoinable party) + - Show sizes of parties + - Mark parties with 'carry' in their notes + - Lookup info when joining another party via Dungeon Party Finder (disabled by default) + - Added active pet + found dungeon secrets to dungeon player lookup ### Fixed - Fixed some possible problems with bad server connection diff --git a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java index 2adfcb7..f666181 100644 --- a/src/main/java/de/cowtipper/cowlection/config/MooConfig.java +++ b/src/main/java/de/cowtipper/cowlection/config/MooConfig.java @@ -85,13 +85,16 @@ public class MooConfig { public static int dungOverlayPositionY; public static int dungOverlayGuiScale; public static boolean dungOverlayTextShadow; - public static int dungClassMin; - public static boolean dungFilterPartiesWithArcherDupes; - public static boolean dungFilterPartiesWithBerserkDupes; - public static boolean dungFilterPartiesWithHealerDupes; - public static boolean dungFilterPartiesWithMageDupes; - public static boolean dungFilterPartiesWithTankDupes; private static String dungPartyFinderPlayerLookup; + public static boolean dungPartyFinderPartyLookup; + public static boolean dungPartiesSize; + public static int dungClassMin; + public static boolean dungFilterPartiesWithCarry; + private static boolean dungFilterPartiesWithArcherDupes; + private static boolean dungFilterPartiesWithBerserkDupes; + private static boolean dungFilterPartiesWithHealerDupes; + private static boolean dungFilterPartiesWithMageDupes; + private static boolean dungFilterPartiesWithTankDupes; private static Configuration cfg = null; private static final List<MooConfigCategory> configCategories = new ArrayList<>(); @@ -435,38 +438,52 @@ public class MooConfig { "to make it easier to find the perfect party:", "", "Marks parties...", - " ‣ you cannot join: " + EnumChatFormatting.RED + "⬛" + EnumChatFormatting.RESET + " (red carpet)", - " ‣ with someone below a certain class level: " + EnumChatFormatting.RED + EnumChatFormatting.BOLD + "ᐯ" + EnumChatFormatting.RESET, - " ‣ with duplicated roles you specify below: " + EnumChatFormatting.GOLD + "²⁺", - " ‣ that match your criteria: " + EnumChatFormatting.GREEN + "⬛" + EnumChatFormatting.RESET + " (green carpet)"); + " ‣ you cannot join: " + EnumChatFormatting.RED + "⬛", + " ‣ that do not meet all your criteria: " + EnumChatFormatting.GOLD + "⬛", + " ‣ with someone below a certain class level: " + EnumChatFormatting.RED + EnumChatFormatting.BOLD + "ᐯ" + EnumChatFormatting.RESET, + " ‣ with duplicated roles you specify below: " + EnumChatFormatting.GOLD + "²⁺", + " ‣ with 'carry' in their notes: " + EnumChatFormatting.AQUA + "carry", + " ‣ that match your criteria: " + EnumChatFormatting.GREEN + "⬛"); + + 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"})); + + Property propDungPartyFinderPartyLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungPartyFinderPartyLookup", false, "Lookup info when joining another party?")); + + Property propDungPartiesSize = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungPartiesSize", true, "Show size of parties?"), + new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.WHITE + "1 - 4").gray())); Property propDungClassMin = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungClassMin", 0, "Marks parties with members with lower class level than this value") .setMinValue(0).setMaxValue(50), new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.RED + EnumChatFormatting.BOLD + "ᐯ").gray())); + Property propDungFilterPartiesWithCarry = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), + "dungFilterPartiesWithCarry", true, "Mark parties with carry in the notes"), + new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.AQUA + "carry" + + EnumChatFormatting.GRAY + " or " + EnumChatFormatting.GREEN + "carry " + EnumChatFormatting.GRAY + "('free' carries)").gray())); + Property propDungFilterPartiesWithArcherDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithArcherDupes", true, "Mark parties with duplicated Archer class?"), - new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺A").gray())); + new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "A").gray())); Property propDungFilterPartiesWithBerserkDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithBerserkDupes", false, "Mark parties with duplicated Berserk class?"), - new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺B").gray())); + new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "B").gray())); Property propDungFilterPartiesWithHealerDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithHealerDupes", false, "Mark parties with duplicated Healer class?"), - new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺H").gray())); + new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "H").gray())); Property propDungFilterPartiesWithMageDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithMageDupes", false, "Mark parties with duplicated Mage class?"), - new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺M").gray())); + new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "M").gray())); Property propDungFilterPartiesWithTankDupes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(), "dungFilterPartiesWithTankDupes", false, "Mark parties with duplicated Tank class?"), - new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺T").gray())); - - 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"})); + new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.GOLD + "²⁺" + EnumChatFormatting.YELLOW + "T").gray())); boolean modifiedMooCmdAlias = false; String mooCmdAliasPreChange = mooCmdAlias; @@ -510,13 +527,16 @@ public class MooConfig { dungOverlayPositionY = propDungOverlayPositionY.getInt(); dungOverlayGuiScale = propDungOverlayGuiScale.getInt(); dungOverlayTextShadow = propDungOverlayTextShadow.getBoolean(); + dungPartyFinderPlayerLookup = propDungPartyFinderPlayerLookup.getString(); + dungPartyFinderPartyLookup = propDungPartyFinderPartyLookup.getBoolean(); + dungPartiesSize = propDungPartiesSize.getBoolean(); dungClassMin = propDungClassMin.getInt(); + dungFilterPartiesWithCarry = propDungFilterPartiesWithCarry.getBoolean(); dungFilterPartiesWithArcherDupes = propDungFilterPartiesWithArcherDupes.getBoolean(); dungFilterPartiesWithBerserkDupes = propDungFilterPartiesWithBerserkDupes.getBoolean(); dungFilterPartiesWithHealerDupes = propDungFilterPartiesWithHealerDupes.getBoolean(); dungFilterPartiesWithMageDupes = propDungFilterPartiesWithMageDupes.getBoolean(); dungFilterPartiesWithTankDupes = propDungFilterPartiesWithTankDupes.getBoolean(); - dungPartyFinderPlayerLookup = propDungPartyFinderPlayerLookup.getString(); if (!StringUtils.equals(mooCmdAliasPreChange, mooCmdAlias)) { @@ -564,13 +584,16 @@ public class MooConfig { propDungOverlayPositionY.set(dungOverlayPositionY); propDungOverlayGuiScale.set(dungOverlayGuiScale); propDungOverlayTextShadow.set(dungOverlayTextShadow); + propDungPartyFinderPlayerLookup.set(dungPartyFinderPlayerLookup); + propDungPartyFinderPartyLookup.set(dungPartyFinderPartyLookup); + propDungPartiesSize.set(dungPartiesSize); propDungClassMin.set(dungClassMin); + propDungFilterPartiesWithCarry.set(dungFilterPartiesWithCarry); propDungFilterPartiesWithArcherDupes.set(dungFilterPartiesWithArcherDupes); propDungFilterPartiesWithBerserkDupes.set(dungFilterPartiesWithBerserkDupes); propDungFilterPartiesWithHealerDupes.set(dungFilterPartiesWithHealerDupes); propDungFilterPartiesWithMageDupes.set(dungFilterPartiesWithMageDupes); propDungFilterPartiesWithTankDupes.set(dungFilterPartiesWithTankDupes); - propDungPartyFinderPlayerLookup.set(dungPartyFinderPlayerLookup); if (saveToFile && cfg.hasChanged()) { boolean isPlayerIngame = Minecraft.getMinecraft().thePlayer != null; @@ -695,9 +718,11 @@ public class MooConfig { public static boolean isTooltipToggleKeyBindingPressed() { return tooltipToggleKeyBinding > 0 && Keyboard.isKeyDown(tooltipToggleKeyBinding); } + public static boolean isLookupWikiKeyBindingPressed() { return lookupWikiKeyBinding > 0 && Keyboard.isKeyDown(lookupWikiKeyBinding); } + public static boolean isLookupPriceKeyBindingPressed() { return lookupPriceKeyBinding > 0 && Keyboard.isKeyDown(lookupPriceKeyBinding); } diff --git a/src/main/java/de/cowtipper/cowlection/data/HyPlayerData.java b/src/main/java/de/cowtipper/cowlection/data/HyPlayerData.java index 96c3325..5a7c168 100644 --- a/src/main/java/de/cowtipper/cowlection/data/HyPlayerData.java +++ b/src/main/java/de/cowtipper/cowlection/data/HyPlayerData.java @@ -2,6 +2,8 @@ package de.cowtipper.cowlection.data; import net.minecraft.util.EnumChatFormatting; +import java.util.Map; + @SuppressWarnings("unused") public class HyPlayerData { private String displayname; @@ -14,6 +16,7 @@ public class HyPlayerData { private long lastLogin; private long lastLogout; private String mostRecentGameType; + private Map<String, Integer> achievements; /** * No-args constructor for GSON @@ -41,6 +44,13 @@ public class HyPlayerData { return DataHelper.GameType.getFancyName(mostRecentGameType); } + public int getAchievement(String achievementName) { + if (achievements != null) { + return achievements.getOrDefault(achievementName, 0); + } + return 0; + } + public boolean hasNeverJoinedHypixel() { // example player that has never joined Hypixel (as of April 2020): Joe return rank == null && lastLogin == 0; diff --git a/src/main/java/de/cowtipper/cowlection/data/HySkyBlockStats.java b/src/main/java/de/cowtipper/cowlection/data/HySkyBlockStats.java index 7ebd894..242c0c3 100644 --- a/src/main/java/de/cowtipper/cowlection/data/HySkyBlockStats.java +++ b/src/main/java/de/cowtipper/cowlection/data/HySkyBlockStats.java @@ -200,6 +200,15 @@ public class HySkyBlockStats { return pets; } + public Pet getActivePet() { + for (Pet pet : pets) { + if (pet.isActive()) { + return pet; + } + } + return null; + } + public List<String> getArmor() { NBTTagCompound nbt = inv_armor.getDecodedData(); List<String> armorList = new ArrayList<>(); diff --git a/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java b/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java index 0f61bb2..43f49fe 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/ChatListener.java @@ -1,6 +1,7 @@ package de.cowtipper.cowlection.listener; import de.cowtipper.cowlection.Cowlection; +import de.cowtipper.cowlection.config.CredentialStorage; import de.cowtipper.cowlection.config.MooConfig; import de.cowtipper.cowlection.data.Friend; import de.cowtipper.cowlection.data.HySkyBlockStats; @@ -175,7 +176,9 @@ public class ChatListener { messageSender = partyOrGameInviteMatcher.group(1); } else if (dungeonPartyFinderJoinedMatcher.find()) { messageSender = dungeonPartyFinderJoinedMatcher.group(1); - if (MooConfig.getDungPartyFinderPlayerLookupDisplay() != MooConfig.Setting.DISABLED && !messageSender.equals(Minecraft.getMinecraft().thePlayer.getName())) { + if (CredentialStorage.isMooValid && !messageSender.equals(Minecraft.getMinecraft().thePlayer.getName()) + && MooConfig.getDungPartyFinderPlayerLookupDisplay() != MooConfig.Setting.DISABLED) { + // another player joined via Dungeon Party Finder String dungeonClass = dungeonPartyFinderJoinedMatcher.group(2) + " Lvl " + dungeonPartyFinderJoinedMatcher.group(3); getDungeonPartyMemberDetails(messageSender, dungeonClass); } @@ -200,29 +203,48 @@ public class ChatListener { boolean outputAsChatMessages = MooConfig.getDungPartyFinderPlayerLookupDisplay() == MooConfig.Setting.TEXT; HySkyBlockStats.Profile.Member member = activeProfile.getMember(stalkedPlayer.getUuid()); - MooChatComponent armorLookupComponent; String armorLookupPrefix = " ❈ " + EnumChatFormatting.DARK_GREEN + playerName; String delimiter = "\n" + (outputAsChatMessages ? " " : ""); String armorLookupResult = EnumChatFormatting.LIGHT_PURPLE + " ➜ " + EnumChatFormatting.GRAY + dungeonClass + delimiter + String.join(delimiter, member.getArmor()); + // active pet: + HySkyBlockStats.Profile.Pet activePet = member.getActivePet(); + String petInfo = (outputAsChatMessages ? "\n " : "\n\n") + EnumChatFormatting.GRAY + "Active pet: " + (activePet != null ? activePet.toFancyString() : "" + EnumChatFormatting.DARK_GRAY + EnumChatFormatting.ITALIC + "none"); + HySkyBlockStats.Profile.Dungeons dungeons = member.getDungeons(); String highestFloorCompletions = "\n" + (outputAsChatMessages ? " " : "") + EnumChatFormatting.GRAY + "Completed no dungeons yet"; + + String skyBlockDetails; if (outputAsChatMessages) { // highest floor completions: if (dungeons != null && dungeons.hasPlayed()) { highestFloorCompletions = dungeons.getHighestFloorCompletions(1, true).toString(); } - armorLookupComponent = new MooChatComponent(armorLookupPrefix + armorLookupResult + highestFloorCompletions).green(); + skyBlockDetails = armorLookupPrefix + armorLookupResult + petInfo + highestFloorCompletions; } else { // as a tooltip: == MooConfig.Setting.TOOLTIP if (dungeons != null && dungeons.hasPlayed()) { // highest floor completions: highestFloorCompletions = dungeons.getHighestFloorCompletions(3, false).toString(); } - armorLookupComponent = new MooChatComponent(armorLookupPrefix + EnumChatFormatting.GREEN + (playerName.endsWith("s") ? "'" : "'s") + " dungeons info (hover me)").green() - .setHover(new MooChatComponent(EnumChatFormatting.BOLD + playerName + armorLookupResult + highestFloorCompletions)); + skyBlockDetails = EnumChatFormatting.BOLD + playerName + armorLookupResult + petInfo + highestFloorCompletions; } - main.getChatHelper().sendMessage(armorLookupComponent.setSuggestCommand("/p kick " + playerName, outputAsChatMessages)); + + ApiUtils.fetchHyPlayerDetails(stalkedPlayer, hyPlayerData -> { + String foundDungeonsSecrets = ""; + if (hyPlayerData != null) { + foundDungeonsSecrets = "\n" + (outputAsChatMessages ? " " : "") + EnumChatFormatting.GRAY + "Found secrets: " + EnumChatFormatting.GOLD + hyPlayerData.getAchievement("skyblock_treasure_hunter"); + } + + MooChatComponent armorLookupComponent; + if (outputAsChatMessages) { + armorLookupComponent = new MooChatComponent(skyBlockDetails + foundDungeonsSecrets).green(); + } else { + armorLookupComponent = new MooChatComponent(armorLookupPrefix + EnumChatFormatting.GREEN + (playerName.endsWith("s") ? "'" : "'s") + " dungeons info (hover me)").green() + .setHover(new MooChatComponent(skyBlockDetails + foundDungeonsSecrets)); + } + main.getChatHelper().sendMessage(armorLookupComponent.setSuggestCommand("/p kick " + playerName, outputAsChatMessages)); + }); } }); } 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 ba48367..918c63d 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsListener.java @@ -2,6 +2,7 @@ package de.cowtipper.cowlection.listener.skyblock; import com.mojang.realmsclient.util.Pair; import de.cowtipper.cowlection.Cowlection; +import de.cowtipper.cowlection.config.CredentialStorage; import de.cowtipper.cowlection.config.MooConfig; import de.cowtipper.cowlection.config.gui.MooConfigGui; import de.cowtipper.cowlection.data.DataHelper.DungeonClass; @@ -12,15 +13,13 @@ import de.cowtipper.cowlection.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.client.audio.SoundCategory; import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.inventory.Container; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; -import net.minecraft.item.EnumDyeColor; import net.minecraft.item.ItemSkull; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -50,7 +49,7 @@ public class DungeonsListener { /** * example: (space)Robin_Hood: Archer (42) */ - private final Pattern DUNGEON_PARTY_FINDER_PLAYER = Pattern.compile("^ (?:\\w+): ([A-Za-z]+) \\((\\d+)\\)$"); + private final Pattern DUNGEON_PARTY_FINDER_PLAYER = Pattern.compile("^ (\\w+): ([A-Za-z]+) \\((\\d+)\\)$"); /** * examples: "Floor: Entrance", "Floor: Floor 4", "Floor: Floor IV" */ @@ -284,7 +283,7 @@ public class DungeonsListener { // not a player skull, don't draw party status indicator return; } - ItemStack indicatorItem = null; + PartyType partyType = PartyType.NONE; List<String> itemTooltip = item.getTooltip(Minecraft.getMinecraft().thePlayer, false); if (itemTooltip.size() < 5) { @@ -296,35 +295,58 @@ public class DungeonsListener { || lastToolTipLine.startsWith("Requires a Class at Level") || lastToolTipLine.startsWith("Requires Catacombs Level")) { // cannot enter dungeon - indicatorItem = new ItemStack(Blocks.carpet, 1, EnumDyeColor.RED.getMetadata()); + partyType = PartyType.UNJOINABLE; } else if (lastToolTipLine.endsWith("You are in this party!")) { - indicatorItem = new ItemStack(Items.leather, 1); + partyType = PartyType.CURRENT; } else { Map<DungeonClass, AtomicInteger> dungClassesInParty = new LinkedHashMap<>(); AtomicInteger classCounter = new AtomicInteger(); classCounter.incrementAndGet(); dungClassesInParty.put(activeDungeonClass, classCounter); // add our own class + int partySize = 5; + String isCarry = null; boolean memberTooLowLevel = false; for (String toolTipLine : itemTooltip) { - Matcher playerDetailMatcher = DUNGEON_PARTY_FINDER_PLAYER.matcher(EnumChatFormatting.getTextWithoutFormattingCodes(toolTipLine)); + String toolTipLineWithoutFormatting = EnumChatFormatting.getTextWithoutFormattingCodes(toolTipLine); + Matcher playerDetailMatcher = DUNGEON_PARTY_FINDER_PLAYER.matcher(toolTipLineWithoutFormatting); if (playerDetailMatcher.matches()) { - String className = playerDetailMatcher.group(1); + String className = playerDetailMatcher.group(2); DungeonClass clazz = DungeonClass.get(className); dungClassesInParty.putIfAbsent(clazz, new AtomicInteger(0)); dungClassesInParty.get(clazz).incrementAndGet(); - int classLevel = MathHelper.parseIntWithDefault(playerDetailMatcher.group(2), 100); + int classLevel = MathHelper.parseIntWithDefault(playerDetailMatcher.group(3), 100); if (classLevel < MooConfig.dungClassMin) { memberTooLowLevel = true; } + } else if (" Empty".equals(toolTipLineWithoutFormatting)) { + --partySize; + } else if (MooConfig.dungFilterPartiesWithCarry && toolTipLineWithoutFormatting.startsWith("Note: ")) { + String partyNote = toolTipLineWithoutFormatting.toLowerCase(); + if (partyNote.contains("carry") || partyNote.contains("carries")) { + partyType = PartyType.UNIDEAL; + isCarry = partyNote.contains("free") ? "free" : "paid"; + } } } + FontRenderer font = Minecraft.getMinecraft().fontRendererObj; + if (MooConfig.dungFilterPartiesWithCarry && isCarry != null) { + GlStateManager.pushMatrix(); + GlStateManager.translate(0, 0, 281); + double scaleFactor = 0.5; + GlStateManager.scale(scaleFactor, scaleFactor, 0); + int carryColor = "free".equals(isCarry) ? new Color(85, 240, 85, 255).getRGB() : new Color(85, 240, 240, 255).getRGB(); + font.drawStringWithShadow("carry", (float) ((x + 1) / scaleFactor), (float) ((y + 5) / scaleFactor), carryColor); + GlStateManager.popMatrix(); + } if (memberTooLowLevel) { + // at least one party member is lower than the min class level + partyType = PartyType.UNIDEAL; GlStateManager.pushMatrix(); GlStateManager.translate(0, 0, 280); - Minecraft.getMinecraft().fontRendererObj.drawStringWithShadow(EnumChatFormatting.BOLD + "ᐯ", x + 1, y + 8, new Color(220, 20, 20, 255).getRGB()); + font.drawStringWithShadow(EnumChatFormatting.BOLD + "ᐯ", x + 1, y + 8, new Color(220, 20, 20, 255).getRGB()); GlStateManager.popMatrix(); } StringBuilder dupedClasses = new StringBuilder(); @@ -334,25 +356,35 @@ public class DungeonsListener { } } if (dupedClasses.length() > 0) { - dupedClasses.insert(0, EnumChatFormatting.RED).insert(0, "²⁺"); + // party has class duplicates + partyType = PartyType.UNIDEAL; + dupedClasses.insert(0, EnumChatFormatting.YELLOW).insert(0, "²⁺"); // 2+ GlStateManager.pushMatrix(); GlStateManager.translate(0, 0, 280); double scaleFactor = 0.8; GlStateManager.scale(scaleFactor, scaleFactor, 0); - Minecraft.getMinecraft().fontRendererObj.drawStringWithShadow(dupedClasses.toString(), (float) (x / scaleFactor), (float) (y / scaleFactor), new Color(255, 170, 0, 255).getRGB()); + font.drawStringWithShadow(dupedClasses.toString(), (float) (x / scaleFactor), (float) (y / scaleFactor), new Color(255, 170, 0, 255).getRGB()); GlStateManager.popMatrix(); - } else if (!memberTooLowLevel) { + } else if (!memberTooLowLevel && isCarry == null) { // party matches our criteria! - indicatorItem = new ItemStack(Blocks.carpet, 1, EnumDyeColor.LIME.getMetadata()); + partyType = PartyType.SUITABLE; + } + // add party size indicator + if (MooConfig.dungPartiesSize && partySize > 0) { + GlStateManager.pushMatrix(); + GlStateManager.translate(0, 0, 280); + String partySizeIndicator = String.valueOf(partySize); + font.drawStringWithShadow(partySizeIndicator, x + 17 - font.getStringWidth(partySizeIndicator), y + 9, 0xffFFFFFF); + GlStateManager.popMatrix(); } } - if (indicatorItem != null) { - GlStateManager.enableRescaleNormal(); - RenderHelper.enableGUIStandardItemLighting(); - Minecraft.getMinecraft().getRenderItem().renderItemIntoGUI(indicatorItem, x, y); - RenderHelper.disableStandardItemLighting(); - GlStateManager.disableRescaleNormal(); + if (partyType != PartyType.CURRENT + || (/*partyType == PartyType.CURRENT &&*/ Minecraft.getSystemTime() % 1000 < 600)) { + GlStateManager.pushMatrix(); + GlStateManager.translate(0, 0, partyType.getZIndex()); + Gui.drawRect(x, y, x + 16, y + 16, partyType.getColor()); + GlStateManager.popMatrix(); } } @@ -454,11 +486,25 @@ public class DungeonsListener { if (hoveredSlot != null && hoveredSlot.getHasStack()) { // clicked on an item List<String> itemToolTip = hoveredSlot.getStack().getTooltip(Minecraft.getMinecraft().thePlayer, false); - if (itemToolTip.size() < 5) { + if (itemToolTip.size() < 5 || hoveredSlot.getStack().getItem() != Items.skull) { // not a valid dungeon party tooltip return; } extractQueuedFloorNr(itemToolTip, DUNGEON_PARTY_FINDER_FLOOR); + + if (CredentialStorage.isMooValid && MooConfig.dungPartyFinderPartyLookup) { + List<String> partyMembers = new ArrayList<>(); + for (String toolTipLine : itemToolTip) { + String toolTipLineWithoutFormatting = EnumChatFormatting.getTextWithoutFormattingCodes(toolTipLine); + Matcher playerDetailMatcher = DUNGEON_PARTY_FINDER_PLAYER.matcher(toolTipLineWithoutFormatting); + if (playerDetailMatcher.matches()) { + partyMembers.add(playerDetailMatcher.group(1)); + } + } + if (partyMembers.size() > 0) { + new DungeonsPartyListener(main, partyMembers); + } + } } } else if (inventory.getName().equals("Group Builder")) { // get dungeon floor nr when creating a dungeon party for party finder @@ -582,4 +628,28 @@ public class DungeonsListener { } } } + + private enum PartyType { + SUITABLE(0xff32CD32, 240), + UNIDEAL(0xffCD8032, 240), + UNJOINABLE(0xffD75B5B, 279), + CURRENT(0xff5FDE6C, 240), + NONE(0xffFF0000, 279); + + private final float zIndex; + private final int color; + + PartyType(int color, float zIndex) { + this.color = color; + this.zIndex = zIndex; + } + + public float getZIndex() { + return zIndex; + } + + public int getColor() { + return color; + } + } } diff --git a/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsPartyListener.java b/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsPartyListener.java index ed6906a..775634d 100644 --- a/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsPartyListener.java +++ b/src/main/java/de/cowtipper/cowlection/listener/skyblock/DungeonsPartyListener.java @@ -8,12 +8,14 @@ import de.cowtipper.cowlection.data.HySkyBlockStats; import de.cowtipper.cowlection.util.ApiUtils; import de.cowtipper.cowlection.util.MooChatComponent; import de.cowtipper.cowlection.util.Utils; +import net.minecraft.client.Minecraft; import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; @@ -34,15 +36,28 @@ public class DungeonsPartyListener { private final AtomicInteger pendingApiRequests = new AtomicInteger(); private final ConcurrentHashMap<String, Optional<UUID>> partyMembers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, HySkyBlockStats> partyMemberStats = new ConcurrentHashMap<>(); + private final ConcurrentHashMap<String, Integer> partyMemberFoundDungeonSecrets = new ConcurrentHashMap<>(); public DungeonsPartyListener(Cowlection main) { if (nextStep == Step.STOP) { // prevent double-registration of this listener nextStep = Step.START; this.main = main; + // get party members by parsing output of /party list: MinecraftForge.EVENT_BUS.register(this); } } + public DungeonsPartyListener(Cowlection main, List<String> partyMembers) { + if (nextStep == Step.STOP) { // prevent double-registration of this listener + this.main = main; + nextStep = Step.AWAITING_API_RESPONSE; + for (String partyMember : partyMembers) { + this.partyMembers.put(partyMember, Optional.empty()); + } + getDungeonPartyStats(); + } + } + // priority = highest to ignore other mods modifying the chat output @SubscribeEvent(priority = EventPriority.HIGHEST, receiveCanceled = true) public void onMessageReceived(ClientChatReceivedEvent e) { @@ -130,16 +145,28 @@ public class DungeonsPartyListener { private void getDungeonPartyStats() { for (String partyMemberName : partyMembers.keySet()) { pendingApiRequests.incrementAndGet(); + pendingApiRequests.incrementAndGet(); ApiUtils.fetchFriendData(partyMemberName, partyMember -> { // (1) send Mojang API request to get uuid if (partyMember != null && !partyMember.equals(Friend.FRIEND_NOT_FOUND)) { partyMembers.put(partyMemberName, Optional.ofNullable(partyMember.getUuid())); + ApiUtils.fetchSkyBlockStats(partyMember, hySkyBlockStats -> { // (2) once completed, request SkyBlock stats - int apiRequestsLeft = pendingApiRequests.decrementAndGet(); partyMemberStats.put(partyMemberName, hySkyBlockStats); - if (apiRequestsLeft == 0) { + if (pendingApiRequests.decrementAndGet() == 0) { + // (3) wait for all requests to finish + // (4) once completed extract relevant data + nextStep = Step.SEND_PARTY_STATS; + sendDungeonPartyStats(); + } + }); + ApiUtils.fetchHyPlayerDetails(partyMember, hyPlayerData -> { + // (2) once completed, request player stats + partyMemberFoundDungeonSecrets.put(partyMemberName, (hyPlayerData != null ? hyPlayerData.getAchievement("skyblock_treasure_hunter") : 0)); + + if (pendingApiRequests.decrementAndGet() == 0) { // (3) wait for all requests to finish // (4) once completed extract relevant data nextStep = Step.SEND_PARTY_STATS; @@ -149,23 +176,24 @@ public class DungeonsPartyListener { } else { // player not found (nicked?) pendingApiRequests.decrementAndGet(); + pendingApiRequests.decrementAndGet(); } }); } } private void sendDungeonPartyStats() { + String thePlayerName = Minecraft.getMinecraft().thePlayer.getName(); MooChatComponent dungeonsParty = new MooChatComponent("Dungeons party").gold().bold(); StringBuilder playerEntry = new StringBuilder(); StringBuilder playerTooltip = new StringBuilder(); String partyMemberName = ""; for (Map.Entry<String, Optional<UUID>> partyMember : partyMembers.entrySet()) { - partyMemberName = partyMember.getKey(); if (playerEntry.length() > 0) { // append previous data MooChatComponent dungeonPartyEntry = new MooChatComponent(playerEntry.toString()) - .setSuggestCommand("/p kick " + partyMemberName, false); + .setSuggestCommand((partyMemberName.equals(thePlayerName) ? "/boop " : "/p kick ") + partyMemberName, false); if (playerTooltip.length() > 0) { dungeonPartyEntry.setHover(new MooChatComponent(playerTooltip.toString())); } @@ -174,6 +202,7 @@ public class DungeonsPartyListener { playerEntry.setLength(0); playerTooltip.setLength(0); } + partyMemberName = partyMember.getKey(); String errorNamePrefix = " " + EnumChatFormatting.RED + partyMemberName + EnumChatFormatting.LIGHT_PURPLE + " ➜ " + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC; if (!partyMember.getValue().isPresent()) { @@ -185,7 +214,6 @@ public class DungeonsPartyListener { // player name: playerEntry.append(" ").append(EnumChatFormatting.DARK_GREEN).append(partyMemberName).append(EnumChatFormatting.LIGHT_PURPLE).append(" ➜ ").append(EnumChatFormatting.GRAY); - HySkyBlockStats sbStats = partyMemberStats.get(partyMemberName); if (sbStats != null && sbStats.isSuccess()) { HySkyBlockStats.Profile activeProfile = sbStats.getActiveProfile(uuid); @@ -199,9 +227,13 @@ public class DungeonsPartyListener { // ^ abort if any of the above failed, otherwise visualize API data: HySkyBlockStats.Profile.Member member = activeProfile.getMember(uuid); - // append player name + class and class level + armor + // active pet: + HySkyBlockStats.Profile.Pet activePet = member.getActivePet(); + + // append player name + class and class level + armor + active pet playerTooltip.append(EnumChatFormatting.WHITE).append(EnumChatFormatting.BOLD).append(partyMemberName).append(EnumChatFormatting.LIGHT_PURPLE).append(" ➜ ").append(EnumChatFormatting.GRAY).append(EnumChatFormatting.ITALIC).append("no class selected") - .append("\n").append(String.join("\n", member.getArmor())); + .append("\n").append(String.join("\n", member.getArmor())) + .append("\n\n").append(EnumChatFormatting.GRAY).append("Active pet: ").append(activePet != null ? activePet.toFancyString() : "" + EnumChatFormatting.DARK_GRAY + EnumChatFormatting.ITALIC + "none"); HySkyBlockStats.Profile.Dungeons dungeons = member.getDungeons(); boolean hasNotPlayedDungeons = dungeons == null || !dungeons.hasPlayed(); @@ -221,13 +253,16 @@ public class DungeonsPartyListener { // highest floor completions: playerTooltip.append(dungeons.getHighestFloorCompletions(3, false)); + + // found dungeon secrets: + playerTooltip.append("\n").append(EnumChatFormatting.GRAY).append("Found secrets: ").append(EnumChatFormatting.GOLD).append(partyMemberFoundDungeonSecrets.getOrDefault(partyMemberName, 0)); } else { playerEntry.setLength(0); playerEntry.append(errorNamePrefix).append("API error").append(sbStats != null && sbStats.getCause() != null ? ": " + sbStats.getCause() : ""); } } MooChatComponent dungeonPartyEntry = new MooChatComponent(playerEntry.toString()) - .setSuggestCommand("/p kick " + partyMemberName, false); + .setSuggestCommand((partyMemberName.equals(thePlayerName) ? "/boop " : "/p kick ") + partyMemberName, false); if (playerTooltip.length() > 0) { dungeonPartyEntry.setHover(new MooChatComponent(playerTooltip.toString())); } diff --git a/src/main/java/de/cowtipper/cowlection/util/GuiHelper.java b/src/main/java/de/cowtipper/cowlection/util/GuiHelper.java index 8ce23e5..decc579 100644 --- a/src/main/java/de/cowtipper/cowlection/util/GuiHelper.java +++ b/src/main/java/de/cowtipper/cowlection/util/GuiHelper.java @@ -5,7 +5,6 @@ import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.Gui; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; diff --git a/src/main/resources/assets/cowlection/lang/en_US.lang b/src/main/resources/assets/cowlection/lang/en_US.lang index 0972533..fc8b2e0 100644 --- a/src/main/resources/assets/cowlection/lang/en_US.lang +++ b/src/main/resources/assets/cowlection/lang/en_US.lang @@ -70,8 +70,16 @@ cowlection.config.dungOverlayGuiScale=Overlay Gui Scale cowlection.config.dungOverlayGuiScale.tooltip=Adjust the size of the dungeon performance overlay (50%%-200%%) cowlection.config.dungOverlayTextShadow=Add text shadow cowlection.config.dungOverlayTextShadow.tooltip=Enable or disable text shadow +cowlection.config.dungPartyFinderPlayerLookup=Show info of joining players §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.config.dungPartyFinderPartyLookup=Lookup info when joining another party? §d§l⚷ +cowlection.config.dungPartyFinderPartyLookup.tooltip=Lookup armor and dungeons stats of each party member when joining via party finder?\n§7Alternatively: §e/moo dungeon party §7or §e/m dp\n§d§l⚷ §eRequires a valid API key! +cowlection.config.dungPartiesSize=Show party sizes? +cowlection.config.dungPartiesSize.tooltip=Show sizes of parties? cowlection.config.dungClassMin=Minimum preferred class level cowlection.config.dungClassMin.tooltip=Marks parties with members with lower class level than this value +cowlection.config.dungFilterPartiesWithCarry=Mark 'carry' parties? +cowlection.config.dungFilterPartiesWithCarry.tooltip=Mark parties that have 'carry' in their notes? cowlection.config.dungFilterPartiesWithArcherDupes=Mark duplicated Archer class? cowlection.config.dungFilterPartiesWithArcherDupes.tooltip=Mark parties with duplicated Archer class? cowlection.config.dungFilterPartiesWithBerserkDupes=Mark duplicated Berserk class? @@ -82,8 +90,6 @@ 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.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 |