diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 93ccfe1fabd5..18ad9d2b53c1 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -218,6 +218,7 @@ import at.hannibal2.skyhanni.features.misc.SkyBlockKickDuration import at.hannibal2.skyhanni.features.misc.SuperpairsClicksAlert import at.hannibal2.skyhanni.features.misc.TimeFeatures import at.hannibal2.skyhanni.features.misc.TpsCounter +import at.hannibal2.skyhanni.features.misc.CustomScoreboard import at.hannibal2.skyhanni.features.misc.compacttablist.AdvancedPlayerList import at.hannibal2.skyhanni.features.misc.compacttablist.TabListReader import at.hannibal2.skyhanni.features.misc.compacttablist.TabListRenderer @@ -615,6 +616,7 @@ class SkyHanniMod { loadModule(ShiftClickEquipment()) loadModule(LockMouseLook) loadModule(DungeonFinderFeatures()) + loadModule(CustomScoreboard()) init() diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt index 1e32ff71802a..616b76d625e0 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -36,6 +36,7 @@ import at.hannibal2.skyhanni.test.PacketTest import at.hannibal2.skyhanni.test.SkyHanniConfigSearchResetCommand import at.hannibal2.skyhanni.test.SkyHanniDebugsAndTests import at.hannibal2.skyhanni.test.TestBingo +import at.hannibal2.skyhanni.test.command.CopyActionBar import at.hannibal2.skyhanni.test.command.CopyItemCommand import at.hannibal2.skyhanni.test.command.CopyNearbyEntitiesCommand import at.hannibal2.skyhanni.test.command.CopyNearbyParticlesCommand @@ -289,6 +290,9 @@ object Commands { "shconfigmanagerreset", "Reloads the config manager and rendering processors of MoulConfig. This §cWILL RESET §7your config, but also updating the java config files (names, description, orderings and stuff)." ) { SkyHanniDebugsAndTests.configManagerResetCommand(it) } + "shcopyactionbar", + "Copies the actionbar to the clipboard" + ) { CopyActionBar.command(it) } } private fun internalCommands() { diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java index 86f60a8f7f46..5e383fb6d7b1 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java @@ -708,6 +708,93 @@ public static class KickDurationConfig { public Position position = new Position(400, 200, 1.3f); } + @Expose + @ConfigOption(name = "Custom Scoreboard", desc = "") + @Accordion + public MiscConfig.CustomScoreboard customScoreboard = new MiscConfig.CustomScoreboard(); + + public static class CustomScoreboard { + + @Expose + @ConfigOption( + name = "Enabled", + desc = "Show a custom scoreboard instead of the default one." //TODO: MAKE COOLER + ) + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption( + name = "Text Format", + desc = "Drag text to change the appearance of the overlay." + ) + @ConfigEditorDraggableList( + exampleText = { + "§6§lSKYBLOCK", + "§7Profile", + "§ePurse", + "§dMotes", + "§eBank", + "§bBits", + "§cCopper", + "§aGems", + "", + "§7Location", + "§7Ingame Time", + "§7Current Server", + "§7Powder\n §fMithril: §254,646\n §fGemstone: §d51,234", + "", + "§cSlayer", + "§7Current Event", + "§7Current Mayor", + "", + "§cHeat", + "§9Party:\n- hannibal2\n- Moulberry\n- Vahvl\n- J10a1n15", + "§7Maxwell Power", + "§ewww.hypixel.net", + } + ) + public List textFormat = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21)); + + @Expose + @ConfigOption(name = "Max Party List", desc = "Max number of party members to show in the party list. (You are not included)") + @ConfigEditorSlider( + minValue = 1, + maxValue = 25, // why do I even set it so high + minStep = 1 + ) + public Property maxPartyList = Property.of(4); + + @Expose + @ConfigOption(name = "Hide lines with no info", desc = "Hide lines that have no info to display, like hiding the party when not being in one.") + @ConfigEditorBoolean + @FeatureToggle + public boolean hideEmptyLines = true; + + @Expose + @ConfigOption(name = "Hide Info not relevant to location", desc = "Hide lines that are not relevant to the current location, like hiding copper while not in garden") + @ConfigEditorBoolean + @FeatureToggle + public boolean hideIrrelevantLines = true; + + @Expose + @ConfigOption(name = "Display Numbers First", desc = "Determines whether the number or line name displays first. " + + "§eNote: Will not update the preview above!") + @ConfigEditorBoolean + @FeatureToggle + public boolean displayNumbersFirst = false; + + @Expose + @ConfigOption(name = "Show Mayor Perks", desc = "Show the perks of the current mayor.") + @ConfigEditorBoolean + @FeatureToggle + public boolean showMayorPerks = true; + + @Expose + public Position position = new Position(10, 80, false, true); + } + @Expose @ConfigOption(name = "Exp Bottles", desc = "Hides all the experience orbs lying on the ground.") @ConfigEditorBoolean diff --git a/src/main/java/at/hannibal2/skyhanni/data/ActionBarStatsData.kt b/src/main/java/at/hannibal2/skyhanni/data/ActionBarStatsData.kt index cddf83d0bdcb..91aeda7449c5 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/ActionBarStatsData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/ActionBarStatsData.kt @@ -15,11 +15,14 @@ object ActionBarStatsData { ) var groups = mutableMapOf("health" to "", "riftTime" to "", "defense" to "", "mana" to "") + var actionBar = "" @SubscribeEvent fun onActionBar(event: LorenzActionBarEvent) { if (!LorenzUtils.inSkyBlock) return + actionBar = event.message + for ((groupName, pattern) in patterns) { pattern.matchMatcher(event.message) { groups[groupName] = group(groupName) diff --git a/src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt b/src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt index d4ad57e2d531..169e4fc45bd9 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt @@ -20,7 +20,7 @@ class MayorElection { companion object { var rawMayorData: MayorJson? = null var candidates = mapOf() - var currentCandidate: MayorJson.Candidate? = null + var currentCandidate: MayorJson.Candidate? = null //todo: should it not be called currentMayor? fun isPerkActive(mayor: String, perk: String) = currentCandidate?.let { currentCandidate -> currentCandidate.name == mayor && currentCandidate.perks.any { it.name == perk } diff --git a/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt index cb3b2103197e..77c734b837c0 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt @@ -12,9 +12,12 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent class PurseAPI { // TODO USE SH-REPO private val pattern = "(Piggy|Purse): §6(?[\\d,]*).*".toPattern() - private var currentPurse = 0.0 private var inventoryCloseTime = 0L + companion object { + var currentPurse = 0.0 + } + @SubscribeEvent fun onInventoryClose(event: InventoryCloseEvent) { inventoryCloseTime = System.currentTimeMillis() diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CustomScoreboard.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CustomScoreboard.kt new file mode 100644 index 000000000000..5ae024fff4e0 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CustomScoreboard.kt @@ -0,0 +1,399 @@ +// +// Requested by alpaka8123 (https://discord.com/channels/997079228510117908/1162844830360146080) +// Done by J10a1n15, with lots of help from hanni, and snippets from item tracker features <3 +// +// I'm also like really sorry for anyone who has to look at this code, it looks kinda bad +// + +// +// TODO LIST +// V1 RELEASE +// - enums prob (why) +// - toggle between " " and " " +// - Hide default scoreboard +// - the things that arent done yet +// +// V2 RELEASE +// - Soulflow API +// - Bank API +// - Custom Scoreboard Background +// - quiver +// - icons +// - beacon power +// - skyblock level +// - commissions +// + +package at.hannibal2.skyhanni.features.misc + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.HypixelData +import at.hannibal2.skyhanni.data.ScoreboardData +import at.hannibal2.skyhanni.data.PurseAPI +import at.hannibal2.skyhanni.data.MayorElection +import at.hannibal2.skyhanni.data.PartyAPI +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase +import at.hannibal2.skyhanni.utils.TabListData +import at.hannibal2.skyhanni.utils.TimeUtils.formatted +import io.github.moulberry.notenoughupdates.util.SkyBlockTime +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import java.util.* + +private val config get() = SkyHanniMod.feature.misc.customScoreboard +private var display = emptyList>() +private var partyCount = 0 + +// Stats / Numbers +private var purse = "0" +private var motes = "0" +private var bank = "0" +private var bits = "0" +private var copper = "0" +private var gems = "0" +private var location = "None" +private var lobbyCode = "None" +private var heat = "0" +private var mithrilPowder = "0" +private var gemstonePowder = "0" + + +enum class CustomScoreboardElements ( + // displayLine: The line that is displayed on the scoreboard + val displayLine: List, + + // alternativeLine: The line that is displayed on the scoreboard when "displayNumbersFirst" is enabled + val alternativeLine: List, + + // islands: The islands that this line is displayed on + val islands: List, + + // visibilityOption: The option that is used to hide this line - use 0 to only display on the listed islands, 1 to hide on the listed islands + val visibilityOption : Int, + + // index: The index of the line + val index: Int, + + // data: The data that is used for this line + val data: String = "" +){ + SKYBLOCK( + listOf("§6§lSKYBLOCK"), + listOf(), + listOf(), + 0, + 0 + ), + PROFILE( + listOf(getProfileTypeAsSymbol() + HypixelData.profileName.firstLetterUppercase()), + listOf(), + listOf(), + 0, + 1 + ), + PURSE( + listOf("Purse: §6$purse"), + listOf("§6$purse Purse"), + listOf(IslandType.THE_RIFT), + 1, + 2, + purse + ), + MOTES( + listOf("Motes: §d$motes"), + listOf("§d$motes Motes"), + listOf(IslandType.THE_RIFT), + 0, + 3, + motes + ), + BANK( + listOf("Bank: §6$bank"), + listOf("§6$bank Bank"), + listOf(IslandType.THE_RIFT), + 1, + 4, + bank + ), + BITS( + listOf("Bits: §b$bits"), + listOf("§b$bits Bits"), + listOf(IslandType.THE_RIFT), + 1, + 5, + bits + ), + COPPER( + listOf("Copper: §c$copper"), + listOf("§c$copper Copper"), + listOf(IslandType.GARDEN), + 0, + 6, + copper + ), + GEMS( + listOf("Gems: §a$gems"), + listOf("§a$gems Gems"), + listOf(IslandType.THE_RIFT), + 1, + 7, + gems + ), + EMPTY_LINE( + listOf(""), + listOf(), + listOf(), + 0, + 8 + ), + LOCATION( + listOf(location), + listOf(), + listOf(), + 0, + 9 + ), + SKYBLOCK_TIME( + listOf(SkyBlockTime.now().formatted(false)), + listOf(), + listOf(), + 0, + 10 + ), + LOBBY_CODE( + listOf("§8$lobbyCode"), + listOf(), + listOf(), + 0, + 11 + ), + POWDER( + listOf("§9§lPowder") + (" §7- §fMithril: §2$mithrilPowder") + (" §7- §fGemstone: §d$gemstonePowder"), + listOf("§9§lPowder") + (" §7- §2$mithrilPowder Mithril") + (" §7- §d$gemstonePowder Gemstone"), + listOf(IslandType.CRYSTAL_HOLLOWS, IslandType.DWARVEN_MINES), + 0, + 12 + ), + EMPTY_LINE2( + listOf(""), + listOf(), + listOf(), + 0, + 13 + ), + SLAYER( + listOf("§7Slayer"), + listOf(""), + listOf(IslandType.HUB, IslandType.SPIDER_DEN, IslandType.THE_PARK, IslandType.THE_END, IslandType.CRIMSON_ISLE), + 0, + 14 + ), + CURRENT_EVENT( + listOf("§cCurrent Event"), + listOf(""), + listOf(), + 0, + 15 + ), + MAYOR( + listOf( + MayorElection.currentCandidate?.name?.let { translateMayorNameToColor(it) } ?: "" + ) + (if (config.showMayorPerks) { + MayorElection.currentCandidate?.perks?.map { " §7- §e${it.name}" } ?: emptyList() + } else { + emptyList() + }), + listOf(), + listOf(IslandType.THE_RIFT), + 1, + 16 + ), + EMPTY_LINE3( + listOf(""), + listOf(), + listOf(), + 0, + 17 + ), + HEAT( + listOf(if(heat == "0") "Heat: §c♨ 0" else "Heat: $heat"), + listOf(if(heat == "0") "§c♨ 0 Heat" else "$heat Heat"), + listOf(IslandType.CRYSTAL_HOLLOWS), + 0, + 18, + heat + ), + PARTY( + listOf( + "§9Party", + *PartyAPI.partyMembers.takeWhile { partyCount < config.maxPartyList.get() } + .map { " §7- §7$it" } + .toTypedArray() + ), + listOf(), + listOf(IslandType.CATACOMBS, IslandType.DUNGEON_HUB, IslandType.KUUDRA_ARENA, IslandType.CRIMSON_ISLE), + 0, + 19, + partyCount.toString() + ), + MAXWELL( + listOf("§7Maxwell Power"), + listOf(), + listOf(IslandType.THE_RIFT), + 1, + 20 + ), + WEBSITE( + listOf("§ewww.hypixel.net"), + listOf(), + listOf(), + 0, + 21 + ); +} + +class CustomScoreboard { + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) { + if (!config.enabled) return + if (!LorenzUtils.inSkyBlock) return + config.position.renderStringsAndItems(display, posLabel = "Custom Scoreboard") + } + + @SubscribeEvent + fun onTick(event: TickEvent) { + // Draws the custom scoreboard + display = drawScoreboard() + + // Resets Party count + partyCount = 0 + + // Gets some values for the scoreboard + for (line in TabListData.getTabList()){ + if (line.startsWith(" Gems: §r§a")){ + gems = line.removePrefix(" Gems: §r§a") + } + if (line.startsWith(" Bank: §r§6")){ + bank = line.removePrefix(" Bank: §r§6") + } + if (line.startsWith(" §r§fMithril Powder: §r§2")){ + mithrilPowder = line.removePrefix(" §r§fMithril Powder: §r§2") + } + if (line.startsWith(" §r§fGemstone Powder: §r§d")){ + gemstonePowder = line.removePrefix(" §r§fGemstone Powder: §r§d") + } + } + + for (line in ScoreboardData.sidebarLinesFormatted){ + if (line.startsWith(" §7⏣ ") || line.startsWith(" §5ф ")){ + location = line + } + if (line.startsWith("Motes: §d")){ + motes = line.removePrefix("Motes: §d") + } + if (extractLobbyCode(line) is String ){ + lobbyCode = extractLobbyCode(line)!!.substring(1) //removes first char (number of color code) + } + if (line.startsWith("Heat: ")){ + heat = line.removePrefix("Heat: ") + } + if (line.startsWith("Bits: §b")){ + bits = line.removePrefix("Bits: §b") + } + if (line.startsWith("Copper: §c")){ + copper = line.removePrefix("Copper: §c") + } + } + purse = LorenzUtils.formatInteger(PurseAPI.currentPurse.toInt()) + } + + private fun drawScoreboard() = buildList> { + val lineMap = HashMap>() + for (element in CustomScoreboardElements.entries) { + if (element.data == "0" && config.hideEmptyLines){ // Hide empty lines + lineMap[element.index] = listOf("") + continue + } + + lineMap[element.index] = formatLine(element) + } + + return formatDisplay(lineMap) + } + + private fun formatLine(element: CustomScoreboardElements) : List{ + if (element.alternativeLine.isEmpty()) return element.displayLine + return if (config.displayNumbersFirst) element.alternativeLine else element.displayLine + } + + private fun formatDisplay(lineMap: HashMap>): MutableList> { + val newList = mutableListOf>() + for (index in config.textFormat) { + lineMap[index]?.let { + + // Multiline support + if (it[0] == "§9Party" + || it[0].toString().contains(MayorElection.currentCandidate?.name ?: "") + || it[0].toString().contains("Powder") + ) { + for (item in it) { + newList.add(listOf(item)) + } + continue + } + + // Adds empty lines + if(it[0] == ""){ + newList.add(listOf("")) + continue + } + + // Does not display this line + if(it[0] == ""){ + continue + } + + newList.add(it) + } + } + + return newList + } + + private fun isEnabled() : Boolean{ + return config.enabled && LorenzUtils.inSkyBlock + } +} + +private fun translateMayorNameToColor(input: String) : String { + return when (input) { + "Aatrox" -> "§3$input" + "Cole" -> "§e$input" + "Diana" -> "§2$input" + "Diaz" -> "§6$input" + "Finnegan" -> "§c$input" + "Foxy" -> "§d$input" + "Marina" -> "§b$input" + "Paul" -> "§c$input" + else -> "§7$input" + } +} + +private fun extractLobbyCode(input: String): String? { + val regex = Regex("§(\\d{3}/\\d{2}/\\d{2}) §([A-Za-z0-9]+)$") + val matchResult = regex.find(input) + return matchResult?.groupValues?.lastOrNull() +} + +private fun getProfileTypeAsSymbol(): String { + return when { + HypixelData.ironman -> "§7♲ " // Ironman + HypixelData.stranded -> "§a☀ " // Stranded + HypixelData.bingo -> "§cⒷ " // Bingo - TODO: Consider using colors from BingoAPI + else -> "§e" // Default case + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/CopyActionBar.kt b/src/main/java/at/hannibal2/skyhanni/test/command/CopyActionBar.kt new file mode 100644 index 000000000000..8e94684af699 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/test/command/CopyActionBar.kt @@ -0,0 +1,18 @@ +package at.hannibal2.skyhanni.test.command + +import at.hannibal2.skyhanni.data.ActionBarStatsData +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.transformIf +import at.hannibal2.skyhanni.utils.OSUtils +import at.hannibal2.skyhanni.utils.StringUtils.removeColor + +object CopyActionBar { + fun command(args: Array) { + val noColor = args.size == 1 && args[0] == "true" + var string = "" + string = ActionBarStatsData.actionBar.transformIf({noColor}) { removeColor() } + + OSUtils.copyToClipboard(string) + LorenzUtils.chat("§e[SkyHanni] actionbar copied into your clipboard!") + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt index 33a9bbe83aa7..92859717cdb8 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/TimeUtils.kt @@ -119,7 +119,7 @@ object TimeUtils { }.toLong() } - fun SkyBlockTime.formatted(): String { + fun SkyBlockTime.formatted(hoursAndMinutes : Boolean = true): String { val hour = if (this.hour > 12) this.hour - 12 else this.hour val timeOfDay = if (this.hour > 11) "pm" else "am" // hooray for 12-hour clocks var minute = this.minute.toString() @@ -131,6 +131,9 @@ object TimeUtils { val day = this.day val daySuffix = SkyBlockTime.daySuffix(day) val year = this.year + + if (!hoursAndMinutes) return "$month $day$daySuffix, Year $year" // Early Winter 1st Year 300 + return "$month $day$daySuffix, Year $year $hour:${minute}$timeOfDay" // Early Winter 1st Year 300, 12:03pm } }