diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | src/main/java/eu/olli/cowlection/Cowlection.java | 18 | ||||
-rw-r--r-- | src/main/java/eu/olli/cowlection/command/MooCommand.java | 11 | ||||
-rw-r--r-- | src/main/java/eu/olli/cowlection/handler/DungeonCache.java | 52 | ||||
-rw-r--r-- | src/main/java/eu/olli/cowlection/listener/PlayerListener.java | 13 | ||||
-rw-r--r-- | src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java | 75 |
7 files changed, 153 insertions, 19 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 83686aa..0526450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [1.8.9-0.9.0] - unreleased ### Added +- Added SkyBlock Dungeon deaths counter + - sends current deaths automatically; or manually with `/moo deaths` - New alias for `/moo` command: `/m` - Config option: Change position of item quality in tooltip of dungeon items - Added `/moo say [optional text]`: You can say `moo` again without triggering the command `/moo` 🎉 @@ -26,6 +26,7 @@ It is a collection of different features mainly focused on Hypixel SkyBlock. | Stalk SkyBlock stats of a player | `/moo stalkskyblock` | | Analyze minions on a private island | `/moo analyzeIsland` | | Dungeon interfaces enhancements (normalize dungeon item stats, improved party finder) | Hold <kbd>shift</kbd> while viewing a dungeon item tooltip | +| Dungeon death counter | automatically; or with `/moo deaths` | ## Download You can download the compiled .jar files from the [release section](https://github.com/cow-mc/Cowlection/releases). diff --git a/src/main/java/eu/olli/cowlection/Cowlection.java b/src/main/java/eu/olli/cowlection/Cowlection.java index 9ce14c3..2811e15 100644 --- a/src/main/java/eu/olli/cowlection/Cowlection.java +++ b/src/main/java/eu/olli/cowlection/Cowlection.java @@ -4,6 +4,7 @@ import eu.olli.cowlection.command.MooCommand; import eu.olli.cowlection.command.ShrugCommand; import eu.olli.cowlection.command.TabCompletableCommand; import eu.olli.cowlection.config.MooConfig; +import eu.olli.cowlection.handler.DungeonCache; import eu.olli.cowlection.handler.FriendsHandler; import eu.olli.cowlection.handler.PlayerCache; import eu.olli.cowlection.listener.ChatListener; @@ -39,7 +40,7 @@ public class Cowlection { private VersionChecker versionChecker; private ChatHelper chatHelper; private PlayerCache playerCache; - private boolean isOnSkyBlock; + private DungeonCache dungeonCache; private Logger logger; @Mod.EventHandler @@ -96,6 +97,13 @@ public class Cowlection { return playerCache; } + public DungeonCache getDungeonCache() { + if (dungeonCache == null) { + dungeonCache = new DungeonCache(this); + } + return dungeonCache; + } + public File getConfigDirectory() { return configDir; } @@ -108,14 +116,6 @@ public class Cowlection { return logger; } - public boolean isOnSkyBlock() { - return isOnSkyBlock; - } - - public void setIsOnSkyBlock(boolean isOnSkyBlock) { - this.isOnSkyBlock = isOnSkyBlock; - } - /** * Get mod's instance; instead of this method use dependency injection where possible */ diff --git a/src/main/java/eu/olli/cowlection/command/MooCommand.java b/src/main/java/eu/olli/cowlection/command/MooCommand.java index 0f469fb..26ccb35 100644 --- a/src/main/java/eu/olli/cowlection/command/MooCommand.java +++ b/src/main/java/eu/olli/cowlection/command/MooCommand.java @@ -11,6 +11,7 @@ import eu.olli.cowlection.data.DataHelper; import eu.olli.cowlection.data.Friend; import eu.olli.cowlection.data.HySkyBlockStats; import eu.olli.cowlection.data.HyStalkingData; +import eu.olli.cowlection.handler.DungeonCache; import eu.olli.cowlection.search.GuiSearch; import eu.olli.cowlection.util.*; import net.minecraft.client.Minecraft; @@ -146,6 +147,13 @@ public class MooCommand extends CommandBase { .append(EnumChatFormatting.RED).append("Unknown minion ").append(EnumChatFormatting.YELLOW).append("(new or with minion skin) ").append(tierColor).append(minionTier); }); main.getChatHelper().sendMessage(EnumChatFormatting.YELLOW, analysisResults.toString()); + } else if (args[0].equalsIgnoreCase("deaths")) { + DungeonCache dungeonCache = main.getDungeonCache(); + if (dungeonCache.isInDungeon()) { + dungeonCache.sendDeathCounts(); + } else { + throw new MooCommandException(EnumChatFormatting.DARK_RED + "Looks like you're not in a dungeon..."); + } } else if (args[0].equalsIgnoreCase("add")) { handleBestFriendAdd(args); } else if (args[0].equalsIgnoreCase("remove")) { @@ -498,6 +506,7 @@ public class MooCommand extends CommandBase { .appendSibling(createCmdHelpEntry("stalk", "Get info of player's status")) .appendSibling(createCmdHelpEntry("stalkskyblock", "Get info of player's SkyBlock stats")) .appendSibling(createCmdHelpEntry("analyzeIsland", "Analyze a SkyBlock private island")) + .appendSibling(createCmdHelpEntry("deaths", "SkyBlock Dungeons: death counts")) .appendSibling(createCmdHelpEntry("add", "Add best friends")) .appendSibling(createCmdHelpEntry("remove", "Remove best friends")) .appendSibling(createCmdHelpEntry("list", "View list of best friends")) @@ -536,7 +545,7 @@ public class MooCommand extends CommandBase { public List<String> addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) { if (args.length == 1) { return getListOfStringsMatchingLastWord(args, - /* friends & other players */ "stalk", "stalkskyblock", "skyblockstalk", "analyzeIsland", "add", "remove", "list", "nameChangeCheck", "toggle", + /* friends & other players */ "stalk", "stalkskyblock", "skyblockstalk", "analyzeIsland", "deaths", "add", "remove", "list", "nameChangeCheck", "toggle", /* miscellaneous */ "config", "search", "guiscale", "shrug", "apikey", /* update mod */ "update", "updateHelp", "version", "directory", /* help */ "help"); diff --git a/src/main/java/eu/olli/cowlection/handler/DungeonCache.java b/src/main/java/eu/olli/cowlection/handler/DungeonCache.java new file mode 100644 index 0000000..0431685 --- /dev/null +++ b/src/main/java/eu/olli/cowlection/handler/DungeonCache.java @@ -0,0 +1,52 @@ +package eu.olli.cowlection.handler; + +import eu.olli.cowlection.Cowlection; +import eu.olli.cowlection.util.TickDelay; +import net.minecraft.util.EnumChatFormatting; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class DungeonCache { + private boolean isInDungeon; + private final Map<String, Integer> deathCounter; + private final Cowlection main; + + public DungeonCache(Cowlection main) { + this.main = main; + deathCounter = new HashMap<>(); + } + + public void onDungeonEntered() { + isInDungeon = true; + deathCounter.clear(); + } + + public void onDungeonLeft() { + isInDungeon = false; + deathCounter.clear(); + } + + public void addDeath(String playerName) { + int previousPlayerDeaths = deathCounter.getOrDefault(playerName, 0); + deathCounter.put(playerName, previousPlayerDeaths + 1); + + new TickDelay(this::sendDeathCounts, 1); + } + + public boolean isInDungeon() { + return isInDungeon; + } + + public void sendDeathCounts() { + if (deathCounter.isEmpty()) { + main.getChatHelper().sendMessage(EnumChatFormatting.GOLD, "☠ Deaths: " + EnumChatFormatting.WHITE + "none \\o/"); + } else { + String deaths = deathCounter.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).map(deathEntry -> " " + EnumChatFormatting.WHITE + deathEntry.getKey() + ": " + EnumChatFormatting.LIGHT_PURPLE + deathEntry.getValue()) + .collect(Collectors.joining("\n")); + main.getChatHelper().sendMessage(EnumChatFormatting.RED, "☠ " + EnumChatFormatting.BOLD + "Deaths:\n" + deaths); + } + } +} diff --git a/src/main/java/eu/olli/cowlection/listener/PlayerListener.java b/src/main/java/eu/olli/cowlection/listener/PlayerListener.java index fe15a4f..aa91678 100644 --- a/src/main/java/eu/olli/cowlection/listener/PlayerListener.java +++ b/src/main/java/eu/olli/cowlection/listener/PlayerListener.java @@ -27,6 +27,7 @@ public class PlayerListener { private final Cowlection main; private DungeonsListener dungeonsListener; private SkyBlockListener skyBlockListener; + private boolean isOnSkyBlock; public PlayerListener(Cowlection main) { this.main = main; @@ -71,7 +72,7 @@ public class PlayerListener { public void onServerJoin(FMLNetworkEvent.ClientConnectedToServerEvent e) { main.getVersionChecker().runUpdateCheck(false); new TickDelay(() -> main.getChatHelper().sendOfflineMessages(), 6 * 20); - main.setIsOnSkyBlock(false); + isOnSkyBlock = false; } @SubscribeEvent @@ -79,14 +80,14 @@ public class PlayerListener { // check if player is on SkyBlock or on another gamemode new TickDelay(() -> { ScoreObjective scoreboardSidebar = e.entityPlayer.worldObj.getScoreboard().getObjectiveInDisplaySlot(1); - boolean wasOnSkyBlock = main.isOnSkyBlock(); - main.setIsOnSkyBlock(scoreboardSidebar != null && EnumChatFormatting.getTextWithoutFormattingCodes(scoreboardSidebar.getDisplayName()).startsWith("SKYBLOCK")); + boolean wasOnSkyBlock = isOnSkyBlock; + isOnSkyBlock = (scoreboardSidebar != null && EnumChatFormatting.getTextWithoutFormattingCodes(scoreboardSidebar.getDisplayName()).startsWith("SKYBLOCK")); - if (!wasOnSkyBlock && main.isOnSkyBlock()) { + if (!wasOnSkyBlock && isOnSkyBlock) { // player wasn't on SkyBlock before but now is on SkyBlock main.getLogger().info("Entered SkyBlock! Registering SkyBlock listeners"); registerSkyBlockListeners(); - } else if (wasOnSkyBlock && !main.isOnSkyBlock()) { + } else if (wasOnSkyBlock && !isOnSkyBlock) { // player was on SkyBlock before and is now in another gamemode unregisterSkyBlockListeners(); main.getLogger().info("Leaving SkyBlock! Un-registering SkyBlock listeners"); @@ -104,6 +105,7 @@ public class PlayerListener { } private void unregisterSkyBlockListeners() { + main.getDungeonCache().onDungeonLeft(); if (dungeonsListener != null) { MinecraftForge.EVENT_BUS.unregister(dungeonsListener); dungeonsListener = null; @@ -116,7 +118,6 @@ public class PlayerListener { @SubscribeEvent public void onServerLeave(FMLNetworkEvent.ClientDisconnectionFromServerEvent e) { - main.setIsOnSkyBlock(false); main.getFriendsHandler().saveBestFriends(); main.getPlayerCache().clearAllCaches(); unregisterSkyBlockListeners(); diff --git a/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java b/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java index eedd0b7..0ea2a0c 100644 --- a/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java +++ b/src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java @@ -2,6 +2,7 @@ package eu.olli.cowlection.listener.skyblock; import eu.olli.cowlection.Cowlection; import eu.olli.cowlection.config.MooConfig; +import eu.olli.cowlection.util.TickDelay; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.renderer.GlStateManager; @@ -11,21 +12,25 @@ import net.minecraft.inventory.Slot; import net.minecraft.item.ItemSkull; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.scoreboard.Score; +import net.minecraft.scoreboard.ScoreObjective; +import net.minecraft.scoreboard.ScorePlayerTeam; +import net.minecraft.scoreboard.Scoreboard; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.MathHelper; +import net.minecraftforge.client.event.ClientChatReceivedEvent; import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.common.util.Constants; import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.event.entity.player.PlayerSetSpawnEvent; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import org.apache.commons.lang3.StringUtils; import org.lwjgl.input.Keyboard; import java.awt.*; -import java.util.HashSet; import java.util.List; -import java.util.ListIterator; -import java.util.Set; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -58,6 +63,17 @@ public class DungeonsListener { * </pre> */ private final Pattern TOOLTIP_LINE_PATTERN = Pattern.compile("^(?<prefix>(?:" + FORMATTING_CODE + ")+[A-Za-z ]+: " + FORMATTING_CODE + ")(?<statNonDungeon>[+-]?[0-9]+)(?<statNonDungeonUnit>%| HP|)(?: (?<colorReforge>" + FORMATTING_CODE + ")\\((?<reforge>[A-Za-z]+) (?<statReforge>[+-]?[0-9]+)(?<statReforgeUnit>%| HP|)\\))?(?: (?<colorDungeon>" + FORMATTING_CODE + ")\\((?<statDungeon>[+-]?[.0-9]+)(?<statDungeonUnit>%| HP|)\\))?$"); + /** + * Player deaths in dungeon: + * <ul> + * <li> ☠ [player] disconnected from the Dungeon and became a ghost.</li> + * <li> ☠ [player/You] died and became a ghost.</li> + * <li> ☠ [player] fell to their death with help from [mob] and became a ghost.</li> + * <li> ☠ [player] was killed by [mob] and became a ghost.</li> + * <li> ☠ You were killed by [mob] and became a ghost.</li> + * </ul> + */ + private final Pattern DUNGEON_DEATH_PATTERN = Pattern.compile("^ ☠ (\\w+) (?:.*?) and became a ghost\\.$"); private String activeDungeonClass; @@ -323,4 +339,57 @@ public class DungeonsListener { } Minecraft.getMinecraft().fontRendererObj.drawStringWithShadow(status, x, y, color.getRGB()); } + + // Events inside dungeons + @SubscribeEvent + public void onDungeonsEnterOrLeave(PlayerSetSpawnEvent e) { + // check if player has entered or left a SkyBlock dungeon + new TickDelay(() -> { + Scoreboard scoreboard = e.entityPlayer.worldObj.getScoreboard(); + ScoreObjective scoreboardSidebar = scoreboard.getObjectiveInDisplaySlot(1); + if (scoreboardSidebar == null) { + return; + } + boolean wasInDungeon = main.getDungeonCache().isInDungeon(); + + Collection<Score> scoreboardLines = scoreboard.getSortedScores(scoreboardSidebar); + for (Score line : scoreboardLines) { + ScorePlayerTeam scorePlayerTeam = scoreboard.getPlayersTeam(line.getPlayerName()); + if (scorePlayerTeam != null) { + String lineWithoutFormatting = EnumChatFormatting.getTextWithoutFormattingCodes(scorePlayerTeam.getColorPrefix() + scorePlayerTeam.getColorSuffix()); + + if (lineWithoutFormatting.startsWith(" ⏣")) { + boolean isInDungeonNow = lineWithoutFormatting.startsWith(" ⏣ The Catacombs"); + + if (!wasInDungeon && isInDungeonNow) { + main.getLogger().info("Entered SkyBlock Dungeon!"); + main.getDungeonCache().onDungeonEntered(); + } else if (wasInDungeon && !isInDungeonNow) { + main.getLogger().info("Leaving SkyBlock Dungeon!"); + main.getDungeonCache().onDungeonLeft(); + } + return; + } + } + } + }, 20); // 1 second delay, making sure scoreboard got sent + } + + @SubscribeEvent + public void onMessageReceived(ClientChatReceivedEvent e) { + if (main.getDungeonCache().isInDungeon() && e.type != 2) { // normal chat or system msg (not above action bar) + String text = EnumChatFormatting.getTextWithoutFormattingCodes(e.message.getUnformattedText()); + Matcher dungeonDeathMatcher = DUNGEON_DEATH_PATTERN.matcher(text); + if (dungeonDeathMatcher.matches()) { + String playerName = dungeonDeathMatcher.group(1); + if (playerName.equals("You")) { + playerName = Minecraft.getMinecraft().thePlayer.getName(); + } + main.getDungeonCache().addDeath(playerName); + } else if (text.trim().equals("> EXTRA STATS <")) { + // dungeon "end screen" + new TickDelay(() -> main.getDungeonCache().sendDeathCounts(), 5); + } + } + } } |