aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt256
1 files changed, 256 insertions, 0 deletions
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)
+}