summaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/data
diff options
context:
space:
mode:
authorJ10a1n15 <45315647+j10a1n15@users.noreply.github.com>2024-03-12 19:59:55 +0100
committerGitHub <noreply@github.com>2024-03-12 19:59:55 +0100
commit4352ffb08d4bfffc06adad2a068f375ab9874333 (patch)
treeb8ce745fca8ab82ffa04f6ab87334139a6b7192b /src/main/java/at/hannibal2/skyhanni/data
parenteb863d60e82b5541a9f42d2608f61cb97ada209b (diff)
downloadskyhanni-4352ffb08d4bfffc06adad2a068f375ab9874333.tar.gz
skyhanni-4352ffb08d4bfffc06adad2a068f375ab9874333.tar.bz2
skyhanni-4352ffb08d4bfffc06adad2a068f375ab9874333.zip
Feature: CustomScoreboard (#893)
Co-authored-by: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Co-authored-by: Thunderblade73 <gaidermarkus@gmail.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/data')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/ArrowType.kt9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt200
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/FameRanks.kt26
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt15
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/MaxwellAPI.kt152
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt121
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt79
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/Mayors.kt105
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt256
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt18
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ArrowTypeJson.java14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/FameRankJson.java24
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ItemsJson.java3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/MaxwellPowersJson.java10
18 files changed, 962 insertions, 93 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/data/ArrowType.kt b/src/main/java/at/hannibal2/skyhanni/data/ArrowType.kt
new file mode 100644
index 000000000..5ce7dd33f
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/ArrowType.kt
@@ -0,0 +1,9 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.utils.NEUInternalName
+
+data class ArrowType(val arrow: String, val internalName: NEUInternalName) {
+ override fun toString(): String {
+ return internalName.asString()
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt
new file mode 100644
index 000000000..2802399bf
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt
@@ -0,0 +1,200 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.FameRanks.getFameRankByNameOrNull
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.ScoreboardChangeEvent
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.formatInt
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.StringUtils.matches
+import at.hannibal2.skyhanni.utils.StringUtils.removeResets
+import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpace
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object BitsAPI {
+ private val profileStorage get() = ProfileStorageData.profileSpecific?.bits
+ private val playerStorage get() = SkyHanniMod.feature.storage
+
+ var bits: Int
+ get() = profileStorage?.bits ?: 0
+ private set(value) {
+ profileStorage?.bits = value
+ }
+ var currentFameRank: FameRank?
+ get() = playerStorage?.currentFameRank?.let { getFameRankByNameOrNull(it) }
+ private set(value) {
+ if (value != null) {
+ playerStorage?.currentFameRank = value.name
+ }
+ }
+ var bitsToClaim: Int
+ get() = profileStorage?.bitsToClaim ?: 0
+ private set(value) {
+ profileStorage?.bitsToClaim = value
+ }
+
+ private const val defaultcookiebits = 4800
+
+ private val bitsDataGroup = RepoPattern.group("data.bits")
+
+ // Scoreboard patterns
+ val bitsScoreboardPattern by bitsDataGroup.pattern(
+ "scoreboard",
+ "^Bits: §b(?<amount>[\\d,.]+).*$"
+ )
+
+ // Chat patterns
+ private val bitsChatGroup = bitsDataGroup.group("chat")
+
+ private val bitsFromFameRankUpChatPattern by bitsChatGroup.pattern(
+ "famerankup",
+ "§eYou gained §3(?<amount>.*) Bits Available §ecompounded from all your §epreviously eaten §6cookies§e! Click here to open §6cookie menu§e!"
+ )
+
+ private val boosterCookieAte by bitsChatGroup.pattern(
+ "boostercookieate",
+ "§eYou consumed a §6Booster Cookie§e!.*"
+ )
+
+ // GUI patterns
+ private val bitsGuiGroup = bitsDataGroup.group("gui")
+
+ private val bitsAvailableMenuPattern by bitsGuiGroup.pattern(
+ "availablemenu",
+ "§7Bits Available: §b(?<toClaim>[\\d,]+)(§3.+)?"
+ )
+
+ private val fameRankSbMenuPattern by bitsGuiGroup.pattern(
+ "sbmenufamerank",
+ "§7Your rank: §e(?<rank>.*)"
+ )
+
+ private val fameRankCommunityShopPattern by bitsGuiGroup.pattern(
+ "communityshopfamerank",
+ "§7Fame Rank: §e(?<rank>.*)"
+ )
+
+ private val bitsGuiNamePattern by bitsGuiGroup.pattern(
+ "mainmenuname",
+ "^SkyBlock Menu$"
+ )
+
+ private val bitsGuiStackPattern by bitsGuiGroup.pattern(
+ "mainmenustack",
+ "^§6Booster Cookie$"
+ )
+
+ private val fameRankGuiNamePattern by bitsGuiGroup.pattern(
+ "famerankmenuname",
+ "^(Community Shop|Booster Cookie)$"
+ )
+
+ private val fameRankGuiStackPattern by bitsGuiGroup.pattern(
+ "famerankmenustack",
+ "^(§aCommunity Shop|§eFame Rank)$"
+ )
+
+ @SubscribeEvent
+ fun onScoreboardChange(event: ScoreboardChangeEvent) {
+ if (!isEnabled()) return
+ for (line in event.newList) {
+ val message = line.trimWhiteSpace().removeResets()
+
+ bitsScoreboardPattern.matchMatcher(message) {
+ val amount = group("amount").formatInt()
+
+ if (amount > bits) {
+ bitsToClaim -= amount - bits
+ ChatUtils.debug("You have gained §3${amount - bits} Bits §7according to the scoreboard!")
+ }
+ bits = amount
+
+ return
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+ val message = event.message.trimWhiteSpace().removeResets()
+
+ bitsFromFameRankUpChatPattern.matchMatcher(message) {
+ val amount = group("amount").formatInt()
+ bitsToClaim += amount
+
+ return
+ }
+
+ boosterCookieAte.matchMatcher(message) {
+ bitsToClaim += (defaultcookiebits * (currentFameRank?.bitsMultiplier ?: return)).toInt()
+
+ return
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryFullyLoaded(event: InventoryFullyOpenedEvent) {
+ if (!isEnabled()) return
+
+ val stacks = event.inventoryItems
+
+ if (bitsGuiNamePattern.matches(event.inventoryName)) {
+ val cookieStack = stacks.values.lastOrNull { bitsGuiStackPattern.matches(it.displayName) } ?: return
+ for (line in cookieStack.getLore()) {
+ bitsAvailableMenuPattern.matchMatcher(line) {
+ bitsToClaim = group("toClaim").formatInt()
+
+ return
+ }
+ }
+ return
+ }
+
+ if (fameRankGuiNamePattern.matches(event.inventoryName)) {
+ val fameRankStack = stacks.values.lastOrNull { fameRankGuiStackPattern.matches(it.displayName) } ?: return
+
+ line@ for (line in fameRankStack.getLore()) {
+ fameRankCommunityShopPattern.matchMatcher(line) {
+ val rank = group("rank")
+
+ currentFameRank = getFameRankByNameOrNull(rank)
+ ?: return ErrorManager.logErrorWithData(
+ FameRankNotFoundException(rank),
+ "FameRank $rank not found",
+ "Rank" to rank,
+ "Lore" to fameRankStack.getLore(),
+ "FameRanks" to FameRanks.fameRanks
+ )
+
+ continue@line
+ }
+
+ fameRankSbMenuPattern.matchMatcher(line) {
+ val rank = group("rank")
+
+ currentFameRank = getFameRankByNameOrNull(rank)
+ ?: return ErrorManager.logErrorWithData(
+ FameRankNotFoundException(rank),
+ "FameRank $rank not found",
+ "Rank" to rank,
+ "Lore" to fameRankStack.getLore(),
+ "FameRanks" to FameRanks.fameRanks
+ )
+
+ continue@line
+ }
+ }
+ }
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && profileStorage != null
+
+ class FameRankNotFoundException(rank: String) : Exception("FameRank not found: $rank")
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/FameRanks.kt b/src/main/java/at/hannibal2/skyhanni/data/FameRanks.kt
new file mode 100644
index 000000000..663e2b970
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/FameRanks.kt
@@ -0,0 +1,26 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.data.jsonobjects.repo.FameRankJson
+import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object FameRanks {
+ var fameRanks = emptyMap<String, FameRank>()
+ private set
+
+ fun getFameRankByNameOrNull(name: String) = fameRanks[name]
+
+ @SubscribeEvent
+ fun onRepoReload(event: RepositoryReloadEvent) {
+ val ranks = event.getConstant<FameRankJson>("FameRank")
+ fameRanks = ranks.fame_rank.values.map { FameRank(it.name, it.fame_required, it.bits_multiplier, it.votes) }
+ .associateBy { it.name }
+ }
+}
+
+data class FameRank(
+ val name: String,
+ val fameRequired: Int,
+ val bitsMultiplier: Double,
+ val electionVotes: Int
+)
diff --git a/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt b/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt
index 4fbebe958..ee9a6a23a 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt
@@ -3,8 +3,10 @@ package at.hannibal2.skyhanni.data
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.core.config.Position
import at.hannibal2.skyhanni.config.core.config.gui.GuiPositionEditor
+import at.hannibal2.skyhanni.events.GuiPositionMovedEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzKeyPressEvent
+import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.test.SkyHanniDebugsAndTests
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.isRancherSign
@@ -51,11 +53,20 @@ class GuiEditManager {
currentPositions.clear()
}
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ lastMovedGui?.let {
+ GuiPositionMovedEvent(it).postAndCatch()
+ lastMovedGui = null
+ }
+ }
+
companion object {
var currentPositions = mutableMapOf<String, Position>()
private var latestPositions = mapOf<String, Position>()
private var currentBorderSize = mutableMapOf<String, Pair<Int, Int>>()
+ private var lastMovedGui: String? = null
@JvmStatic
fun add(position: Position, posLabel: String, x: Int, y: Int) {
@@ -116,6 +127,10 @@ class GuiEditManager {
fun GuiProfileViewer.anyTextBoxFocused() =
this.getPropertiesWithType<GuiElementTextField>().any { it.focus }
+
+ fun handleGuiPositionMoved(guiName: String) {
+ lastMovedGui = guiName
+ }
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
index b206eb565..c347fcdba 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
@@ -300,9 +300,12 @@ class HypixelData {
private fun checkIsland() {
var newIsland = ""
var guesting = false
+ TabListData.fullyLoaded = false
+
for (line in TabListData.getTabList()) {
islandNamePattern.matchMatcher(line) {
newIsland = group("island").removeColor()
+ TabListData.fullyLoaded = true
}
if (line == " Status: §r§9Guest") {
guesting = true
diff --git a/src/main/java/at/hannibal2/skyhanni/data/MaxwellAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MaxwellAPI.kt
new file mode 100644
index 000000000..0f19854f7
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/MaxwellAPI.kt
@@ -0,0 +1,152 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.data.jsonobjects.repo.MaxwellPowersJson
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
+import at.hannibal2.skyhanni.utils.NumberUtil.formatInt
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.StringUtils.matches
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import at.hannibal2.skyhanni.utils.StringUtils.removeResets
+import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpace
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraft.enchantment.Enchantment
+import net.minecraft.enchantment.Enchantment.power
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+object MaxwellAPI {
+
+ private val storage get() = ProfileStorageData.profileSpecific
+
+ var currentPower: String?
+ get() = storage?.maxwell?.currentPower
+ set(value) {
+ storage?.maxwell?.currentPower = value ?: return
+ }
+ var magicalPower: Int?
+ get() = storage?.maxwell?.magicalPower
+ set(value) {
+ storage?.maxwell?.magicalPower = value ?: return
+ }
+
+ private var powers = mutableListOf<String>()
+
+ private val group = RepoPattern.group("data.maxwell")
+ private val chatPowerpattern by group.pattern(
+ "chat.power",
+ "§eYou selected the §a(?<power>.*) §e(power )?for your §aAccessory Bag§e!"
+ )
+ private val inventoryPowerPattern by group.pattern(
+ "inventory.power",
+ "§7Selected Power: §a(?<power>.*)"
+ )
+ private val inventoryMPPattern by group.pattern(
+ "inventory.magicalpower",
+ "§7Magical Power: §6(?<mp>[\\d,]+)"
+ )
+ private val thaumaturgyGuiPattern by group.pattern(
+ "gui.thaumaturgy",
+ "Accessory Bag Thaumaturgy"
+ )
+ private val yourBagsGuiPattern by group.pattern(
+ "gui.yourbags",
+ "Your Bags"
+ )
+ private val powerSelectedPattern by group.pattern(
+ "gui.selectedpower",
+ "§aPower is selected!"
+ )
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+ val message = event.message.trimWhiteSpace().removeResets()
+
+ chatPowerpattern.matchMatcher(message) {
+ val power = group("power")
+ currentPower = getPowerByNameOrNull(power)
+ ?: return ErrorManager.logErrorWithData(
+ UnknownMaxwellPower("Unknown power: $power"),
+ "Unknown power: $power",
+ "power" to power,
+ "message" to message
+ )
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryFullyLoaded(event: InventoryFullyOpenedEvent) {
+ if (!isEnabled()) return
+
+ if (thaumaturgyGuiPattern.matches(event.inventoryName)) {
+ val selectedPowerStack =
+ event.inventoryItems.values.find {
+ powerSelectedPattern.matches(it.getLore().lastOrNull())
+ } ?: return
+ val displayName = selectedPowerStack.displayName.removeColor()
+
+ currentPower = getPowerByNameOrNull(displayName)
+ ?: return ErrorManager.logErrorWithData(
+ UnknownMaxwellPower("Unknown power: $power"),
+ "Unknown power: $power",
+ "power" to power,
+ "displayName" to displayName,
+ "lore" to selectedPowerStack.getLore()
+ )
+ return
+ }
+
+ if (yourBagsGuiPattern.matches(event.inventoryName)) {
+ val stacks = event.inventoryItems
+
+ for (stack in stacks.values) {
+ processStack(stack)
+ }
+ }
+ }
+
+ private fun processStack(stack: ItemStack) {
+ for (line in stack.getLore()) {
+ inventoryMPPattern.matchMatcher(line) {
+ // MagicalPower is boosted in catacombs
+ if (IslandType.CATACOMBS.isInIsland()) return@matchMatcher
+
+ val mp = group("mp")
+ magicalPower = mp.formatInt()
+ return@matchMatcher
+ }
+
+ inventoryPowerPattern.matchMatcher(line) {
+ val power = group("power")
+ currentPower = getPowerByNameOrNull(power)
+ ?: return@matchMatcher ErrorManager.logErrorWithData(
+ UnknownMaxwellPower("Unknown power: ${Enchantment.power}"),
+ "Unknown power: ${Enchantment.power}",
+ "power" to Enchantment.power,
+ "displayName" to stack.displayName,
+ "lore" to stack.getLore()
+ )
+ return@matchMatcher
+ }
+ }
+ }
+
+ private fun getPowerByNameOrNull(name: String) = powers.find { it == name }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && storage != null
+
+ // Load powers from repo
+ @SubscribeEvent
+ fun onRepoLoad(event: RepositoryReloadEvent) {
+ val data = event.getConstant<MaxwellPowersJson>("MaxwellPowers")
+ powers = data.powers
+ }
+
+ class UnknownMaxwellPower(message: String) : Exception(message)
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt
new file mode 100644
index 000000000..bc937a5a2
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt
@@ -0,0 +1,121 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.ConfigManager
+import at.hannibal2.skyhanni.data.Mayor.Companion.setMayorWithActivePerks
+import at.hannibal2.skyhanni.data.jsonobjects.local.MayorJson
+import at.hannibal2.skyhanni.events.DebugDataCollectEvent
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.utils.APIUtil
+import at.hannibal2.skyhanni.utils.CollectionUtils.put
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.asTimeMark
+import io.github.moulberry.notenoughupdates.util.SkyBlockTime
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.minutes
+
+object MayorAPI {
+ var lastUpdate = SimpleTimeMark.farPast()
+ private var dispatcher = Dispatchers.IO
+
+ private var rawMayorData: MayorJson? = null
+ var candidates = mapOf<Int, MayorJson.Candidate>()
+ private set
+ var currentMayor: Mayor? = null
+ private set
+ var timeTillNextMayor = Duration.ZERO
+ private set
+
+ private const val ELECTION_END_MONTH = 3 //Late Spring
+ private const val ELECTION_END_DAY = 27
+
+ /**
+ * @param input: The name of the mayor
+ * @return: The neu color of the mayor; If no mayor was found, it will return "§cUnknown: §7"
+ */
+ fun mayorNameToColorCode(input: String): String {
+ return Mayor.getMayorFromName(input).color
+ }
+
+ /**
+ * @param input: The name of the mayor
+ * @return: The neu color of the mayor + the name of the mayor; If no mayor was found, it will return "§cUnknown: §7[input]"
+ */
+ fun mayorNameWithColorCode(input: String) = mayorNameToColorCode(input) + input
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!LorenzUtils.onHypixel) return
+
+ if (event.repeatSeconds(2)) {
+ checkHypixelAPI()
+ getTimeTillNextMayor()
+ }
+ }
+
+ private fun calculateNextMayorTime(): SimpleTimeMark {
+ var mayorYear = SkyBlockTime.now().year
+
+ // Check if either the month is already over or the day is after 27th in the third month
+ if (SkyBlockTime.now().month > ELECTION_END_MONTH || (SkyBlockTime.now().day >= ELECTION_END_DAY && SkyBlockTime.now().month == ELECTION_END_MONTH)) {
+ // If so, the next mayor will be in the next year
+ mayorYear++
+ }
+
+ return SkyBlockTime(mayorYear, ELECTION_END_MONTH, day = ELECTION_END_DAY).asTimeMark()
+ }
+
+ private fun getTimeTillNextMayor() {
+ val nextMayorTime = calculateNextMayorTime()
+ timeTillNextMayor = nextMayorTime - SimpleTimeMark.now()
+ }
+
+ private fun checkCurrentMayor() {
+ val nextMayorTime = calculateNextMayorTime()
+
+ // Check if it is still the mayor from the old SkyBlock year
+ currentMayor = candidates[nextMayorTime.toSkyBlockTime().year - 1]?.let {
+ // TODO: Once Jerry is active, add the sub mayor perks in here
+ setMayorWithActivePerks(it.name, it.perks)
+ }
+ }
+
+ private fun checkHypixelAPI() {
+ if (lastUpdate.passedSince() < 20.minutes) return
+ lastUpdate = SimpleTimeMark.now()
+
+ SkyHanniMod.coroutineScope.launch {
+ val url = "https://api.hypixel.net/v2/resources/skyblock/election"
+ val jsonObject = withContext(dispatcher) { APIUtil.getJSONResponse(url) }
+ rawMayorData = ConfigManager.gson.fromJson(jsonObject, MayorJson::class.java)
+ val data = rawMayorData ?: return@launch
+ val map = mutableMapOf<Int, MayorJson.Candidate>()
+ map put data.mayor.election.getPairs()
+ data.current?.let {
+ map put data.current.getPairs()
+ }
+ candidates = map
+ checkCurrentMayor()
+ }
+ }
+
+ private fun MayorJson.Election.getPairs() = year + 1 to candidates.bestCandidate()
+
+ private fun List<MayorJson.Candidate>.bestCandidate() = maxBy { it.votes }
+
+ @SubscribeEvent
+ fun onDebugDataCollect(event: DebugDataCollectEvent) {
+ event.title("Mayor")
+ event.addIrrelevant {
+ add("Current Mayor: ${currentMayor?.name ?: "Unknown"}")
+ add("Active Perks: ${currentMayor?.activePerks}")
+ add("Last Update: $lastUpdate (${lastUpdate.passedSince()} ago)")
+ add("Time Till Next Mayor: $timeTillNextMayor")
+ }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt b/src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt
deleted file mode 100644
index e73a6bf14..000000000
--- a/src/main/java/at/hannibal2/skyhanni/data/MayorElection.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-package at.hannibal2.skyhanni.data
-
-import at.hannibal2.skyhanni.SkyHanniMod
-import at.hannibal2.skyhanni.config.ConfigManager
-import at.hannibal2.skyhanni.data.jsonobjects.local.MayorJson
-import at.hannibal2.skyhanni.events.LorenzTickEvent
-import at.hannibal2.skyhanni.utils.APIUtil
-import at.hannibal2.skyhanni.utils.CollectionUtils.put
-import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.SimpleTimeMark
-import io.github.moulberry.notenoughupdates.util.SkyBlockTime
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
-import kotlin.time.Duration.Companion.minutes
-
-class MayorElection {
-
- private var lastUpdate = SimpleTimeMark.farPast()
- private var dispatcher = Dispatchers.IO
-
- companion object {
-
- var rawMayorData: MayorJson? = null
- var candidates = mapOf<Int, MayorJson.Candidate>()
- var currentCandidate: MayorJson.Candidate? = null
-
- fun isPerkActive(mayor: String, perk: String) = currentCandidate?.let { currentCandidate ->
- currentCandidate.name == mayor && currentCandidate.perks.any { it.name == perk }
- } ?: false
- }
-
- @SubscribeEvent
- fun onTick(event: LorenzTickEvent) {
- if (!LorenzUtils.onHypixel) return
-
- if (event.repeatSeconds(3)) {
- check()
- }
- }
-
- private fun check() {
- if (lastUpdate.passedSince() < 20.minutes) return
- lastUpdate = SimpleTimeMark.now()
-
- SkyHanniMod.coroutineScope.launch {
- val url = "https://api.hypixel.net/v2/resources/skyblock/election"
- val jsonObject = withContext(dispatcher) { APIUtil.getJSONResponse(url) }
- rawMayorData = ConfigManager.gson.fromJson(jsonObject, MayorJson::class.java)
- val data = rawMayorData ?: return@launch
- val map = mutableMapOf<Int, MayorJson.Candidate>()
- map put data.mayor.election.getPairs()
- data.current?.let {
- map put data.current.getPairs()
- }
- candidates = map
- checkCurrentMayor()
- }
- }
-
- private fun checkCurrentMayor() {
- var currentYear = SkyBlockTime.now().year
-
- // The time in the current SkyBlock year when the election circle will restart
- val month = 3 // Late Spring
- val nextMayorTime = SkyBlockTime(currentYear, month, day = 27).toMillis()
-
- // Is it still the mayor from old sb year?
- if (nextMayorTime > System.currentTimeMillis()) {
- currentYear--
- }
- currentCandidate = candidates[currentYear]
- }
-
- private fun MayorJson.Election.getPairs() = year + 1 to candidates.bestCandidate()
-
- private fun List<MayorJson.Candidate>.bestCandidate() = maxBy { it.votes }
-}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt b/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt
new file mode 100644
index 000000000..cd6205c7a
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt
@@ -0,0 +1,105 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.data.jsonobjects.local.MayorJson
+
+enum class Mayor(
+ val mayorName: String,
+ val color: String,
+ private vararg val perks: Perk,
+) {
+ AATROX("Aatrox", "§3", Perk.SLASHED_PRICING, Perk.SLAYER_XP_BUFF, Perk.PATHFINDER),
+ COLE("Cole", "§e", Perk.PROSPECTION, Perk.MINING_XP_BUFF, Perk.MINING_FIESTA),
+ DIANA("Diana", "§2", Perk.LUCKY, Perk.MYTHOLOGICAL_RITUAL, Perk.PET_XP_BUFF),
+ DIAZ("Diaz", "§6", Perk.BARRIER_STREET, Perk.SHOPPING_SPREE),
+ FINNEGAN("Finnegan", "§c", Perk.FARMING_SIMULATOR, Perk.PELT_POCALYPSE, Perk.GOATED),
+ FOXY("Foxy", "§d", Perk.SWEET_TOOTH, Perk.BENEVOLENCE, Perk.EXTRA_EVENT),
+ MARINA("Marina", "§b", Perk.FISHING_XP_BUFF, Perk.LUCK_OF_THE_SEA, Perk.FISHING_FESTIVAL),
+ PAUL("Paul", "§c", Perk.MARAUDER, Perk.EZPZ, Perk.BENEDICTION),
+
+ SCORPIUS("Scorpius", "§d", Perk.BRIBE, Perk.DARKER_AUCTIONS),
+ JERRY("Jerry", "§d", Perk.PERKPOCALYPSE, Perk.STATSPOCALYPSE, Perk.JERRYPOCALYPSE),
+ DERPY("Derpy", "§d", Perk.TURBO_MINIONS, Perk.AH_CLOSED, Perk.DOUBLE_MOBS_HP, Perk.MOAR_SKILLZ),
+
+ UNKNOWN("Unknown", "§c"),
+ ;
+
+ val activePerks: MutableList<Perk> = mutableListOf()
+
+ companion object {
+ fun getMayorFromName(name: String) = entries.firstOrNull { it.mayorName == name } ?: UNKNOWN
+
+ fun setMayorWithActivePerks(name: String, perks: ArrayList<MayorJson.Perk>): Mayor {
+ val mayor = getMayorFromName(name)
+
+ mayor.perks.forEach { it.isActive = false }
+ mayor.activePerks.clear()
+ perks.mapNotNull { perk -> Perk.entries.firstOrNull { it.perkName == perk.name } }
+ .filter { mayor.perks.contains(it) }.forEach {
+ it.isActive = true
+ mayor.activePerks.add(it)
+ }
+
+ return mayor
+ }
+ }
+}
+
+enum class Perk(val perkName: String) {
+ // Aatrox
+ SLASHED_PRICING("SLASHED Pricing"),
+ SLAYER_XP_BUFF("Slayer XP Buff"),
+ PATHFINDER("Pathfinder"),
+
+ // Cole
+ PROSPECTION("Prospection"),
+ MINING_XP_BUFF("Mining XP Buff"),
+ MINING_FIESTA("Mining Fiesta"),
+
+ // Diana
+ LUCKY("Lucky!"),
+ MYTHOLOGICAL_RITUAL("Mythological Ritual"),
+ PET_XP_BUFF("Pet XP Buff"),
+
+ // Diaz
+ BARRIER_STREET("Barrier Street"),
+ SHOPPING_SPREE("Shopping Spree"),
+
+ // Finnegan
+ FARMING_SIMULATOR("Farming Simulator"),
+ PELT_POCALYPSE("Pelt-pocalypse"),
+ GOATED("GOATed"),
+
+ // Foxy
+ SWEET_TOOTH("Sweet Tooth"),
+ BENEVOLENCE("Benevolence"),
+ EXTRA_EVENT("Extra Event"),
+
+ // Marina
+ FISHING_XP_BUFF("Fishing XP Buff"),
+ LUCK_OF_THE_SEA("Luck of the Sea 2.0"),
+ FISHING_FESTIVAL("Fishing Festival"),
+
+ // Paul
+ MARAUDER("Marauder"),
+ EZPZ("EZPZ"),
+ BENEDICTION("Benediction"),
+
+
+ // Scorpius
+ BRIBE("Bribe"),
+ DARKER_AUCTIONS("Darker Auctions"),
+
+ // Jerry
+ PERKPOCALYPSE("Perkpocalypse"),
+ STATSPOCALYPSE("Statspocalypse"),
+ JERRYPOCALYPSE("Jerrypocalypse"),
+
+ // Derpy
+ TURBO_MINIONS("TURBO MINIONS!!!"),
+ AH_CLOSED("AH CLOSED!!!"),
+ DOUBLE_MOBS_HP("DOUBLE MOBS HP!!!"),
+ MOAR_SKILLZ("MOAR SKILLZ!!!"),
+ ;
+
+ var isActive = false
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt
index 22fb8e4f8..ace56f67f 100644
--- a/