aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--README.md1
-rw-r--r--src/main/java/eu/olli/cowlection/Cowlection.java18
-rw-r--r--src/main/java/eu/olli/cowlection/command/MooCommand.java11
-rw-r--r--src/main/java/eu/olli/cowlection/handler/DungeonCache.java52
-rw-r--r--src/main/java/eu/olli/cowlection/listener/PlayerListener.java13
-rw-r--r--src/main/java/eu/olli/cowlection/listener/skyblock/DungeonsListener.java75
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` 🎉
diff --git a/README.md b/README.md
index 6efe063..89b7ccb 100644
--- a/README.md
+++ b/README.md
@@ -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);
+ }
+ }
+ }
}