aboutsummaryrefslogtreecommitdiff
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/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt
@@ -8,7 +8,7 @@ import at.hannibal2.skyhanni.utils.StringUtils.cleanPlayerName
import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.StringUtils.removeResets
-import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpaceAndResets
+import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpace
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.random.Random
@@ -94,7 +94,7 @@ object PartyAPI {
@SubscribeEvent
fun onChat(event: LorenzChatEvent) {
- val message = event.message.trimWhiteSpaceAndResets().removeResets()
+ val message = event.message.trimWhiteSpace().removeResets()
// new member joined
youJoinedPartyPattern.matchMatcher(message) {
diff --git a/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt
index 90ea15627..f4266f800 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt
@@ -4,8 +4,8 @@ import at.hannibal2.skyhanni.events.InventoryCloseEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.PurseChangeCause
import at.hannibal2.skyhanni.events.PurseChangeEvent
-import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber
-import at.hannibal2.skyhanni.utils.NumberUtil.milion
+import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble
+import at.hannibal2.skyhanni.utils.NumberUtil.million
import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraft.client.Minecraft
@@ -13,7 +13,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
object PurseAPI {
private val patternGroup = RepoPattern.group("data.purse")
- private val coinsPattern by patternGroup.pattern(
+ val coinsPattern by patternGroup.pattern(
"coins",
"(§.)*(Piggy|Purse): §6(?<coins>[\\d,.]+)( ?(§.)*\\([+-](?<earned>[\\d,.]+)\\)?|.*)?$"
)
@@ -22,8 +22,9 @@ object PurseAPI {
"Piggy: (?<coins>.*)"
)
- private var currentPurse = 0.0
private var inventoryCloseTime = 0L
+ var currentPurse = 0.0
+ private set
@SubscribeEvent
fun onInventoryClose(event: InventoryCloseEvent) {
@@ -32,10 +33,9 @@ object PurseAPI {
@SubscribeEvent
fun onTick(event: LorenzTickEvent) {
-
for (line in ScoreboardData.sidebarLinesFormatted) {
val newPurse = coinsPattern.matchMatcher(line) {
- group("coins").formatNumber().toDouble()
+ group("coins").formatDouble()
} ?: continue
val diff = newPurse - currentPurse
if (diff == 0.0) continue
@@ -52,7 +52,7 @@ object PurseAPI {
return PurseChangeCause.GAIN_TALISMAN_OF_COINS
}
- if (diff == 15.milion || diff == 100.milion) {
+ if (diff == 15.million || diff == 100.million) {
return PurseChangeCause.GAIN_DICE_ROLL
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt
new file mode 100644
index 000000000..603b9963b
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt
@@ -0,0 +1,256 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.jsonobjects.repo.ArrowTypeJson
+import at.hannibal2.skyhanni.data.jsonobjects.repo.ItemsJson
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.PlaySoundEvent
+import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemCategory
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull
+import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.round
+import at.hannibal2.skyhanni.utils.NEUInternalName
+import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName
+import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber
+import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getEnchantments
+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.minecraft.client.Minecraft
+import net.minecraft.item.ItemBow
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+private var infinityQuiverLevelMultiplier = 0.03f
+
+object QuiverAPI {
+ private val storage get() = ProfileStorageData.profileSpecific
+ var currentArrow: ArrowType?
+ get() = storage?.arrows?.currentArrow?.asInternalName()?.let { getArrowByNameOrNull(it) } ?: NONE_ARROW_TYPE
+ set(value) {
+ storage?.arrows?.currentArrow = value?.toString() ?: return
+ }
+ var arrowAmount: MutableMap<NEUInternalName, Float>
+ get() = storage?.arrows?.arrowAmount ?: mutableMapOf()
+ set(value) {
+ storage?.arrows?.arrowAmount = value
+ }
+ var currentAmount: Int
+ get() = arrowAmount[currentArrow?.internalName]?.toInt() ?: 0
+ set(value) {
+ arrowAmount[currentArrow?.internalName ?: return] = value.toFloat()
+ }
+
+ private var arrows: List<ArrowType> = listOf()
+
+ const val MAX_ARROW_AMOUNT = 2880
+ private val SKELETON_MASTER_CHESTPLATE = "SKELETON_MASTER_CHESTPLATE".asInternalName()
+
+ var NONE_ARROW_TYPE: ArrowType? = null
+ private var FLINT_ARROW_TYPE: ArrowType? = null
+
+ private val group = RepoPattern.group("data.quiver")
+ private val chatGroup = group.group("chat")
+ private val selectPattern by chatGroup.pattern("select", "§aYou set your selected arrow type to §f(?<arrow>.*)§a!")
+ private val fillUpJaxPattern by chatGroup.pattern(
+ "fillupjax",
+ "(§.)*Jax forged (§.)*(?<type>.*?)(§.)* x(?<amount>[\\d,]+)( (§.)*for (§.)*(?<coins>[\\d,]+) Coins)?(§.)*!"
+ )
+ private val fillUpPattern by chatGroup.pattern(
+ "fillup",
+ "§aYou filled your quiver with §f(?<flintAmount>.*) §aextra arrows!"
+ )
+ private val clearedPattern by chatGroup.pattern("cleared", "§aCleared your quiver!")
+ private val arrowResetPattern by chatGroup.pattern("arrowreset", "§cYour favorite arrow has been reset!")
+ private val addedToQuiverPattern by chatGroup.pattern(
+ "addedtoquiver",
+ "(§.)*You've added (§.)*(?<type>.*) x(?<amount>.*) (§.)*to your quiver!"
+ )
+
+ // Bows that don't use the players arrows, checked using the SkyBlock Id
+ private val fakeBowsPattern by group.pattern("fakebows", "^(BOSS_SPIRIT_BOW|CRYPT_BOW)$")
+ private val quiverInventoryNamePattern by group.pattern("quivername", "^Quiver$")
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+ val message = event.message.trimWhiteSpace().removeResets()
+
+ selectPattern.matchMatcher(message) {
+ val type = group("arrow")
+ currentArrow = getArrowByNameOrNull(type)
+ ?: return ErrorManager.logErrorWithData(
+ UnknownArrowType("Unknown arrow type: $type"),
+ "Unknown arrow type: $type",
+ "message" to message,
+ )
+ return
+ }
+
+ fillUpJaxPattern.matchMatcher(message) {
+ val type = group("type")
+ val amount = group("amount").formatNumber().toFloat()
+ val filledUpType = getArrowByNameOrNull(type)
+ ?: return ErrorManager.logErrorWithData(
+ UnknownArrowType("Unknown arrow type: $type"),
+ "Unknown arrow type: $type",
+ "message" to message,
+ )
+
+ arrowAmount.addOrPut(filledUpType.internalName, amount)
+ return
+ }
+
+ fillUpPattern.matchMatcher(message) {
+ val flintAmount = group("flintAmount").formatNumber().toFloat()
+
+ FLINT_ARROW_TYPE?.let { arrowAmount.addOrPut(it.internalName, flintAmount) }
+ return
+ }
+
+ addedToQuiverPattern.matchMatcher(message) {
+ val type = group("type")
+ val amount = group("amount").formatNumber().toFloat()
+
+ val filledUpType = getArrowByNameOrNull(type)
+ ?: return ErrorManager.logErrorWithData(
+ UnknownArrowType("Unknown arrow type: $type"),
+ "Unknown arrow type: $type",
+ "message" to message,
+ )
+
+ arrowAmount.addOrPut(filledUpType.internalName, amount)
+ return
+ }
+
+ clearedPattern.matchMatcher(message) {
+ currentAmount = 0
+ arrowAmount.clear()
+
+ return
+ }
+
+ arrowResetPattern.matchMatcher(message) {
+ currentArrow = NONE_ARROW_TYPE
+ currentAmount = 0
+
+ return
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryFullyLoaded(event: InventoryFullyOpenedEvent) {
+ if (!isEnabled()) return
+ if (!quiverInventoryNamePattern.matches(event.inventoryName)) return
+
+ // clear to prevent duplicates
+ currentAmount = 0
+ arrowAmount.clear()
+
+ val stacks = event.inventoryItems
+ for (stack in stacks.values) {
+ if (stack.getItemCategoryOrNull() != ItemCategory.ARROW) continue
+
+ val arrow = stack.getInternalNameOrNull() ?: continue
+
+ val arrowType = getArrowByNameOrNull(arrow) ?: continue
+
+ arrowAmount.addOrPut(arrowType.internalName, stack.stackSize.toFloat())
+ }
+ }
+
+ /*
+ Modified method to remove arrows from SkyblockFeatures QuiverOverlay
+ Original method source:
+ https://github.com/MrFast-js/SkyblockFeatures/blob/ae4bf0b91ed0fb17114d9cdaccaa9aef9a6c8d01/src/main/java/mrfast/sbf/features/overlays/QuiverOverlay.java#L127
+
+ Changes made:
+ - Added "fake bows" check
+ - Added "infinite quiver" check
+ - Added "sneaking" check
+ - Added "bow sound distance" check
+ - Added "skeleton master chestplate" check
+ */
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ fun onPlaySound(event: PlaySoundEvent) {
+ if (!isEnabled()) return
+ if (event.soundName != "random.bow") return
+
+ val holdingBow = InventoryUtils.getItemInHand()?.item is ItemBow
+ && !fakeBowsPattern.matches(InventoryUtils.getItemInHand()?.getInternalNameOrNull()?.asString() ?: "")
+
+ if (!holdingBow) return
+
+ // check if sound location is more than configAmount block away from player
+ val soundLocation = event.distanceToPlayer
+ if (soundLocation > SkyHanniMod.feature.dev.bowSoundDistance) return
+
+ val arrowType = currentArrow?.internalName ?: return
+ val amount = arrowAmount[arrowType] ?: return
+ if (amount <= 0) return
+
+ if (InventoryUtils.getChestplate()
+ // The chestplate has the ability to not use arrows
+ // https://hypixel-skyblock.fandom.com/wiki/Skeleton_Master_Armor
+ ?.getInternalNameOrNull() == SKELETON_MASTER_CHESTPLATE
+ ) return
+
+ val infiniteQuiverLevel = InventoryUtils.getItemInHand()?.getEnchantments()?.get("infinite_quiver") ?: 0
+
+ val amountToRemove = {
+ when (Minecraft.getMinecraft().thePlayer.isSneaking) {
+ true -> 1.0f
+ false -> {
+ when (infiniteQuiverLevel) {
+ in 1..10 -> 1 - (infinityQuiverLevelMultiplier * infiniteQuiverLevel)
+ else -> 1.0f
+ }
+ }
+ }
+ }
+
+ arrowAmount[arrowType] = amount - amountToRemove()
+ }
+
+ fun Int.asArrowPercentage() = ((this.toFloat() / MAX_ARROW_AMOUNT) * 100).round(1)
+
+ fun hasBowInInventory(): Boolean {
+ return InventoryUtils.getItemsInOwnInventory().any { it.item is ItemBow }
+ }
+
+ fun getArrowByNameOrNull(name: String): ArrowType? {
+ return arrows.firstOrNull { it.arrow == name }
+ }
+
+ fun getArrowByNameOrNull(internalName: NEUInternalName): ArrowType? {
+ return arrows.firstOrNull { it.internalName == internalName }
+ }
+
+ private fun NEUInternalName.asArrowTypeOrNull() = getArrowByNameOrNull(this)
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && storage != null
+
+
+ // Load arrows from repo
+ @SubscribeEvent
+ fun onRepoReload(event: RepositoryReloadEvent) {
+ val itemData = event.getConstant<ItemsJson>("Items")
+ infinityQuiverLevelMultiplier = itemData.enchant_multiplier["infinite_quiver"] ?: 0.03f
+
+ val arrowData = event.getConstant<ArrowTypeJson>("ArrowTypes")
+ arrows = arrowData.arrows.map { ArrowType(it.value.arrow, it.key.asInternalName()) }
+
+ NONE_ARROW_TYPE = getArrowByNameOrNull("NONE".asInternalName())
+ FLINT_ARROW_TYPE = getArrowByNameOrNull("FLINT".asInternalName())
+ }
+
+ class UnknownArrowType(message: String) : Exception(message)
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt b/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt
index 661330f92..b2cb66ef0 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt
@@ -35,13 +35,23 @@ class ScoreboardData {
fun formatLines(rawList: List<String>): List<String> {
val list = mutableListOf<String>()
for (line in rawList) {
- val seperator = splitIcons.find { line.contains(it) } ?: continue
- val split = line.split(seperator)
+ val separator = splitIcons.find { line.contains(it) } ?: continue
+ val split = line.split(separator)
val start = split[0]
var end = split[1]
- if (end.length >= 2) {
- end = end.substring(2)
+ // get last color code in start
+ val lastColorIndex = start.lastIndexOf('§')
+ val lastColor = when {
+ lastColorIndex != -1 && lastColorIndex + 1 < start.length && (start[lastColorIndex + 1] in '0'..'9' || start[lastColorIndex + 1] in 'a'..'f') -> start.substring(
+ lastColorIndex,
+ lastColorIndex + 2
+ )
+ else -> ""
}
+
+ // remove first color code from end, when it is the same as the last color code in start
+ end = end.removePrefix(lastColor)
+
list.add(start + end)
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
index 296e2db65..6ecf6935c 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
@@ -30,7 +30,7 @@ object SlayerAPI {
var latestSlayerCategory = ""
private var latestProgressChangeTime = 0L
var latestWrongAreaWarning = 0L
- private var latestSlayerProgress = ""
+ var latestSlayerProgress = ""
fun hasActiveSlayerQuest() = latestSlayerCategory != ""
diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ArrowTypeJson.java b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ArrowTypeJson.java
new file mode 100644
index 000000000..3388cb62c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ArrowTypeJson.java
@@ -0,0 +1,14 @@
+package at.hannibal2.skyhanni.data.jsonobjects.repo;
+
+import com.google.gson.annotations.Expose;
+import java.util.Map;
+
+public class ArrowTypeJson {
+ @Expose
+ public Map<String, ArrowAttributes> arrows;
+
+ public static class ArrowAttributes {
+ @Expose
+ public String arrow;
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/FameRankJson.java b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/FameRankJson.java
new file mode 100644
index 000000000..e060d55d1
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/FameRankJson.java
@@ -0,0 +1,24 @@
+package at.hannibal2.skyhanni.data.jsonobjects.repo;
+
+import com.google.gson.annotations.Expose;
+
+import java.util.Map;
+
+public class FameRankJson {
+ @Expose
+ public Map<String, FameRank> fame_rank;
+
+ public static class FameRank {
+ @Expose
+ public String name;
+
+ @Expose
+ public int fame_required;
+
+ @Expose
+ public double bits_multiplier;
+
+ @Expose
+ public int votes;
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ItemsJson.java b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ItemsJson.java
index ddd814159..0efeeb9fd 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ItemsJson.java
+++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/ItemsJson.java
@@ -14,6 +14,9 @@ public class ItemsJson {
public Map<String, Integer> crimson_tiers;
@Expose
+ public Map<String, Float> enchant_multiplier;
+
+ @Expose
public List<NEUInternalName> lava_fishing_rods;
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/MaxwellPowersJson.java b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/MaxwellPowersJson.java
new file mode 100644
index 000000000..6ac70abb6
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/MaxwellPowersJson.java
@@ -0,0 +1,10 @@
+package at.hannibal2.skyhanni.data.jsonobjects.repo;
+
+import com.google.gson.annotations.Expose;
+
+import java.util.List;
+
+public class MaxwellPowersJson {
+ @Expose
+ public List<String> powers;
+}