From a6ecdbcb42572739d4290bbaca463956c66b20cb Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Sun, 15 Sep 2024 11:41:48 +0200 Subject: [PATCH 001/102] Improvement: Spade tracker visibility (#2496) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> --- .../event/diana/DianaProfitTracker.kt | 18 ++++++---- .../diana/MythologicalCreatureTracker.kt | 34 +++++++++++-------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt index b6ec4d0a62e9..fb90de9e118e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt @@ -8,9 +8,11 @@ import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.ItemAddEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.features.event.diana.DianaAPI.isDianaSpade import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString +import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators @@ -107,7 +109,7 @@ object DianaProfitTracker { @SubscribeEvent fun onItemAdd(event: ItemAddEvent) { - if (!isEnabled()) return + if (!(DianaAPI.isDoingDiana() && config.enabled)) return tryAddItem(event.internalName, event.amount, event.source == ItemAddManager.Source.COMMAND) } @@ -137,9 +139,7 @@ object DianaProfitTracker { tryHide(event) } - if (message == "§6§lRARE DROP! §r§eYou dug out a §r§9Griffin Feather§r§e!" || - message == "§eFollow the arrows to find the §r§6treasure§r§e!" - ) { + if (message == "§6§lRARE DROP! §r§eYou dug out a §r§9Griffin Feather§r§e!" || message == "§eFollow the arrows to find the §r§6treasure§r§e!") { BurrowAPI.lastBurrowRelatedChatMessage = SimpleTimeMark.now() tryHide(event) } @@ -153,7 +153,13 @@ object DianaProfitTracker { @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent) { - if (!isEnabled()) return + if (!LorenzUtils.inSkyBlock) return + if (!config.enabled) return + val spadeInHand = InventoryUtils.getItemInHand()?.isDianaSpade ?: false + if (!DianaAPI.isDoingDiana() && !spadeInHand) return + if (spadeInHand) { + tracker.firstUpdate() + } tracker.renderDisplay(config.position) } @@ -168,6 +174,4 @@ object DianaProfitTracker { fun resetCommand() { tracker.resetCommand() } - - private fun isEnabled() = DianaAPI.isDoingDiana() && config.enabled } diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt index 3c512d4c4389..55e422caba5f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt @@ -5,11 +5,13 @@ import at.hannibal2.skyhanni.data.MayorAPI.getElectionYear import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.features.event.diana.DianaAPI.isDianaSpade import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString import at.hannibal2.skyhanni.utils.CollectionUtils.sumAllValues import at.hannibal2.skyhanni.utils.ConditionalUtils +import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RegexUtils.matches @@ -90,19 +92,18 @@ object MythologicalCreatureTracker { @SubscribeEvent fun onChat(event: LorenzChatEvent) { for (creatureType in MythologicalCreatureType.entries) { - if (creatureType.pattern.matches(event.message)) { - BurrowAPI.lastBurrowRelatedChatMessage = SimpleTimeMark.now() - tracker.modify { - it.count.addOrPut(creatureType, 1) - - // TODO migrate to abstract feature in the future - if (creatureType == MythologicalCreatureType.MINOS_INQUISITOR) { - event.chatComponent = ChatComponentText(event.message + " §e(${it.creaturesSinceLastInquisitor})") - it.creaturesSinceLastInquisitor = 0 - } else it.creaturesSinceLastInquisitor++ - } - if (config.hideChat) event.blockedReason = "mythological_creature_dug" + if (!creatureType.pattern.matches(event.message)) continue + BurrowAPI.lastBurrowRelatedChatMessage = SimpleTimeMark.now() + tracker.modify { + it.count.addOrPut(creatureType, 1) + + // TODO migrate to abstract feature in the future + if (creatureType == MythologicalCreatureType.MINOS_INQUISITOR) { + event.chatComponent = ChatComponentText(event.message + " §e(${it.creaturesSinceLastInquisitor})") + it.creaturesSinceLastInquisitor = 0 + } else it.creaturesSinceLastInquisitor++ } + if (config.hideChat) event.blockedReason = "mythological_creature_dug" } } @@ -133,7 +134,13 @@ object MythologicalCreatureTracker { @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent) { - if (!isEnabled()) return + if (!LorenzUtils.inSkyBlock) return + if (!config.enabled) return + val spadeInHand = InventoryUtils.getItemInHand()?.isDianaSpade ?: false + if (!DianaAPI.isDoingDiana() && !spadeInHand) return + if (spadeInHand) { + tracker.firstUpdate() + } tracker.renderDisplay(config.position) } @@ -142,5 +149,4 @@ object MythologicalCreatureTracker { tracker.resetCommand() } - private fun isEnabled() = DianaAPI.isDoingDiana() && config.enabled } From 3e2cd8ee29ce571286d5cc2ccdc7ed74a9fffcbe Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Sun, 15 Sep 2024 11:56:44 +0200 Subject: [PATCH 002/102] Fix: Scoreboard Error during M7 Dragons (#2510) --- src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt b/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt index 378fe03c6953..a8cafcf3d981 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt @@ -51,16 +51,13 @@ object ScoreboardData { val lastColor = start.lastColorCode() ?: "" // Generate the list of color suffixes - val colorSuffixes = generateSequence(lastColor) { it.dropLast(2) } - .takeWhile { it.isNotEmpty() } - .toMutableList() + val colorSuffixes = lastColor.chunked(2).toMutableList() // Iterate through the colorSuffixes to remove matching prefixes from 'end' for (suffix in colorSuffixes.toList()) { if (end.startsWith(suffix)) { end = end.removePrefix(suffix) colorSuffixes.remove(suffix) - break } } From 67e09820b733caee179ea3c8808aa358cdcff94d Mon Sep 17 00:00:00 2001 From: MTOnline69 <97001154+MTOnline69@users.noreply.github.com> Date: Sun, 15 Sep 2024 11:01:03 +0100 Subject: [PATCH 003/102] Fix: Broodmother countdown wrong in singular case (#2513) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../features/combat/BroodmotherFeatures.kt | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/BroodmotherFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/BroodmotherFeatures.kt index e087fdd1934e..dd7c088b0b7e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/BroodmotherFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/BroodmotherFeatures.kt @@ -60,9 +60,7 @@ object BroodmotherFeatures { private fun onStageUpdate() { ChatUtils.debug("New Broodmother stage: $currentStage") - if (lastStage == null) { - if (onServerJoin()) return - } + if (lastStage == null && onServerJoin()) return // ignore Hypixel bug where the stage may temporarily revert to Imminent after the Broodmother's death if (currentStage == StageEntry.IMMINENT && lastStage == StageEntry.ALIVE) return @@ -72,23 +70,23 @@ object BroodmotherFeatures { return } - val timeUntilSpawn = currentStage?.minutes?.minutes - broodmotherSpawnTime = SimpleTimeMark.now() + timeUntilSpawn!! - if (currentStage == StageEntry.IMMINENT && config.imminentWarning) { playImminentWarning() return } - if (config.stages.contains(currentStage) && lastStage != null) { - if (currentStage == StageEntry.SLAIN) { - onBroodmotherSlain() - } else { - val pluralize = StringUtils.pluralize(timeUntilSpawn.toInt(DurationUnit.MINUTES), "minute") - ChatUtils.chat( - "Broodmother: $lastStage §e-> $currentStage§e. §b${timeUntilSpawn.inWholeMinutes} $pluralize §euntil it spawns!" - ) - } + val lastStage = lastStage ?: return + val timeUntilSpawn = currentStage?.minutes?.minutes ?: return + broodmotherSpawnTime = SimpleTimeMark.now() + timeUntilSpawn + + if (currentStage !in config.stages) return + if (currentStage == StageEntry.SLAIN) { + onBroodmotherSlain() + } else { + val pluralize = StringUtils.pluralize(timeUntilSpawn.toInt(DurationUnit.MINUTES), "minute") + ChatUtils.chat( + "Broodmother: $lastStage §e-> $currentStage§e. §b${timeUntilSpawn.inWholeMinutes} $pluralize §euntil it spawns!" + ) } } @@ -99,7 +97,7 @@ object BroodmotherFeatures { val pluralize = StringUtils.pluralize(currentStage?.minutes ?: 0, "minute") var message = "The Broodmother's current stage in this server is ${currentStage.toString().replace("!", "")}§e." if (currentStage?.minutes != 0) { - message += " It will spawn within §b${currentStage?.minutes} $pluralize§e." + message += " It will spawn §bwithin ${currentStage?.minutes} $pluralize§e." } ChatUtils.chat(message) return true @@ -161,7 +159,7 @@ object BroodmotherFeatures { if (!isCountdownEnabled()) return if (broodmotherSpawnTime.isFarPast()) { - if (lastStage != null) { + if (lastStage != null && currentStage == StageEntry.ALIVE) { display = "§4Broodmother spawned!" } } else { From bea837f2ba12f049614f92096b47a9e601c288b9 Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Sun, 15 Sep 2024 12:10:47 +0200 Subject: [PATCH 004/102] Fix: Zombie Shootout Baby Zombie Bounding Box (#2512) --- .../event/carnival/CarnivalZombieShootout.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt index d57903a57949..79e93f2addf5 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt @@ -23,6 +23,7 @@ import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.entity.monster.EntityZombie import net.minecraft.init.Blocks import net.minecraft.item.ItemStack +import net.minecraft.util.AxisAlignedBB import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.awt.Color import kotlin.time.Duration.Companion.seconds @@ -95,9 +96,21 @@ object CarnivalZombieShootout { for ((zombie, type) in drawZombies) { val entity = EntityUtils.getEntityByID(zombie.entityId) ?: continue + val isSmall = (entity as? EntityZombie)?.isChild ?: false + + val boundingBox = entity.entityBoundingBox.let { + if (isSmall) { + val widthScale = 0.5 + val heightScale = 0.85 + val newMin = it.minX + (it.maxX - it.minX) * (1 - widthScale) / 2 + val newMax = it.maxX - (it.maxX - it.minX) * (1 - widthScale) / 2 + val newHeight = it.minY + (it.maxY - it.minY) * heightScale / 1.75 + AxisAlignedBB(newMin, it.minY, it.minZ, newMax, newHeight, it.maxZ) + } else it + }.expand(0.1, 0.05, 0.0).offset(0.0, 0.05, 0.0) event.drawHitbox( - entity.entityBoundingBox.expand(0.1, 0.05, 0.0).offset(0.0, 0.05, 0.0), + boundingBox, lineWidth = 3, type.color, depth = false, From 8bea71e2a047cd643e88907fe9a6a7d98f47fdc8 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 15 Sep 2024 12:13:02 +0200 Subject: [PATCH 005/102] Fix: Catch a Fish Goals (#2517) --- .../skyhanni/features/event/carnival/CarnivalGoal.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalGoal.kt b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalGoal.kt index c36212a22d8b..beedfd6a5676 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalGoal.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalGoal.kt @@ -69,13 +69,13 @@ enum class CarnivalGoal( CATCH_A_FISH_SCORE( GoalType.CATCH_A_FISH, "§7Reach §a3,000 score §7in a single game", - "(§8 - §r)?§7Catch §r§a30 Fish §r§7in a single game of §r§3Catch a Fish§r§7.", + "(§8 - §r)?§7Reach §r§a3,000 score §r§7in a single game of §r§3Catch a Fish§r§7.", "Reach §a3,000 score", ), CATCH_FISH( GoalType.CATCH_A_FISH, "§7Catch §a30 Fish §7in a single game of", - "(§8 - §r)?§7Reach §r§a3,000 score §r§7in a single game of §r§3Catch a Fish§r§7.", + "(§8 - §r)?§7Catch §r§a30 Fish §r§7in a single game of §r§3Catch a Fish§r§7.", "Catch §a30 Fish", ), CATCH_YELLOW_FISH( From b8f4b63f6efdef2cb189d6094b5f5bad17b8a80a Mon Sep 17 00:00:00 2001 From: The1Divider <65572114+The1Divider@users.noreply.github.com> Date: Sun, 15 Sep 2024 04:25:29 -0600 Subject: [PATCH 006/102] Feature: Highlight More Hoppity's Collection Rabbits (#2438) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../ChocolateFactoryConfig.java | 21 ++-- .../event/hoppity/HoppityCollectionStats.kt | 116 +++++++++++++++--- 2 files changed, 109 insertions(+), 28 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java index e35bc4d7e790..fa817ede042f 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java @@ -2,6 +2,7 @@ import at.hannibal2.skyhanni.config.FeatureToggle; import at.hannibal2.skyhanni.config.core.config.Position; +import at.hannibal2.skyhanni.features.event.hoppity.HoppityCollectionStats.HighlightRabbitTypes; import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStats.ChocolateFactoryStat; import com.google.gson.annotations.Expose; import io.github.notenoughupdates.moulconfig.annotations.Accordion; @@ -174,18 +175,22 @@ public class ChocolateFactoryConfig { public boolean hoppityMenuShortcut = true; @Expose - @ConfigOption(name = "Highlight Requirement Rabbits", desc = "Highlight rabbits that have requirements.\n" + - "§cRed: Requirement not met.\n" + - "§aGreen: Requirement met.") + @ConfigOption(name = "Highlight Found Rabbits", desc = "Highlight rabbits that have already been found.") @ConfigEditorBoolean @FeatureToggle - public boolean highlightRabbitsWithRequirement = false; + public boolean highlightFoundRabbits = false; @Expose - @ConfigOption(name = "Only Requirement Not Met", desc = "Only highlight the rabbits you don't have the requirement for.") - @ConfigEditorBoolean - @FeatureToggle - public boolean onlyHighlightRequirementNotMet = true; + @ConfigOption(name = "Highlight Rabbits", desc = "Highlight specific rabbit types in Hoppity's Collection.") + @ConfigEditorDraggableList + public List highlightRabbits = new ArrayList<>(Arrays.asList( + HighlightRabbitTypes.ABI, + HighlightRabbitTypes.FACTORY, + HighlightRabbitTypes.MET, + HighlightRabbitTypes.NOT_MET, + HighlightRabbitTypes.SHOP, + HighlightRabbitTypes.STRAYS + )); @Expose @ConfigOption( diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt index a06b3dcbc202..800ac5729aaf 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt @@ -24,15 +24,17 @@ import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatInt import at.hannibal2.skyhanni.utils.RegexUtils.anyMatches -import at.hannibal2.skyhanni.utils.RegexUtils.find import at.hannibal2.skyhanni.utils.RegexUtils.findMatcher -import at.hannibal2.skyhanni.utils.RegexUtils.matchFirst +import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern @SkyHanniModule object HoppityCollectionStats { @@ -97,6 +99,30 @@ object HoppityCollectionStats { "§c✖ §7Requirement §e(?[\\d,]+)§7/§a(?[\\d,]+)", ) + /** + * REGEX-TEST: §6Factory Milestones§7. + */ + private val factoryMilestone by RepoPattern.pattern( + "rabbit.requirement.factory", + "§6Factory Milestones§7.", + ) + + /** + * REGEX-TEST: §6Shop Milestones§7. + */ + private val shopMilestone by RepoPattern.pattern( + "rabbit.requirement.shop", + "§6Shop Milestones§7.", + ) + + /** + * REGEX-TEST: §7§7Obtained by finding the §aStray Rabbit + */ + private val strayRabbit by RepoPattern.pattern( + "rabbit.requirement.stray", + "§7§7Obtained by finding the §aStray Rabbit", + ) + /** * REGEX-TEST: Find 15 unique egg locations in the Deep Caverns. */ @@ -109,6 +135,21 @@ object HoppityCollectionStats { private val loggedRabbits get() = ProfileStorageData.profileSpecific?.chocolateFactory?.rabbitCounts ?: mutableMapOf() + enum class HighlightRabbitTypes( + private val displayName: String, + val color: LorenzColor, + ) { + ABI("§2Abi", LorenzColor.DARK_GREEN), + FACTORY("§eFactory Milestones", LorenzColor.YELLOW), + MET("§aRequirement Met", LorenzColor.GREEN), + NOT_MET("§cRequirement Not Met.", LorenzColor.RED), + SHOP("§6Shop Milestones", LorenzColor.GOLD), + STRAYS("§3Stray Rabbits", LorenzColor.DARK_AQUA), + ; + + override fun toString(): String = displayName + } + @KSerializable data class LocationRabbit( val locationName: String, @@ -133,17 +174,57 @@ object HoppityCollectionStats { var inInventory = false + private val highlightConfigMap: Map = mapOf( + factoryMilestone to HighlightRabbitTypes.FACTORY, + requirementMet to HighlightRabbitTypes.MET, + requirementNotMet to HighlightRabbitTypes.NOT_MET, + shopMilestone to HighlightRabbitTypes.SHOP, + strayRabbit to HighlightRabbitTypes.STRAYS, + ) + @SubscribeEvent fun onInventoryOpen(event: InventoryFullyOpenedEvent) { if (!(LorenzUtils.inSkyBlock)) return - if (!pagePattern.matches(event.inventoryName)) return + if (!pagePattern.matches(event.inventoryName)) { + // Clear highlight cache in case options are toggled + highlightMap.clear() + return + } inInventory = true if (config.hoppityCollectionStats) { display = buildDisplay(event) } + + if (config.highlightRabbits.isNotEmpty()) { + for ((_, stack) in event.inventoryItems) filterRabbitToHighlight(stack) + } } + private fun filterRabbitToHighlight(stack: ItemStack) { + val lore = stack.getLore() + + if (lore.isEmpty()) return + if (!rabbitNotFoundPattern.anyMatches(lore) && !config.highlightFoundRabbits) return + + if (highlightMap.containsKey(stack.displayName)) return + + if (stack.displayName == "§aAbi" && config.highlightRabbits.contains(HighlightRabbitTypes.ABI)) { + highlightMap[stack.displayName] = HighlightRabbitTypes.ABI.color + return + } + + // cache rabbits until collection is closed + for ((pattern, rabbitType) in highlightConfigMap) { + if (pattern.anyMatches(lore) && config.highlightRabbits.contains(rabbitType)) { + highlightMap[stack.displayName] = rabbitType.color + break + } + } + } + + private var highlightMap = mutableMapOf() + @SubscribeEvent fun onInventoryClose(event: InventoryCloseEvent) { inInventory = false @@ -162,22 +243,17 @@ object HoppityCollectionStats { ) } - // TODO cache with inventory update event @SubscribeEvent fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { - if (!config.highlightRabbitsWithRequirement) return if (!inInventory) return + if (config.highlightRabbits.isEmpty()) return for (slot in InventoryUtils.getItemsInOpenChest()) { - val lore = slot.stack.getLore() - if (lore.any { requirementMet.find(it) } && !config.onlyHighlightRequirementNotMet) - slot highlight LorenzColor.GREEN - if (lore.any { requirementNotMet.find(it) }) { - val found = !rabbitNotFoundPattern.anyMatches(lore) - // Hypixel allows purchasing Rabbits from Hoppity NPC even when the requirement is not yet met. - if (!found) { - slot highlight LorenzColor.RED - } + val name = slot.stack.displayName + + if (name.isEmpty()) continue + highlightMap[name]?.let { + slot highlight it } } } @@ -186,9 +262,10 @@ object HoppityCollectionStats { if (!config.showLocationRequirementsRabbitsInHoppityStats) return val missingLocationRabbits = locationRabbitRequirements.values.filter { !it.hasMetRequirements() } - val tips = locationRabbitRequirements.map { - it.key + " §7(§e" + it.value.locationName + "§7): " + (if (it.value.hasMetRequirements()) "§a" else "§c") + - it.value.foundCount + "§7/§a" + it.value.requiredCount + val tips = locationRabbitRequirements.map { (name, rabbit) -> + "$name §7(§e${rabbit.locationName}§7): ${ + if (rabbit.hasMetRequirements()) "§a" else "§c" + }${rabbit.foundCount}§7/§a${rabbit.requiredCount}" } newList.add( @@ -311,7 +388,7 @@ object HoppityCollectionStats { // used to make sure that mod data is synchronized with Hypixel private fun getFoundRabbitsFromHypixel(event: InventoryFullyOpenedEvent): Int { return event.inventoryItems.firstNotNullOf { - it.value.getLore().matchFirst(rabbitsFoundPattern) { + rabbitsFoundPattern.firstMatcher(it.value.getLore()) { group("current").formatInt() } } @@ -354,7 +431,7 @@ object HoppityCollectionStats { if (!found) continue - val duplicates = itemLore.matchFirst(duplicatesFoundPattern) { + val duplicates = duplicatesFoundPattern.firstMatcher(itemLore) { group("duplicates").formatInt() } ?: 0 @@ -362,7 +439,6 @@ object HoppityCollectionStats { } } - // bugfix for some weird potential user errors (e.g. if users play on alpha and get rabbits) fun clearSavedRabbits() { loggedRabbits.clear() From 0a49e3eb55f8804d6716fa8dd1239c502682726f Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 16 Sep 2024 10:42:40 +0200 Subject: [PATCH 007/102] Fix: Old mayor perks not being marked as inactive (#2524) --- src/main/java/at/hannibal2/skyhanni/data/Mayors.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt b/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt index 78647944c9a0..335648dc5116 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt @@ -146,6 +146,7 @@ enum class Mayor( fun Mayor.addPerks(perks: List) { perks.forEach { it.isActive = false } + activePerks.forEach { it.isActive = false } activePerks.clear() for (perk in perks.filter { perks.contains(it) }) { perk.isActive = true From 2e959c27fa0e0ec75bf26456df9ba80d2a8e5e5b Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Tue, 17 Sep 2024 08:53:33 +0200 Subject: [PATCH 008/102] Fix: Lag spikes in pathfinder (#2525) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt index c5c0071f95da..d85b34cc6936 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt @@ -30,6 +30,7 @@ import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.renderables.Searchable import at.hannibal2.skyhanni.utils.renderables.buildSearchBox import at.hannibal2.skyhanni.utils.renderables.toSearchable +import kotlinx.coroutines.launch import net.minecraft.client.Minecraft import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.seconds @@ -54,7 +55,9 @@ object IslandAreas { } fun noteMoved() { - updateNodes() + SkyHanniMod.coroutineScope.launch { + updateNodes() + } } private fun updateNodes() { From bae0275fc7b176c58c53b827e6b717aec351854f Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Tue, 17 Sep 2024 08:57:14 +0200 Subject: [PATCH 009/102] Fix: Corpse Tracker visiblility (#2516) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../features/mining/glacitemineshaft/CorpseTracker.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt index c54315d7e430..f1d23185f3e0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt @@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.features.mining.glacitemineshaft import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.MiningAPI import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.IslandChangeEvent import at.hannibal2.skyhanni.events.mining.CorpseLootedEvent @@ -82,8 +83,8 @@ object CorpseTracker { if (bucketData.getCorpseCount() == 0L) return@buildList var profit = tracker.drawItems(bucketData, { true }, this) - val applicableKeys: List = bucketData.getSelectedBucket()?.let { listOf(it) } - ?: enumValues().toList().filter { bucketData.corpsesLooted[it] != null } + val applicableKeys: List = bucketData.getSelectedBucket()?.let { listOf(it) } ?: enumValues().toList() + .filter { bucketData.corpsesLooted[it] != null } var totalKeyCost = 0.0 var totalKeyCount = 0 val keyCostStrings = buildList { @@ -138,5 +139,5 @@ object CorpseTracker { } fun isEnabled() = - config.enabled && IslandType.DWARVEN_MINES.isInIsland() && (!config.onlyInMineshaft || IslandType.MINESHAFT.isInIsland()) + LorenzUtils.inSkyBlock && config.enabled && (IslandType.MINESHAFT.isInIsland() || (!config.onlyInMineshaft && MiningAPI.inGlacialTunnels())) } From 1247a11ea7846af1531546263c525b3e005d0eed Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Tue, 17 Sep 2024 03:00:08 -0400 Subject: [PATCH 010/102] Fix: BucketedItemTrackerData Not Exposed (#2528) --- .../skyhanni/utils/tracker/BucketedItemTrackerData.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/at/hannibal2/skyhanni/utils/tracker/BucketedItemTrackerData.kt b/src/main/java/at/hannibal2/skyhanni/utils/tracker/BucketedItemTrackerData.kt index 3bedaf8a4303..2197bf5c0e9e 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/tracker/BucketedItemTrackerData.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/tracker/BucketedItemTrackerData.kt @@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.tracker.ItemTrackerData.TrackedItem +import com.google.gson.annotations.Expose import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl abstract class BucketedItemTrackerData> : TrackerData() { @@ -64,7 +65,9 @@ abstract class BucketedItemTrackerData> : TrackerData() { ) } + @Expose private var selectedBucket: E? = null + @Expose private var bucketedItems: MutableMap> = HashMap() private fun getBucket(bucket: E): MutableMap = bucketedItems[bucket]?.toMutableMap() ?: HashMap() From 46d39d831c23b02481c3ed0b34ebf74ac26bc088 Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:35:17 +0200 Subject: [PATCH 011/102] Fix: Efficient Miner on Ore Mined Event (#2526) Co-authored-by: ItsEmpa --- .../at/hannibal2/skyhanni/data/MiningAPI.kt | 39 +++++++++++-------- .../skyhanni/events/ServerBlockChangeEvent.kt | 4 +- .../skyhanni/features/mining/OreBlock.kt | 2 +- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt index ecf90172dc79..fd399ae8b037 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt @@ -8,11 +8,13 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.PlaySoundEvent import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent +import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.events.ServerBlockChangeEvent import at.hannibal2.skyhanni.events.mining.OreMinedEvent import at.hannibal2.skyhanni.events.player.PlayerDeathEvent import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern import at.hannibal2.skyhanni.features.mining.OreBlock +import at.hannibal2.skyhanni.features.mining.isTitanium import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.countBy import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer @@ -50,8 +52,7 @@ object MiningAPI { private var lastInitSound = SimpleTimeMark.farPast() - private var waitingForInitBlock = false - private var waitingForInitBlockPos: LorenzVec? = null + private var initBlockPos: LorenzVec? = null private var waitingForInitSound = true private var waitingForEffMinerSound = false @@ -146,8 +147,8 @@ object MiningAPI { val pos = event.location.roundLocationToBlock() if (recentClickedBlocks.none { it.first == pos }) return waitingForInitSound = false - waitingForInitBlock = true - waitingForInitBlockPos = event.location.roundLocationToBlock() + waitingForEffMinerBlock = true + initBlockPos = event.location.roundLocationToBlock() lastInitSound = SimpleTimeMark.now() } return @@ -164,21 +165,25 @@ object MiningAPI { @SubscribeEvent fun onBlockChange(event: ServerBlockChangeEvent) { if (!inCustomMiningIsland()) return - if (event.newState.block.let { it != Blocks.air && it != Blocks.bedrock }) return - if (event.oldState.block.let { it == Blocks.air || it == Blocks.bedrock }) return - if (event.oldState.block == Blocks.air) return + val oldState = event.oldState + val newState = event.newState + val oldBlock = oldState.block + val newBlock = newState.block + + if (oldState == newState) return + if (oldBlock == Blocks.air || oldBlock == Blocks.bedrock) return + if (newBlock != Blocks.air && newBlock != Blocks.bedrock && !isTitanium(newState)) return + val pos = event.location if (pos.distanceToPlayer() > 7) return if (lastInitSound.passedSince() > 100.milliseconds) return - val ore = OreBlock.getByStateOrNull(event.oldState) ?: return + val ore = OreBlock.getByStateOrNull(oldState) ?: return - if (waitingForInitBlock) { - if (waitingForInitBlockPos != pos) return - waitingForInitBlock = false + if (initBlockPos == pos) { surroundingMinedBlocks += MinedBlock(ore, true) to pos - waitingForEffMinerBlock = true + runEvent() return } if (waitingForEffMinerBlock) { @@ -205,7 +210,11 @@ object MiningAPI { if (waitingForInitSound) return if (lastInitSound.passedSince() < 200.milliseconds) return + // in case the init block is not found + resetOreEvent() + } + private fun runEvent() { resetOreEvent() if (surroundingMinedBlocks.isEmpty()) return @@ -237,8 +246,7 @@ object MiningAPI { private fun resetOreEvent() { lastInitSound = SimpleTimeMark.farPast() waitingForInitSound = true - waitingForInitBlock = false - waitingForInitBlockPos = null + initBlockPos = null waitingForEffMinerSound = false waitingForEffMinerBlock = false } @@ -258,8 +266,7 @@ object MiningAPI { add("lastInitSound: ${lastInitSound.passedSince().format()}") } add("waitingForInitSound: $waitingForInitSound") - add("waitingForInitBlock: $waitingForInitBlock") - add("waitingForInitBlockPos: $waitingForInitBlockPos") + add("waitingForInitBlockPos: $initBlockPos") add("waitingForEffMinerSound: $waitingForEffMinerSound") add("waitingForEffMinerBlock: $waitingForEffMinerBlock") add("recentlyClickedBlocks: ${recentClickedBlocks.joinToString { "(${it.first.toCleanString()}" }}") diff --git a/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt index 07aa7630ed37..106ee2821cb3 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/ServerBlockChangeEvent.kt @@ -7,7 +7,7 @@ import at.hannibal2.skyhanni.utils.toLorenzVec import net.minecraft.block.state.IBlockState import net.minecraft.util.BlockPos -class ServerBlockChangeEvent(private val blockPos: BlockPos, private val blockState: IBlockState) : LorenzEvent() { +class ServerBlockChangeEvent(blockPos: BlockPos, blockState: IBlockState) : LorenzEvent() { val location by lazy { blockPos.toLorenzVec() } val old by lazy { location.getBlockAt().toString().getName() } @@ -17,7 +17,7 @@ class ServerBlockChangeEvent(private val blockPos: BlockPos, private val blockSt companion object { - val pattern = "Block\\{minecraft:(?.*)}".toPattern() + private val pattern = "Block\\{minecraft:(?.*)}".toPattern() private fun String.getName() = pattern.matchMatcher(this) { group("name") diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt index 7f5582036dfd..c5d9774b15bf 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt @@ -252,7 +252,7 @@ private fun isHighTierMithril(state: IBlockState): Boolean { return (state.block == Blocks.wool && state.getValue(BlockColored.COLOR) == EnumDyeColor.LIGHT_BLUE) } -private fun isTitanium(state: IBlockState): Boolean { +fun isTitanium(state: IBlockState): Boolean { return (state.block == Blocks.stone && state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.DIORITE_SMOOTH) } From fe59a3143b0b8a181869c5f11b5c869b6ea7c225 Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:36:45 +0200 Subject: [PATCH 012/102] Fix: Dojo Pattern (#2519) --- .../gui/customscoreboard/ScoreboardPattern.kt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt index 9e2694ec0d88..1c6fb4282fec 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt @@ -364,23 +364,28 @@ object ScoreboardPattern { ) val dojoChallengePattern by miscSb.pattern( "dojochallenge", - "^(§.)*Challenge: (§.)*(?[\\w ]+)$", + "(§.)*Challenge: (§.)*(?[\\w ]+)", ) val dojoDifficultyPattern by miscSb.pattern( "dojodifficulty", - "^(§.)*Difficulty: (§.)*(?[\\w ]+)$", + "(§.)*Difficulty: (§.)*(?[\\w ]+)", ) val dojoPointsPattern by miscSb.pattern( "dojopoints", - "^(§.)*Points: (§.)*[\\w.]+ ?(§7\\(§.*§7\\))?\$", + "(§.)*Points: (§.)*[\\w.]+.*", ) + + /** + * There should be a space at the end of the test, Intellij keeps removing it + * REGEX-TEST: Time: §a20s + */ val dojoTimePattern by miscSb.pattern( "dojotime", - "^(§.)*Time: (§.)*[\\w.]+( §7\\(§.*§7\\))?$", + "(§.)*Time: (§.)*[\\w.]+.*", ) val objectivePattern by miscSb.pattern( "objective", - "^(§.)*(Objective|Quest).*", + "(§.)*(Objective|Quest).*", ) val queuePattern by miscSb.pattern( "queued", From 5cba1329e720553854e79557815cdf9319768850 Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:39:08 +0200 Subject: [PATCH 013/102] Fix: Baby Zombie Carnival Size (#2518) --- .../event/carnival/CarnivalZombieShootout.kt | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt index 79e93f2addf5..23e7b9ee48b0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt @@ -23,7 +23,6 @@ import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.entity.monster.EntityZombie import net.minecraft.init.Blocks import net.minecraft.item.ItemStack -import net.minecraft.util.AxisAlignedBB import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.awt.Color import kotlin.time.Duration.Companion.seconds @@ -98,19 +97,11 @@ object CarnivalZombieShootout { val entity = EntityUtils.getEntityByID(zombie.entityId) ?: continue val isSmall = (entity as? EntityZombie)?.isChild ?: false - val boundingBox = entity.entityBoundingBox.let { - if (isSmall) { - val widthScale = 0.5 - val heightScale = 0.85 - val newMin = it.minX + (it.maxX - it.minX) * (1 - widthScale) / 2 - val newMax = it.maxX - (it.maxX - it.minX) * (1 - widthScale) / 2 - val newHeight = it.minY + (it.maxY - it.minY) * heightScale / 1.75 - AxisAlignedBB(newMin, it.minY, it.minZ, newMax, newHeight, it.maxZ) - } else it - }.expand(0.1, 0.05, 0.0).offset(0.0, 0.05, 0.0) + val boundingBox = if (isSmall) entity.entityBoundingBox.expand(0.0, -0.4, 0.0).offset(0.0, -0.4, 0.0) + else entity.entityBoundingBox event.drawHitbox( - boundingBox, + boundingBox.expand(0.1, 0.05, 0.0).offset(0.0, 0.05, 0.0), lineWidth = 3, type.color, depth = false, From a1a903e3a3d223d2654ad941070a552c57fb6642 Mon Sep 17 00:00:00 2001 From: MTOnline69 <97001154+MTOnline69@users.noreply.github.com> Date: Tue, 17 Sep 2024 08:40:31 +0100 Subject: [PATCH 014/102] Removed Feature: Wild Strawberry Dye notification (#2520) --- .../config/features/garden/GardenConfig.java | 6 --- .../farming/WildStrawberryDyeNotification.kt | 48 ------------------- 2 files changed, 54 deletions(-) delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/GardenConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/GardenConfig.java index b39f5f01ce63..8aa9aa6a1124 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/GardenConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/GardenConfig.java @@ -158,12 +158,6 @@ public class GardenConfig { @FeatureToggle public boolean burrowingSporesNotification = true; - @Expose - @ConfigOption(name = "Wild Strawberry", desc = "Show a notification when a Wild Strawberry Dye drops while farming.") - @ConfigEditorBoolean - @FeatureToggle - public boolean wildStrawberryDyeNotification = true; - @Expose @ConfigOption( name = "FF for Contest", diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt deleted file mode 100644 index 34b4191d4e46..000000000000 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/WildStrawberryDyeNotification.kt +++ /dev/null @@ -1,48 +0,0 @@ -package at.hannibal2.skyhanni.features.garden.farming - -import at.hannibal2.skyhanni.events.InventoryCloseEvent -import at.hannibal2.skyhanni.events.OwnInventoryItemUpdateEvent -import at.hannibal2.skyhanni.features.garden.GardenAPI -import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.ChatUtils -import at.hannibal2.skyhanni.utils.ItemBlink -import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName -import at.hannibal2.skyhanni.utils.ItemUtils.name -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName -import at.hannibal2.skyhanni.utils.SimpleTimeMark -import at.hannibal2.skyhanni.utils.SoundUtils -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import kotlin.time.Duration.Companion.seconds - -@SkyHanniModule -object WildStrawberryDyeNotification { - - private var lastCloseTime = SimpleTimeMark.farPast() - - val item by lazy { "DYE_WILD_STRAWBERRY".asInternalName() } - - @SubscribeEvent - fun onInventoryClose(event: InventoryCloseEvent) { - lastCloseTime = SimpleTimeMark.now() - } - - @SubscribeEvent - fun onOwnInventoryItemUpdate(event: OwnInventoryItemUpdateEvent) { - if (!GardenAPI.inGarden()) return - if (!GardenAPI.config.wildStrawberryDyeNotification) return - // Prevent false positives when buying the item in ah or moving it from a storage - if (lastCloseTime.passedSince() < 1.seconds) return - - val itemStack = event.itemStack - - val internalName = itemStack.getInternalName() - if (internalName == item) { - val name = itemStack.name - LorenzUtils.sendTitle(name, 5.seconds) - ChatUtils.chat("You found a $name§e!") - SoundUtils.playBeepSound() - ItemBlink.setBlink(itemStack, 5_000) - } - } -} From 9de67ded5a222598e7b2e6225210785bd690530e Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:27:50 +0200 Subject: [PATCH 015/102] better display names --- .../skyhanni/config/features/inventory/InventoryConfig.java | 1 + .../skyhanni/features/garden/composter/ComposterDisplay.kt | 2 +- .../skyhanni/features/mining/eventtracker/MiningEventDisplay.kt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java index cf162ad7b6a8..5c3bf3170920 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java @@ -61,6 +61,7 @@ public class InventoryConfig { @Expose @ConfigOption(name = "Item Pickup Log", desc = "Logs all the picked up and dropped items") @Accordion + // TODO remove the suffix "config" public ItemPickupLogConfig itemPickupLogConfig = new ItemPickupLogConfig(); @Expose diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt index cebbeef08a71..058ae15909f9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt @@ -180,7 +180,7 @@ object ComposterDisplay { val outsideSb = !LorenzUtils.inSkyBlock && OutsideSbFeature.COMPOSTER_TIME.isSelected() if (!GardenAPI.inGarden() && (inSb || outsideSb)) { val list = Collections.singletonList(listOf(bucket, "§b$format")) - config.outsideGardenPos.renderStringsAndItems(list, posLabel = "Composter Outside Garden Display") + config.outsideGardenPos.renderStringsAndItems(list, posLabel = "Composter Outside Garden") } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventDisplay.kt index 26f41c799b32..a5c34850a2dc 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventDisplay.kt @@ -33,7 +33,7 @@ object MiningEventDisplay { @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) { if (!shouldDisplay()) return - config.position.renderRenderables(display, posLabel = "Upcoming Events Display") + config.position.renderRenderables(display, posLabel = "Upcoming Mining Events") } private fun updateDisplay() { From d1bb73a6826b4c56694a47b5811980ec4cdc90af Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:28:17 +0200 Subject: [PATCH 016/102] Improvement: Updated Carnival Diamond Zombie Color (#2511) --- .../skyhanni/features/event/carnival/CarnivalZombieShootout.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt index 23e7b9ee48b0..ec18d016b561 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/carnival/CarnivalZombieShootout.kt @@ -64,7 +64,7 @@ object CarnivalZombieShootout { LEATHER(30, "Leather Cap", Color(165, 42, 42)), //Brown IRON(50, "Iron Helmet", Color(192, 192, 192)), //Silver GOLD(80, "Golden Helmet", Color(255, 215, 0)), //Gold - DIAMOND(120, "Diamond Helmet", Color(185, 242, 255)) //Diamond + DIAMOND(120, "Diamond Helmet", Color(44, 214, 250)) //Diamond } @SubscribeEvent From 1dcc43a3f37591912f086299c298df4e0e471bd9 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:28:38 +0200 Subject: [PATCH 017/102] Improvement: More Graph Things (#2515) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../config/features/dev/GraphConfig.java | 12 + .../features/rift/EnigmaSoulConfig.java | 8 +- .../hannibal2/skyhanni/data/IslandGraphs.kt | 8 +- .../skyhanni/data/model/GraphNodeTag.kt | 32 ++- .../rift/everywhere/EnigmaSoulWaypoints.kt | 33 ++- .../at/hannibal2/skyhanni/test/GraphEditor.kt | 207 +++--------------- .../skyhanni/test/GraphNodeEditor.kt | 178 +++++++++++++++ .../skyhanni/test/SkyHanniDebugsAndTests.kt | 1 + 8 files changed, 284 insertions(+), 195 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java index 9eed8233c995..acf9ac67d9e0 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java @@ -4,6 +4,7 @@ import com.google.gson.annotations.Expose; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; import org.lwjgl.input.Keyboard; @@ -99,6 +100,17 @@ public class GraphConfig { @ConfigLink(owner = GraphConfig.class, field = "enabled") public Position namedNodesList = new Position(20, 20); + @Expose + @ConfigOption( + name = "Max Node Distance", + desc = "Only render nodes below this distance to the player.") + @ConfigEditorSlider( + minValue = 10, + maxValue = 500, + minStep = 10 + ) + public int maxNodeDistance = 50; + @Expose @ConfigOption(name = "Shows Stats", desc = "Show funny extra statistics on save. May lag the game a bit.") @ConfigEditorBoolean diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java index 96c721539c1c..f64a42506043 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java @@ -15,6 +15,11 @@ public class EnigmaSoulConfig { @FeatureToggle public boolean enabled = true; + @Expose + @ConfigOption(name = "Show Path Finder", desc = "Additionally show a pathfind to the Enigma Soul.") + @ConfigEditorBoolean + public boolean showPathFinder = true; + @ConfigOption( name = "§aRift Guide", desc = "Type §e/riftguide §7in chat or navigate through the SkyBlock Menu to open the §aRift Guide§7. " + @@ -26,6 +31,5 @@ public class EnigmaSoulConfig { @Expose @ConfigOption(name = "Color", desc = "Color of the Enigma Souls.") @ConfigEditorColour - public String color = "0:120:13:49:255"; - + public String color = "0:245:219:27:198"; } diff --git a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt index 02a065abe63a..0b173db46e04 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt @@ -40,6 +40,7 @@ import kotlin.math.abs * jump pads between servers * ring of love/romeo juliet quest * death location + * % of island discvovered (every node was most closest node at least once) * hub: * 12 starter NPC's * diana @@ -49,6 +50,8 @@ import kotlin.math.abs * enigma souls * eyes * big quests + * montezuma souls + * blood effigies * spider: * relicts + throw spot * dwarven mines: @@ -56,7 +59,7 @@ import kotlin.math.abs * commssion areas * events: raffle, goblin slayer, donpieresso * deep - * path to the bottom + * path to the bottom (Rhys NPC) * end * golem spawn * dragon death spot @@ -65,12 +68,15 @@ import kotlin.math.abs * area mini bosses * daily quests * intro tutorials with elle + * fishing spots * * graph todo: * fix rename not using tick but input event we have (+ create the input event in the first place) * toggle distance to node by node path lengh, instead of eye of sight lenght * press test button again to enable "true test mode", with graph math and hiding other stuff * option to compare two graphs, and store multiple graphs in the edit mode in paralell + * + * mineshaft + corpse + ladder spot */ @SkyHanniModule diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt index a7349801324a..e1fa3317a8cf 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt @@ -1,8 +1,15 @@ package at.hannibal2.skyhanni.data.model +import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.utils.LorenzColor -enum class GraphNodeTag(val internalName: String?, val color: LorenzColor, val cleanName: String, val description: String) { +enum class GraphNodeTag( + val internalName: String, + val color: LorenzColor, + val cleanName: String, + val description: String, + val onlyIsland: IslandType? = null, +) { DEV("dev", LorenzColor.WHITE, "Dev", "Intentionally marked as dev."), // E.g. Spawn points, todos, etc // Everywhere @@ -16,31 +23,40 @@ enum class GraphNodeTag(val internalName: String?, val color: LorenzColor, val c ROMEO("romeo", LorenzColor.WHITE, "Romeo & Juliette Quest", "Blocks related to the Romeo and Juliette/Ring of Love quest line."), RACE("race", LorenzColor.WHITE, "Race Start/Stop", "A race start or stop point."), SLAYER("slayer", LorenzColor.WHITE, "Slayer", "A Slayer area"), + HOPPITY("hoppity", LorenzColor.AQUA, "Hoppity Egg", "An egg location in Hoppity's Hunt."), // hoppity // Hub - HUB_12_STARTER("starter_npc", LorenzColor.WHITE, "Starter NPC", "One of the 12 starter NPC's you need to talk to."), + HUB_12_STARTER( + "starter_npc", LorenzColor.WHITE, "Starter NPC", "One of the 12 starter NPC's you need to talk to.", + onlyIsland = IslandType.HUB, + ), // diana // Farming Islands: Pelts - FARMING_CROP("farming_crop", LorenzColor.WHITE, "Farming Crop", "A spot where you can break crops on farming islands."), +// FARMING_CROP("farming_crop", LorenzColor.WHITE, "Farming Crop", "A spot where you can break crops on farming islands."), // Rift - RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the rift."), - RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Eye", "An Eye in the rift to teleport to."), + RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the rift.", onlyIsland = IslandType.THE_RIFT), + RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Rift Eye", "An Eye in the rift to teleport to.", onlyIsland = IslandType.THE_RIFT), + RIFT_MONTEZUMA("rift_montezuma", LorenzColor.GRAY, "Montezuma Soul Piece", "A piece of the Montezuma Soul.", onlyIsland = IslandType.THE_RIFT), + RIFT_EFFIGY("rift_effigy", LorenzColor.RED, "Blood Effigies", "Locations of the Blood Effigies.", onlyIsland = IslandType.THE_RIFT), // Spider's Den - SPIDER_RELIC("SPIDER_RELIC", LorenzColor.DARK_PURPLE, "Relic", "An relic in the Spider's Den."), + SPIDER_RELIC("SPIDER_RELIC", LorenzColor.DARK_PURPLE, "Spider's Relic", "An relic in the Spider's Den.", onlyIsland = IslandType.SPIDER_DEN), // Dwarven Mines - MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Emissary", "A Emissary from the king."), + MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Mines Emissary", "A Emissary from the king.", onlyIsland = IslandType.DWARVEN_MINES), // commission areas + // Crimson Isles + CRIMSON_MINIBOSS("crimson_miniboss", LorenzColor.RED, "Crimson Miniboss", "Mini bosses in Crimson Isle.", onlyIsland = IslandType.CRIMSON_ISLE), + ; val displayName: String = color.getChatColor() + cleanName companion object { - fun byId(internalName: String?): GraphNodeTag? = values().firstOrNull { it.internalName == internalName } + fun byId(internalName: String?): GraphNodeTag? = entries.firstOrNull { it.internalName == internalName } } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt index 67bf84aa94a7..d91b4008d912 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni.features.rift.everywhere +import at.hannibal2.skyhanni.data.IslandGraphs import at.hannibal2.skyhanni.data.jsonobjects.repo.EnigmaSoulsJson import at.hannibal2.skyhanni.events.GuiContainerEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent @@ -11,6 +12,7 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.features.rift.RiftAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor import at.hannibal2.skyhanni.utils.InventoryUtils.getAllItems import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer @@ -46,7 +48,7 @@ object EnigmaSoulWaypoints { "§5Toggle Missing", "§7Click here to toggle", "§7the waypoints for each", - "§7missing souls on this page" + "§7missing souls on this page", ) } @@ -97,19 +99,26 @@ object EnigmaSoulWaypoints { } if (event.slot?.stack == null) return + val split = event.slot.stack.displayName.split("Enigma: ") - if (split.size == 2) { - event.makePickblock() - val name = split.last() - if (soulLocations.contains(name)) { - if (!trackedSouls.contains(name)) { - ChatUtils.chat("§5Tracking the $name Enigma Soul!", prefixColor = "§5") - trackedSouls.add(name) - } else { - trackedSouls.remove(name) - ChatUtils.chat("§5No longer tracking the $name Enigma Soul!", prefixColor = "§5") + if (split.size != 2) return + + event.makePickblock() + val name = split.last() + if (!soulLocations.contains(name)) return + + if (!trackedSouls.contains(name)) { + ChatUtils.chat("§5Tracking the $name Enigma Soul!", prefixColor = "§5") + if (config.showPathFinder) { + soulLocations[name]?.let { + IslandGraphs.pathFind(it, config.color.toChromaColor(), condition = { config.showPathFinder }) } } + trackedSouls.add(name) + } else { + trackedSouls.remove(name) + ChatUtils.chat("§5No longer tracking the $name Enigma Soul!", prefixColor = "§5") + IslandGraphs.stop() } } @@ -138,7 +147,7 @@ object EnigmaSoulWaypoints { if (!isEnabled()) return for (soul in trackedSouls) { soulLocations[soul]?.let { - event.drawWaypointFilled(it, LorenzColor.DARK_PURPLE.toColor(), seeThroughBlocks = true, beacon = true) + event.drawWaypointFilled(it, config.color.toChromaColor(), seeThroughBlocks = true, beacon = true) event.drawDynamicText(it.add(y = 1), "§5${soul.removeSuffix(" Soul")} Soul", 1.5) } } diff --git a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt b/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt index 81a85283e828..d4038162f865 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt @@ -14,13 +14,12 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.ChatUtils -import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString -import at.hannibal2.skyhanni.utils.CollectionUtils.addString import at.hannibal2.skyhanni.utils.ColorUtils import at.hannibal2.skyhanni.utils.KeyboardManager import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld import at.hannibal2.skyhanni.utils.LocationUtils +import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LocationUtils.playerLocation import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils @@ -30,37 +29,27 @@ import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine_nea import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled -import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings -import at.hannibal2.skyhanni.utils.SimpleTimeMark -import at.hannibal2.skyhanni.utils.renderables.Renderable -import at.hannibal2.skyhanni.utils.renderables.ScrollValue -import at.hannibal2.skyhanni.utils.renderables.Searchable -import at.hannibal2.skyhanni.utils.renderables.buildSearchableScrollable -import at.hannibal2.skyhanni.utils.renderables.toSearchable import kotlinx.coroutines.runBlocking import net.minecraft.client.settings.KeyBinding import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable import java.awt.Color -import kotlin.math.sqrt -import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.Duration.Companion.seconds @SkyHanniModule object GraphEditor { - private val config get() = SkyHanniMod.feature.dev.devTool.graph + val config get() = SkyHanniMod.feature.dev.devTool.graph - private fun isEnabled() = config != null && config.enabled + fun isEnabled() = config != null && config.enabled private var id = 0 - private val nodes = mutableListOf() + val nodes = mutableListOf() private val edges = mutableListOf() - private var activeNode: GraphingNode? = null + var activeNode: GraphingNode? = null set(value) { field = value selectedEdge = findEdgeBetweenActiveAndClosed() @@ -106,25 +95,25 @@ object GraphEditor { private val edgeDijkstraColor = LorenzColor.DARK_BLUE.addOpacity(150) private val edgeSelectedColor = LorenzColor.DARK_RED.addOpacity(150) - private val scrollValue = ScrollValue() - private val textInput = TextInput() - private var nodesDisplay = emptyList() - var lastUpdate = SimpleTimeMark.farPast() - @SubscribeEvent(priority = EventPriority.HIGHEST) fun onRender(event: LorenzRenderWorldEvent) { if (!isEnabled()) return nodes.forEach { event.drawNode(it) } edges.forEach { event.drawEdge(it) } - ghostPosition?.let { - event.drawWaypointFilled( - it, - if (activeNode == null) Color.RED else Color.GRAY, - seeThroughBlocks = seeThroughBlocks, - minimumAlpha = 0.2f, - inverseAlphaScale = true, - ) - } + drawGhostPosition(event) + } + + private fun drawGhostPosition(event: LorenzRenderWorldEvent) { + val ghostPosition = ghostPosition ?: return + if (ghostPosition.distanceToPlayer() >= config.maxNodeDistance) return + + event.drawWaypointFilled( + ghostPosition, + if (activeNode == null) Color.RED else Color.GRAY, + seeThroughBlocks = seeThroughBlocks, + minimumAlpha = 0.2f, + inverseAlphaScale = true, + ) } @SubscribeEvent @@ -188,136 +177,6 @@ object GraphEditor { dissolvePossible = edges.count { it.isInEdge(activeNode) } == 2 } - @SubscribeEvent - fun onGuiRender(event: GuiRenderEvent) { - if (!isEnabled()) return - - - config.namedNodesList.renderRenderables( - buildList { - val list = getNodeNames() - val size = list.size - addString("§eGraph Nodes: $size") - val height = (size * 10).coerceAtMost(250) - if (list.isNotEmpty()) { - add(list.buildSearchableScrollable(height, textInput, scrollValue, velocity = 10.0)) - } - }, - posLabel = "Graph Nodes List", - ) - } - - private fun getNodeNames(): List { - if (lastUpdate.passedSince() > 250.milliseconds) { - updateNodeNames() - } - return nodesDisplay - } - - private fun updateNodeNames() { - lastUpdate = SimpleTimeMark.now() - nodesDisplay = drawNodeNames() - } - - private fun updateTagView(node: GraphingNode) { - lastUpdate = SimpleTimeMark.now() + 60.seconds - nodesDisplay = drawTagNames(node) - } - - private fun drawTagNames(node: GraphingNode): List = buildList { - addSearchString("§eChange tag for node '${node.name}§e'") - addSearchString("") - - for (tag in GraphNodeTag.entries) { - val state = if (tag in node.tags) "§aYES" else "§cNO" - val name = state + " §r" + tag.displayName - add(createTagName(name, tag, node)) - } - addSearchString("") - add( - Renderable.clickAndHover( - "§cGo Back!", - tips = listOf("§eClick to go back to the node list!"), - onClick = { - updateNodeNames() - }, - ).toSearchable(), - ) - } - - private fun createTagName( - name: String, - tag: GraphNodeTag, - node: GraphingNode, - ) = Renderable.clickAndHover( - name, - tips = listOf( - "Tag ${tag.name}", - "§7${tag.description}", - "", - "§eClick to set tag for ${node.name} to ${tag.name}!", - ), - onClick = { - if (tag in node.tags) { - node.tags.remove(tag) - } else { - node.tags.add(tag) - } - updateTagView(node) - }, - ).toSearchable(name) - - private fun drawNodeNames(): List = buildList { - for ((node, distance: Double) in nodes.map { it to it.position.distanceSqToPlayer() }.sortedBy { it.second }) { - val name = node.name?.takeIf { !it.isBlank() } ?: continue - val color = if (node == activeNode) "§a" else "§7" - val distanceFormat = sqrt(distance).toInt().addSeparators() - val tagText = node.tags.let { - if (it.isEmpty()) { - " §cNo tag§r" - } else { - val text = node.tags.map { it.internalName }.joinToString(", ") - " §f($text)" - } - } - - val text = "${color}Node §r$name$tagText §7[$distanceFormat]" - add(createNodeTextLine(text, name, node)) - } - } - - private fun MutableList.createNodeTextLine( - text: String, - name: String, - node: GraphingNode, - ): Searchable = Renderable.clickAndHover( - text, - tips = buildList { - add("Node '$name'") - add("") - - if (node.tags.isNotEmpty()) { - add("Tags: ") - for (tag in node.tags) { - add(" §8- §r${tag.displayName}") - } - add("") - } - - add("§eClick to select/deselect this node!") - add("§eControl-Click to edit the tags for this node!") - - }, - onClick = { - if (KeyboardManager.isModifierKeyDown()) { - updateTagView(node) - } else { - activeNode = node - updateNodeNames() - } - }, - ).toSearchable(name) - private fun feedBackInTutorial(text: String) { if (inTutorialMode) { ChatUtils.chat(text) @@ -333,6 +192,7 @@ object GraphEditor { } private fun LorenzRenderWorldEvent.drawNode(node: GraphingNode) { + if (node.position.distanceToPlayer() > config.maxNodeDistance) return this.drawWaypointFilled( node.position, node.getNodeColor(), @@ -368,17 +228,20 @@ object GraphEditor { ) } - private fun LorenzRenderWorldEvent.drawEdge(edge: GraphingEdge) = this.draw3DLine_nea( - edge.node1.position.add(0.5, 0.5, 0.5), - edge.node2.position.add(0.5, 0.5, 0.5), - when { - selectedEdge == edge -> edgeSelectedColor - edge in highlightedEdges -> edgeDijkstraColor - else -> edgeColor - }, - 7, - !seeThroughBlocks, - ) + private fun LorenzRenderWorldEvent.drawEdge(edge: GraphingEdge) { + if (edge.node1.position.distanceToPlayer() > config.maxNodeDistance) return + this.draw3DLine_nea( + edge.node1.position.add(0.5, 0.5, 0.5), + edge.node2.position.add(0.5, 0.5, 0.5), + when { + selectedEdge == edge -> edgeSelectedColor + edge in highlightedEdges -> edgeDijkstraColor + else -> edgeColor + }, + 7, + !seeThroughBlocks, + ) + } private fun GraphingNode.getNodeColor() = when (this) { activeNode -> if (this == closedNode) ColorUtils.blendRGB(activeColor, closedColor, 0.5) else activeColor @@ -723,7 +586,7 @@ object GraphEditor { } // The node object the graph editor is working with -private class GraphingNode( +class GraphingNode( val id: Int, var position: LorenzVec, var name: String? = null, diff --git a/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt b/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt new file mode 100644 index 000000000000..f541b16e307c --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt @@ -0,0 +1,178 @@ +package at.hannibal2.skyhanni.test + +import at.hannibal2.skyhanni.data.model.GraphNodeTag +import at.hannibal2.skyhanni.data.model.TextInput +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.test.GraphEditor.distanceSqToPlayer +import at.hannibal2.skyhanni.utils.CollectionUtils.addString +import at.hannibal2.skyhanni.utils.KeyboardManager +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.renderables.ScrollValue +import at.hannibal2.skyhanni.utils.renderables.Searchable +import at.hannibal2.skyhanni.utils.renderables.buildSearchableScrollable +import at.hannibal2.skyhanni.utils.renderables.toSearchable +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.math.sqrt +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +@SkyHanniModule +object GraphNodeEditor { + + private val scrollValueNodes = ScrollValue() + private val scrollValueTags = ScrollValue() + private val textInput = TextInput() + private var nodesDisplay = emptyList() + private var lastUpdate = SimpleTimeMark.farPast() + + @SubscribeEvent + fun onGuiRender(event: GuiRenderEvent) { + if (!isEnabled()) return + + + config.namedNodesList.renderRenderables( + getNodeNames(), + posLabel = "Graph Nodes List", + ) + } + + private fun getNodeNames(): List { + if (lastUpdate.passedSince() > 250.milliseconds) { + updateNodeNames() + } + return nodesDisplay + } + + private fun updateNodeNames() { + lastUpdate = SimpleTimeMark.now() + nodesDisplay = buildList { + val list = drawNodeNames() + val size = list.size + addString("§eGraph Nodes: $size") + val height = (size * 10).coerceAtMost(250) + if (list.isNotEmpty()) { + add(list.buildSearchableScrollable(height, textInput, scrollValueNodes, velocity = 10.0)) + } + } + } + + private fun updateTagView(node: GraphingNode) { + lastUpdate = SimpleTimeMark.now() + 60.seconds + nodesDisplay = buildList { + val list = drawTagNames(node) + val size = list.size + addString("§eGraph Nodes: $size") + val height = (size * 10).coerceAtMost(250) + if (list.isNotEmpty()) { + add(Renderable.scrollList(list, height, scrollValueTags, velocity = 10.0)) + } + } + } + + private fun drawTagNames(node: GraphingNode): List = buildList { + addString("§eChange tag for node '${node.name}§e'") + addString("") + + for (tag in GraphNodeTag.entries.filter { it in node.tags || checkIsland(it) }) { + val state = if (tag in node.tags) "§aYES" else "§cNO" + val name = state + " §r" + tag.displayName + add(createTagName(name, tag, node)) + } + addString("") + add( + Renderable.clickAndHover( + "§cGo Back!", + tips = listOf("§eClick to go back to the node list!"), + onClick = { + updateNodeNames() + }, + ), + ) + } + + private fun checkIsland(tag: GraphNodeTag): Boolean = tag.onlyIsland?.let { + it == LorenzUtils.skyBlockIsland + } ?: true + + private fun createTagName( + name: String, + tag: GraphNodeTag, + node: GraphingNode, + ) = Renderable.clickAndHover( + name, + tips = listOf( + "Tag ${tag.name}", + "§7${tag.description}", + "", + "§eClick to set tag for ${node.name} to ${tag.name}!", + ), + onClick = { + if (tag in node.tags) { + node.tags.remove(tag) + } else { + node.tags.add(tag) + } + updateTagView(node) + }, + ) + + private fun drawNodeNames(): List = buildList { + for ((node, distance: Double) in GraphEditor.nodes.map { it to it.position.distanceSqToPlayer() }.sortedBy { it.second }) { + val name = node.name?.takeIf { !it.isBlank() } ?: continue + val color = if (node == GraphEditor.activeNode) "§a" else "§7" + val distanceFormat = sqrt(distance).toInt().addSeparators() + val tagText = node.tags.let { tags -> + if (tags.isEmpty()) { + " §cNo tag§r" + } else { + val text = node.tags.joinToString(", ") { it.internalName } + " §f($text)" + } + } + + val text = "${color}Node §r$name$tagText §7[$distanceFormat]" + add(createNodeTextLine(text, name, node)) + } + } + + private fun createNodeTextLine( + text: String, + name: String, + node: GraphingNode, + ): Searchable = Renderable.clickAndHover( + text, + tips = buildList { + add("Node '$name'") + add("") + + if (node.tags.isNotEmpty()) { + add("Tags: ") + for (tag in node.tags) { + add(" §8- §r${tag.displayName}") + } + add("") + } + + add("§eClick to select/deselect this node!") + add("§eControl-Click to edit the tags for this node!") + + }, + onClick = { + if (KeyboardManager.isModifierKeyDown()) { + updateTagView(node) + } else { + GraphEditor.activeNode = node + updateNodeNames() + } + }, + ).toSearchable(name) + + fun isEnabled() = GraphEditor.isEnabled() + private val config get() = GraphEditor.config + +} diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index 9e95df881225..5b7333189b1e 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -116,6 +116,7 @@ object SkyHanniDebugsAndTests { if (args.isEmpty()) { testLocation = null ChatUtils.chat("reset test waypoint") + IslandGraphs.stop() } val x = args[0].toDouble() From 864a36ff3d5b8ede1878fcbc73f2f1ceb1485150 Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:10:42 +0200 Subject: [PATCH 018/102] Fix: Mining Update ItemCategory (#2529) --- src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt | 6 +++--- .../features/misc/PrivateIslandNoPickaxeAbility.kt | 10 ++++------ .../java/at/hannibal2/skyhanni/utils/ItemCategory.kt | 8 ++++++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt index 987a864af77e..3fb643c7c8a3 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt @@ -75,7 +75,7 @@ object ReforgeAPI { fun isValid(itemCategory: ItemCategory?, internalName: NEUInternalName) = when (type) { ReforgeType.SWORD -> setOf( ItemCategory.SWORD, - ItemCategory.GAUNTLET, + ItemCategory.PICKAXE_AND_SWORD, ItemCategory.LONGSWORD, ItemCategory.FISHING_WEAPON, ).contains(itemCategory) @@ -98,7 +98,7 @@ object ReforgeAPI { ReforgeType.PICKAXE -> itemCategory == ItemCategory.PICKAXE || itemCategory == ItemCategory.DRILL || - itemCategory == ItemCategory.GAUNTLET + itemCategory == ItemCategory.PICKAXE_AND_SWORD ReforgeType.EQUIPMENT -> setOf( ItemCategory.CLOAK, @@ -111,7 +111,7 @@ object ReforgeAPI { ReforgeType.ROD -> itemCategory == ItemCategory.FISHING_ROD || itemCategory == ItemCategory.FISHING_WEAPON ReforgeType.SWORD_AND_ROD -> setOf( ItemCategory.SWORD, - ItemCategory.GAUNTLET, + ItemCategory.PICKAXE_AND_SWORD, ItemCategory.LONGSWORD, ItemCategory.FISHING_ROD, ItemCategory.FISHING_WEAPON, diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/PrivateIslandNoPickaxeAbility.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/PrivateIslandNoPickaxeAbility.kt index a29bcecc5ba7..dc55af349d14 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/PrivateIslandNoPickaxeAbility.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/PrivateIslandNoPickaxeAbility.kt @@ -21,12 +21,10 @@ object PrivateIslandNoPickaxeAbility { if (!config.privateIslandNoPickaxeAbility) return if (event.clickType != ClickType.RIGHT_CLICK) return - when (event.itemInHand?.getItemCategoryOrNull()) { - ItemCategory.GAUNTLET, ItemCategory.PICKAXE, ItemCategory.DRILL -> { - event.cancel() - } - - else -> {} + if (event.itemInHand?.getItemCategoryOrNull()?.let { + ItemCategory.miningTools.contains(it) + } == true) { + event.cancel() } } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt index 8fa41978e70c..08272ed0898b 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt @@ -12,7 +12,7 @@ enum class ItemCategory { FISHING_WEAPON, FISHING_ROD, AXE, - GAUNTLET, + PICKAXE_AND_SWORD, HOE, PICKAXE, SHOVEL, @@ -53,6 +53,10 @@ enum class ItemCategory { SACK, CHISEL, DYE, + ORE, + BLOCK, + DWARVEN_METAL, + GEMSTONE, NONE, ; @@ -62,7 +66,7 @@ enum class ItemCategory { fun Collection.containsItem(stack: ItemStack?) = stack?.getItemCategoryOrNull()?.let { this.contains(it) } ?: false - val miningTools = listOf(PICKAXE, DRILL, GAUNTLET) + val miningTools = listOf(PICKAXE, DRILL, PICKAXE_AND_SWORD) val armor = setOf(HELMET, CHESTPLATE, LEGGINGS, BOOTS) } From a7f0634426586c13500e5f8012e245d669053226 Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:22:23 +0200 Subject: [PATCH 019/102] Fix: TunnelMaps area and missing stats for reforge helper (#2530) --- src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt | 3 ++- .../java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt index 6fd65778ecfb..994ff524e869 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt @@ -44,7 +44,8 @@ enum class SkyblockStat(val icon: String) { FORAGING_FORTUNE("§☘"), FARMING_FORTUNE("§6☘"), MINING_FORTUNE("§6☘"), - FEAR("§a☠") + FEAR("§a☠"), + HEAT_RESISTANCE("§c♨"), ; val capitalizedName = name.lowercase().allLettersFirstUppercase() diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt index 8d7f771b7223..847a3673f5ad 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt @@ -524,7 +524,7 @@ object TunnelsMaps { goal = getNext() } - private val areas = setOf("Glacite Tunnels", "Dwarven Base Camp", "Glacite Lake", "Fossil Research Center") + private val areas = setOf("Glacite Tunnels", "Dwarven Base Camp", "Great Glacite Lake", "Fossil Research Center") private fun isEnabled() = IslandType.DWARVEN_MINES.isInIsland() && config.enable && LorenzUtils.skyBlockArea in areas } From 50cbf42bbf7d05a50e6a2ec10f5936667f0ef6f1 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 18 Sep 2024 09:27:05 +0200 Subject: [PATCH 020/102] Fix: Update Ghost Bestiary Tiers (#2533) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/config/ConfigUpdaterMigrator.kt | 2 +- .../BestiaryFormattingConfig.java | 2 +- .../combat/ghostcounter/GhostCounter.kt | 49 ++++++++++--------- .../features/combat/ghostcounter/GhostData.kt | 32 ++++++------ .../combat/ghostcounter/GhostFormatting.kt | 2 +- .../features/combat/ghostcounter/GhostUtil.kt | 2 +- 6 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt index c881cc578ba4..8eb47d3949b2 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt @@ -12,7 +12,7 @@ import com.google.gson.JsonPrimitive object ConfigUpdaterMigrator { val logger = LorenzLogger("ConfigMigration") - const val CONFIG_VERSION = 57 + const val CONFIG_VERSION = 58 fun JsonElement.at(chain: List, init: Boolean): JsonElement? { if (chain.isEmpty()) return this if (this !is JsonObject) return null diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/combat/ghostcounter/textformatting/BestiaryFormattingConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/combat/ghostcounter/textformatting/BestiaryFormattingConfig.java index 46a02e9b21e3..ac9f6e0b210a 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/combat/ghostcounter/textformatting/BestiaryFormattingConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/combat/ghostcounter/textformatting/BestiaryFormattingConfig.java @@ -30,7 +30,7 @@ public class BestiaryFormattingConfig { @ConfigOption(name = "Progress to Max", desc = "Text to show progress when the §eMaxed Bestiary §7option is §aON\n" + "§e%currentKill% §7is replaced with your current total kill.") @ConfigEditorText - public String showMax_progress = "%currentKill%/250k (%percentNumber%%)"; + public String showMax_progress = "%currentKill%/100k (%percentNumber%%)"; @Expose @ConfigOption(name = "Progress", desc = "Text to show progress when the §eMaxed Bestiary §7option is §cOFF\n" + diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt index 3bc3c85d4b2a..664acbc976f6 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt @@ -55,6 +55,7 @@ import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import com.google.gson.JsonPrimitive import io.github.moulberry.notenoughupdates.util.Utils import io.github.moulberry.notenoughupdates.util.XPInformation import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -76,27 +77,27 @@ object GhostCounter { private val patternGroup = RepoPattern.group("combat.ghostcounter") private val skillXPPattern by patternGroup.pattern( "skillxp", - "[+](?[0-9,.]+) \\((?[0-9,.]+)(?:/(?[0-9,.]+))?\\)" + "[+](?[0-9,.]+) \\((?[0-9,.]+)(?:/(?[0-9,.]+))?\\)", ) private val combatSectionPattern by patternGroup.pattern( "combatsection", - ".*[+](?[0-9,.]+) (?[A-Za-z]+) \\((?(?[0-9.,]+)/(?[0-9.,]+)|(?[0-9.]+)%)\\).*" + ".*[+](?[0-9,.]+) (?[A-Za-z]+) \\((?(?[0-9.,]+)/(?[0-9.,]+)|(?[0-9.]+)%)\\).*", ) private val killComboExpiredPattern by patternGroup.pattern( "killcomboexpired", - "§cYour Kill Combo has expired! You reached a (?.*) Kill Combo!" + "§cYour Kill Combo has expired! You reached a (?.*) Kill Combo!", ) private val ghostXPPattern by patternGroup.pattern( "ghostxp", - "(?\\d+(?:\\.\\d+)?(?:,\\d+)?[kK]?)/(?\\d+(?:\\.\\d+)?(?:,\\d+)?[kKmM]?)" + "(?\\d+(?:\\.\\d+)?(?:,\\d+)?[kK]?)/(?\\d+(?:\\.\\d+)?(?:,\\d+)?[kKmM]?)", ) private val bestiaryPattern by patternGroup.pattern( "bestiary", - ".*(?:§\\d|§\\w)+BESTIARY (?:§\\d|§\\w)+Ghost (?:§\\d|§\\w)(?\\d+)➜(?:§\\d|§\\w)(?\\d+).*" + ".*(?:§\\d|§\\w)+BESTIARY (?:§\\d|§\\w)+Ghost (?:§\\d|§\\w)(?\\d+)➜(?:§\\d|§\\w)(?\\d+).*", ) private val skillLevelPattern by patternGroup.pattern( "skilllevel", - ".*§e§lSkills: §r§a(?.*) (?\\d+).*" + ".*§e§lSkills: §r§a(?.*) (?\\d+).*", ) private val format = NumberFormat.getInstance() @@ -127,7 +128,7 @@ object GhostCounter { config.position.renderStringsAndItems( display, extraSpace = config.extraSpace, - posLabel = "Ghost Counter" + posLabel = "Ghost Counter", ) } @@ -205,7 +206,7 @@ object GhostCounter { val etaFormatting = textFormatting.etaFormatting val remaining: Int = when (config.showMax) { - true -> 250_000 - bestiaryCurrentKill + true -> 100_000 - bestiaryCurrentKill false -> killNeeded - currentKill } @@ -222,7 +223,7 @@ object GhostCounter { "%days%" to "days", "%hours%" to "hours", "%minutes%" to "minutes", - "%seconds%" to "seconds" + "%seconds%" to "seconds", ) for ((format, key) in formatMap) { if (etaFormatting.time.contains(format)) { @@ -255,8 +256,7 @@ object GhostCounter { addAsSingletonList(textFormatting.highestKillComboFormat.formatText(Option.MAXKILLCOMBO)) addAsSingletonList(textFormatting.skillXPGainFormat.formatText(Option.SKILLXPGAINED)) addAsSingletonList( - bestiaryFormatting.base.preFormat(bestiary, nextLevel - 1, nextLevel) - .formatBestiary(currentKill, killNeeded) + bestiaryFormatting.base.preFormat(bestiary, nextLevel - 1, nextLevel).formatBestiary(currentKill, killNeeded), ) addAsSingletonList(xpHourFormatting.base.formatText(xp)) @@ -275,7 +275,7 @@ object GhostCounter { Triple("Volta", Option.VOLTACOUNT.getInt(), voltaValue), Triple("Bag Of Cash", Option.BAGOFCASH.getInt(), 1_000_000), Triple("Scavenger Coins", Option.SCAVENGERCOINS.getInt(), 1), - Triple("Ghostly Boots", Option.GHOSTLYBOOTS.getInt(), 77_777) + Triple("Ghostly Boots", Option.GHOSTLYBOOTS.getInt(), 77_777), ) val moneyMadeTips = buildList { for ((name, count, value) in priceMap) { @@ -288,7 +288,7 @@ object GhostCounter { val moneyMadeWithClickableTips = Renderable.clickAndHover( textFormatting.moneyMadeFormat.formatText(moneyMade.addSeparators()), moneyMadeTips, - onClick = { OSUtils.copyToClipboard(moneyMadeTips.joinToString("\n").removeColor()) } + onClick = { OSUtils.copyToClipboard(moneyMadeTips.joinToString("\n").removeColor()) }, ) addAsSingletonList(textFormatting.moneyHourFormat.formatText(final)) addAsSingletonList(moneyMadeWithClickableTips) @@ -327,7 +327,7 @@ object GhostCounter { }, "§eClick to import data!", prefixColor = "§6", - oneTimeClick = true + oneTimeClick = true, ) } } @@ -365,9 +365,8 @@ object GhostCounter { var parse = true if (skillPercent) { percent = nf.parse(group("percent")).toFloat() - val level = - if (currentSkill == "Combat" && currentSkillLevel != -1) currentSkillLevel else XPInformation.getInstance() - .getSkillInfo(skillName)?.level ?: 0 + val level = if (currentSkill == "Combat" && currentSkillLevel != -1) currentSkillLevel else XPInformation.getInstance() + .getSkillInfo(skillName)?.level ?: 0 if (level > 0) { totalSkillXp = SkillExperience.getExpForNextLevel(level) currentSkillXp = totalSkillXp * percent / 100 @@ -419,11 +418,10 @@ object GhostCounter { Option.SORROWCOUNT, Option.VOLTACOUNT, Option.PLASMACOUNT, Option.GHOSTLYBOOTS -> { opt.add(1.0) opt.add(1.0, true) - storage?.totalMF = storage?.totalMF?.plus(group("mf").substring(4).toDouble()) - ?: group("mf").substring(4).toDouble() + storage?.totalMF = + storage?.totalMF?.plus(group("mf").substring(4).toDouble()) ?: group("mf").substring(4).toDouble() Option.TOTALDROPS.add(1.0) - if (opt == Option.SORROWCOUNT) - Option.GHOSTSINCESORROW.set(0.0) + if (opt == Option.SORROWCOUNT) Option.GHOSTSINCESORROW.set(0.0) update() } @@ -459,7 +457,7 @@ object GhostCounter { when (val nextLevel = if (currentLevel >= 25) 26 else currentLevel + 1) { 26 -> { storage?.bestiaryNextLevel = 26.0 - storage?.bestiaryCurrentKill = 250_000.0 + storage?.bestiaryCurrentKill = 100_000.0 storage?.bestiaryKillNeeded = 0.0 } @@ -491,8 +489,7 @@ object GhostCounter { val stacks = event.inventoryItems val ghostStack = stacks.values.find { it.displayName.contains("Ghost") } ?: return val bestiaryNextLevel = - if ("§\\wGhost".toRegex().matches(ghostStack.displayName)) 1 else ghostStack.displayName.substring(8) - .romanToDecimal() + 1 + if ("§\\wGhost".toRegex().matches(ghostStack.displayName)) 1 else ghostStack.displayName.substring(8).romanToDecimal() + 1 storage?.bestiaryNextLevel = bestiaryNextLevel.toDouble() var kills = 0.0 for (line in ghostStack.getLore()) { @@ -523,6 +520,10 @@ object GhostCounter { event.transform(11, "combat.ghostCounter.ghostDisplayText") { element -> ConfigUtils.migrateIntArrayListToEnumArrayList(element, GhostDisplayEntry::class.java) } + + event.transform(58, "combat.ghostCounter.textFormatting.bestiaryFormatting.showMax_progress") { + JsonPrimitive("%currentKill%/100k (%percentNumber%%)") + } } fun isEnabled() = IslandType.DWARVEN_MINES.isInIsland() && config.enabled diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostData.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostData.kt index abcf2dbba722..fde0f51dac1c 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostData.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostData.kt @@ -18,27 +18,23 @@ object GhostData { Option.SKILLXPGAINED to 0.0 ) + // TODO repo val bestiaryData = mutableMapOf().apply { for (i in 1..25) { this[i] = when (i) { - 1 -> 5 - 2 -> 5 - 3 -> 5 - 4 -> 10 - 5 -> 25 - 6 -> 50 - 7 -> 100 - 8 -> 150 - 9 -> 150 - 10 -> 250 - 11 -> 750 - 12 -> 1_500 - 13 -> 2_000 - 14, 15, 16, 17 -> 2_500 - 18 -> 3_000 - 19, 20 -> 3_500 - 21 -> 25_000 - 22, 23, 24, 25 -> 50_000 + 1, 2, 3, 4, 5 -> 4 + 6 -> 20 + 7 -> 40 + 8, 9 -> 60 + 10 -> 100 + 11 -> 300 + 12 -> 600 + 13 -> 800 + 14, 15, 16, 17 -> 1_000 + 18 -> 1_200 + 19, 20 -> 1_400 + 21 -> 10_000 + 22, 23, 24, 25 -> 20_000 else -> 0 } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostFormatting.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostFormatting.kt index 183753e51c63..b58201899bc4 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostFormatting.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostFormatting.kt @@ -160,7 +160,7 @@ object GhostFormatting { base = " &6Bestiary %currentLevel%->%nextLevel%: &b%value%" openMenu = "§cOpen Bestiary Menu !" maxed = "%currentKill% (&c&lMaxed!)" - showMax_progress = "%currentKill%/250k (%percentNumber%%)" + showMax_progress = "%currentKill%/100k (%percentNumber%%)" progress = "%currentKill%/%killNeeded%" } with(killHourFormatting) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt index baacbfa43ed8..6cf777b48cea 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt @@ -141,5 +141,5 @@ object GhostUtil { } private fun percent(number: Double) = - 100.0.coerceAtMost(((number / 250_000) * 100).roundToPrecision(4)).toString() + 100.0.coerceAtMost(((number / 100_000) * 100).roundToPrecision(4)).toString() } From 92ec20864b6653f86bb1a149c9d280e0ae03e62b Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:29:21 +0200 Subject: [PATCH 021/102] better description --- .../at/hannibal2/skyhanni/config/features/gui/GUIConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java index 74ab755c9940..37f3d3c42037 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java @@ -36,7 +36,7 @@ public class GUIConfig { @Accordion public ChromaConfig chroma = new ChromaConfig(); - @ConfigOption(name = "Edit GUI Locations", desc = "Change the position of SkyHanni's overlays.") + @ConfigOption(name = "Edit GUI Locations", desc = "Opens the Position Editor, allows changing the position of SkyHanni's overlays.") @ConfigEditorButton(buttonText = "Edit") public Runnable positions = () -> GuiEditManager.openGuiPositionEditor(true); From 532e748e8e65d8435c5bcc7939f950a0e05a3de7 Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Wed, 18 Sep 2024 03:30:32 -0400 Subject: [PATCH 022/102] Fix: Garden Color in Messages (#2532) --- .../at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt | 7 ++++++- .../features/garden/visitor/GardenVisitorFeatures.kt | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt index 3ae865ac82a1..8ad8d21dab1a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenPlotAPI.kt @@ -72,9 +72,14 @@ object GardenPlotAPI { "spray.target", "§a§lSPRAYONATOR! §r§7You sprayed §r§aPlot §r§7- §r§b(?.*) §r§7with §r§a(?.*)§r§7!" ) + + /** + * REGEX-TEST: §9§lSPLASH! §r§6Your §r§aGarden §r§6was cleared of all active §r§aSprayonator §r§6effects! + * REGEX-TEST: §9§lSPLASH! §r§6Your §r§bGarden §r§6was cleared of all active §r§aSprayonator §r§6effects! + */ private val portableWasherPattern by patternGroup.pattern( "spray.cleared.portablewasher", - "§9§lSPLASH! §r§6Your §r§bGarden §r§6was cleared of all active §r§aSprayonator §r§6effects!" + "§9§lSPLASH! §r§6Your §r§[ba]Garden §r§6was cleared of all active §r§aSprayonator §r§6effects!" ) var plots = listOf() diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt index fef28450f719..aed4f7e7cffc 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt @@ -80,9 +80,14 @@ object GardenVisitorFeatures { private var display = emptyList>() private val patternGroup = RepoPattern.group("garden.visitor") + + /** + * REGEX-TEST: §a§r§aBanker Broadjaw §r§ehas arrived on your §r§aGarden§r§e! + * REGEX-TEST: §a§r§aBanker Broadjaw §r§ehas arrived on your §r§bGarden§r§e! + */ private val visitorArrivePattern by patternGroup.pattern( "visitorarrive", - ".* §r§ehas arrived on your §r§bGarden§r§e!", + ".* §r§ehas arrived on your §r§[ba]Garden§r§e!", ) private val copperPattern by patternGroup.pattern( "copper", From ff1d69d6238b6898dfe4f7c9986661a0737a2100 Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:31:04 +0200 Subject: [PATCH 023/102] Fix: Custom Scoreboard Errors (#2531) --- .../gui/customscoreboard/ScoreboardPattern.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt index 1c6fb4282fec..0324da855ae3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt @@ -274,8 +274,13 @@ object ScoreboardPattern { "uselessgoblin", "^§7Kill goblins!$", ) + + /** + * REGEX-TEST: Remaining: §a1 goblin + * REGEX-TEST: Remaining: §a2 goblins + */ val remainingGoblinPattern by miningSb.pattern( - "remaininggoblin", "^Remaining: §a\\d+ goblins$", + "remaininggoblin", "^Remaining: §a\\d+ goblins?$", ) val yourGoblinKillsPattern by miningSb.pattern( "yourgoblin", "^Your kills: §c\\d+ ☠( §a\\(\\+\\d+\\))?$", @@ -303,9 +308,13 @@ object ScoreboardPattern { "magmaboss", "^§7Boss: §[c6e]\\d{1,3}%$", ) + + /** + * REGEX-TEST: §7Damage Soaked:§629,446 §e(+271 + */ val damageSoakedPattern by combatSb.pattern( "damagesoaked", - "^§7Damage Soaked:", + "^§7Damage Soaked:.*", ) val killMagmasPattern by combatSb.pattern( "killmagmas", From c614724dd0a92d89fa96c3468be156a407f924db Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:50:08 +0200 Subject: [PATCH 024/102] stop initializing the map on every function call Co-authored-by: Empa <42304516+itsempa@users.noreply.github.com> --- .../commands/tabcomplete/PlayerTabComplete.kt | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt index bf44282114e2..070ab478e083 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt @@ -15,6 +15,24 @@ object PlayerTabComplete { private val config get() = SkyHanniMod.feature.misc.commands.tabComplete private var vipVisits = listOf() + private val ignoredCommandCategories = mapOf( + "f" to listOf(PlayerCategory.FRIENDS), + "friend" to listOf(PlayerCategory.FRIENDS), + + "msg" to listOf(), + "w" to listOf(), + "tell" to listOf(), + "boop" to listOf(), + + "visit" to listOf(), + "invite" to listOf(), + "ah" to listOf(), + + "pv" to listOf(), // NEU's Profile Viewer + "shmarkplayer" to listOf(), // SkyHanni's Mark Player + + "trade" to listOf(PlayerCategory.FRIENDS, PlayerCategory.PARTY), + ) @SubscribeEvent fun onRepoReload(event: RepositoryReloadEvent) { @@ -34,41 +52,21 @@ object PlayerTabComplete { } fun handleTabComplete(command: String): List? { - val commands = mapOf( - "f" to listOf(PlayerCategory.FRIENDS), - "friend" to listOf(PlayerCategory.FRIENDS), - - "msg" to listOf(), - "w" to listOf(), - "tell" to listOf(), - "boop" to listOf(), - - "visit" to listOf(), - "invite" to listOf(), - "ah" to listOf(), - - "pv" to listOf(), // NEU's Profile Viewer - "shmarkplayer" to listOf(), // SkyHanni's Mark Player - - "trade" to listOf(PlayerCategory.FRIENDS, PlayerCategory.PARTY) - ) - val ignored = commands[command] ?: return null + val ignoredCategories = ignoredCommandCategories[command] ?: return null return buildList { - if (config.friends && PlayerCategory.FRIENDS !in ignored) { - FriendAPI.getAllFriends() - .filter { it.bestFriend || !config.onlyBestFriends } - .forEach { add(it.name) } + if (config.friends && PlayerCategory.FRIENDS !in ignoredCategories) { + FriendAPI.getAllFriends().filter { it.bestFriend || !config.onlyBestFriends }.forEach { add(it.name) } } - if (config.islandPlayers && PlayerCategory.ISLAND_PLAYERS !in ignored) { + if (config.islandPlayers && PlayerCategory.ISLAND_PLAYERS !in ignoredCategories) { for (entity in EntityUtils.getPlayerEntities()) { add(entity.name) } } - if (config.party && PlayerCategory.PARTY !in ignored) { + if (config.party && PlayerCategory.PARTY !in ignoredCategories) { for (member in PartyAPI.partyMembers) { add(member) } From 780435a4a8594ed3c172fbb4c5bf240a70199042 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Wed, 18 Sep 2024 10:35:33 +0200 Subject: [PATCH 025/102] Fix: Tungsten Detection on Alpha (#2437) Co-authored-by: Empa <42304516+itsempa@users.noreply.github.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/features/mining/OreBlock.kt | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt index c5d9774b15bf..d9b76a35be94 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt @@ -10,6 +10,7 @@ import at.hannibal2.skyhanni.data.MiningAPI.inSpidersDen import at.hannibal2.skyhanni.utils.CollectionUtils.equalsOneOf import net.minecraft.block.BlockColored import net.minecraft.block.BlockSand +import net.minecraft.block.BlockSilverfish import net.minecraft.block.BlockStainedGlass import net.minecraft.block.BlockStainedGlassPane import net.minecraft.block.BlockStone @@ -222,7 +223,7 @@ enum class OreBlock( checkArea = { inGlacite }, ), LOW_TIER_TUNGSTEN( - checkBlock = { it.block == Blocks.cobblestone }, + checkBlock = ::isLowTierTungsten, checkArea = { inGlacite }, ), HIGH_TIER_TUNGSTEN( @@ -240,12 +241,10 @@ enum class OreBlock( } } -private fun isLowTierMithril(state: IBlockState): Boolean { - return when (state.block) { - Blocks.wool -> state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY - Blocks.stained_hardened_clay -> state.getValue(BlockColored.COLOR) == EnumDyeColor.CYAN - else -> false - } +private fun isLowTierMithril(state: IBlockState): Boolean = when (state.block) { + Blocks.wool -> state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY + Blocks.stained_hardened_clay -> state.getValue(BlockColored.COLOR) == EnumDyeColor.CYAN + else -> false } private fun isHighTierMithril(state: IBlockState): Boolean { @@ -270,18 +269,21 @@ private fun isHardStoneHollows(state: IBlockState): Boolean { } private fun isHardstoneGlacite(state: IBlockState): Boolean = - (state.block == Blocks.stone && state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.STONE) || + (state.block == Blocks.monster_egg && state.getValue(BlockSilverfish.VARIANT) == BlockSilverfish.EnumType.STONE) || (state.block == Blocks.wool && state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY) private fun isRedSand(state: IBlockState): Boolean = (state.block == Blocks.sand && state.getValue(BlockSand.VARIANT) == BlockSand.EnumType.RED_SAND) -private fun isLowTierUmber(state: IBlockState): Boolean = state.block == Blocks.hardened_clay || - (state.block == Blocks.stained_hardened_clay && state.getValue(BlockColored.COLOR) == EnumDyeColor.BROWN) +private fun isLowTierUmber(state: IBlockState): Boolean = + state.block == Blocks.hardened_clay || (state.block == Blocks.stained_hardened_clay && state.getValue(BlockColored.COLOR) == EnumDyeColor.BROWN) private fun isHighTierUmber(state: IBlockState): Boolean = (state.block == Blocks.double_stone_slab2 && state.getValue(BlockStoneSlabNew.VARIANT) == BlockStoneSlabNew.EnumType.RED_SANDSTONE) +private fun isLowTierTungsten(state: IBlockState): Boolean = + state.block == Blocks.monster_egg && state.getValue(BlockSilverfish.VARIANT) == BlockSilverfish.EnumType.COBBLESTONE + private fun IBlockState.isGemstoneWithColor(color: EnumDyeColor): Boolean { return when (this.block) { Blocks.stained_glass -> color == this.getValue(BlockStainedGlass.COLOR) From 7b4a810019ad5aab0b689f9294cbde3eaeccb188 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Wed, 18 Sep 2024 11:22:04 +0200 Subject: [PATCH 026/102] Version 0.27 Beta 9 --- docs/CHANGELOG.md | 46 +++++++++++++++++++ docs/FEATURES.md | 5 ++ root.gradle.kts | 2 +- .../java/at/hannibal2/skyhanni/SkyHanniMod.kt | 2 +- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f42ddfffc477..a0795097cb22 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -46,6 +46,11 @@ + Use /shhoppitystats for live stats. + Added optional warning when Hoppity calls you with a rabbit to sell. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272) + Added hotkey for picking up Abiphone calls from Hoppity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272) ++ Added a draggable list for Hoppity's Collection menu rabbit highlighting. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) + + Factory/Shop milestones = Yellow/Gold. + + Stray rabbits = Dark Aqua. + + Abi = Dark Green. ++ Added a toggle to highlight found rabbits in Hoppity's Collection menu. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) #### The Carnival Features @@ -151,6 +156,7 @@ + Added per-election season Diana Trackers. - !nea (https://github.com/hannibal002/SkyHanni/pull/2487) + This includes the Diana Profit Tracker and the Mythological Creature Tracker. + The data is stored moving forward, but there is currently no way to view anything other than the current year. ++ Changed the Diana Profit Tracker and Mythological Creature Tracker to always be visible when holding the Ancestral Spade in hand, even if Diana is not the mayor. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2496) #### Scoreboard Improvements @@ -213,11 +219,16 @@ #### Rift Improvements + Updated the description of the config for Enigma Soul Waypoints to help find the Rift Guide in the game. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2433) ++ Added Path Finder to Enigma Soul waypoints in the Rift. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2515) #### Dungeon Improvements + Added dungeon stars and crimson stars to estimated item value. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2492) +#### The Carnival Improvements + ++ Updated the Zombie Shootout Diamond color to be a deeper blue. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2511) + #### Misc Improvements + Updated Last Server to also alert for servers you have just left. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2494) @@ -262,6 +273,11 @@ + Fixed Powder for 10 Levels overflowing over the max level. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2422) + Fixed Glacite Tunnel Maps repeatedly showing the last commission goal even when there is no new commission. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2432) + Fixed Mineshaft Pity Counter not working with some ore types. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2436) ++ Fixed Tungsten Ore and Hardstone detection after the mining update. - Empa (https://github.com/hannibal002/SkyHanni/pull/2437) ++ Fixed item category errors after the new mining update. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2529) ++ Fixed blocks mined by Efficient Miner not being detected. - Empa (https://github.com/hannibal002/SkyHanni/pull/2526) ++ Fixed Corpse Tracker resetting items between restarts. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2528) ++ Fixed the Corpse Tracker not displaying while in the Mineshaft. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2516) #### Scoreboard Fixes @@ -280,6 +296,9 @@ + Fixed Custom Scoreboard errors while doing Carnival activities. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2455) + Fixed Custom Scoreboard error while in a Dungeon Queue. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2441) + Fixed Custom Scoreboard Error during the Barry Protestors Quest. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2480) ++ Fixed a Custom Scoreboard error during the Magma Boss Fight and Goblin Raid. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2531) ++ Fixed a Custom Scoreboard error while in the Dojo. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2519) ++ Fixed a Custom Scoreboard error during M7 Dragons. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2510) #### Hoppity Fixes @@ -312,6 +331,18 @@ #### Garden Fixes + Fixed outdated Dicer gemstone fortune in FF guide. - Not_a_cow (https://github.com/hannibal002/SkyHanni/pull/2499) ++ Fixed some garden messages not being blocked. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2532) ++ Fixed active sprays not being removed from the GUI when using a washer. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2532) + +#### Combat Fixes + ++ Updated Ghost Counter bestiary tiers to reflect Hypixel reducing the max kills needed from 250K to 100K. - Luna (https://github.com/hannibal002/SkyHanni/pull/2533) ++ Fixed a case where the Broodmother countdown could be wildly inaccurate. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2513) + +#### The Carnival Fixes + ++ Fixed two "Catch a Fish" carnival goals not being properly detected from chat. - Luna (https://github.com/hannibal002/SkyHanni/pull/2517) ++ Fixed the bounding box of baby zombies in Zombie Shootout. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2512) #### Misc Fixes @@ -327,6 +358,8 @@ + Fixed performance issues with a high number of dungeon runs in one session. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2504) + Fixed `/shedittracker duplex` not working. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2501) + Fixed "Last Server Joined" spamming the chat. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2495) ++ Fixed lag spikes in Path Finder. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2525) ++ Fixed old mayor perks not being marked as inactive when the mayor changes. - Luna (https://github.com/hannibal002/SkyHanni/pull/2524) ### Technical Details @@ -366,6 +399,19 @@ + Updated HandleEvent onlyOnIslands to handle multiple islands. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2502) + Added an abstract "bucketed" tracker that can store where items came from. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2306) + Takes an enum in instantiation and has built-in logic for swapping display types. ++ Graph Editor improvements. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2515) + + Code cleanup in Graph Editor, moving node lists into their own class. + + Fixed searchable tag list. + + Added more graph node tags; only display certain tags on specific islands in the editor. + + Added an option to limit the maximum render range of nodes and edges in the Graph Editor for better performance. ++ Hoppity's Collection highlighting now updates on event triggers instead of every frame. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) + +### Removed Features + ++ Removed Wild Strawberry Dye notification. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2520) + + Artist's Abode now sends a lobby-wide message for dye drops, making the notification obsolete. ++ Removed `Only Requirement Not Met` and `Highlight Requirement Rabbits` config options. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) + + Replaced then with a draggable list of different Hoppity collection highlight options. ## Version 0.26 diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 9c67c0de1246..cbf3abacf756 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -989,6 +989,11 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game. + Use /shhoppitystats for live stats. + Added optional warning when Hoppity calls you with a rabbit to sell. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272) + Added hotkey for picking up Abiphone calls from Hoppity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272) ++ Added a draggable list for Hoppity's Collection menu rabbit highlighting. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) + + Factory/Shop milestones = Yellow/Gold. + + Stray rabbits = Dark Aqua. + + Abi = Dark Green. ++ Added a toggle to highlight found rabbits in Hoppity's Collection menu. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) ### The Carnival diff --git a/root.gradle.kts b/root.gradle.kts index ebdebe043c9b..f799ed79d6a1 100644 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -12,7 +12,7 @@ plugins { allprojects { group = "at.hannibal2.skyhanni" - version = "0.27.Beta.8" + version = "0.27.Beta.9" } preprocess { diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 09a293829e25..cab1b690ffb5 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -42,7 +42,7 @@ import org.apache.logging.log4j.Logger clientSideOnly = true, useMetadata = true, guiFactory = "at.hannibal2.skyhanni.config.ConfigGuiForgeInterop", - version = "0.27.Beta.8", + version = "0.27.Beta.9", ) class SkyHanniMod { From 17f900c1d54ba6007de7e6cf4f62ded5ce0655e3 Mon Sep 17 00:00:00 2001 From: Luna Date: Wed, 18 Sep 2024 16:14:49 +0200 Subject: [PATCH 027/102] Fix: Revert Gauntlet item category change (#2534) --- src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt | 6 +++--- src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt index 3fb643c7c8a3..987a864af77e 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt @@ -75,7 +75,7 @@ object ReforgeAPI { fun isValid(itemCategory: ItemCategory?, internalName: NEUInternalName) = when (type) { ReforgeType.SWORD -> setOf( ItemCategory.SWORD, - ItemCategory.PICKAXE_AND_SWORD, + ItemCategory.GAUNTLET, ItemCategory.LONGSWORD, ItemCategory.FISHING_WEAPON, ).contains(itemCategory) @@ -98,7 +98,7 @@ object ReforgeAPI { ReforgeType.PICKAXE -> itemCategory == ItemCategory.PICKAXE || itemCategory == ItemCategory.DRILL || - itemCategory == ItemCategory.PICKAXE_AND_SWORD + itemCategory == ItemCategory.GAUNTLET ReforgeType.EQUIPMENT -> setOf( ItemCategory.CLOAK, @@ -111,7 +111,7 @@ object ReforgeAPI { ReforgeType.ROD -> itemCategory == ItemCategory.FISHING_ROD || itemCategory == ItemCategory.FISHING_WEAPON ReforgeType.SWORD_AND_ROD -> setOf( ItemCategory.SWORD, - ItemCategory.PICKAXE_AND_SWORD, + ItemCategory.GAUNTLET, ItemCategory.LONGSWORD, ItemCategory.FISHING_ROD, ItemCategory.FISHING_WEAPON, diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt index 08272ed0898b..43961f61d31d 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemCategory.kt @@ -12,7 +12,7 @@ enum class ItemCategory { FISHING_WEAPON, FISHING_ROD, AXE, - PICKAXE_AND_SWORD, + GAUNTLET, HOE, PICKAXE, SHOVEL, @@ -66,7 +66,7 @@ enum class ItemCategory { fun Collection.containsItem(stack: ItemStack?) = stack?.getItemCategoryOrNull()?.let { this.contains(it) } ?: false - val miningTools = listOf(PICKAXE, DRILL, PICKAXE_AND_SWORD) + val miningTools = listOf(PICKAXE, DRILL, GAUNTLET) val armor = setOf(HELMET, CHESTPLATE, LEGGINGS, BOOTS) } From ae23866e8cd6264f6c55e6891fcfb9e748f91152 Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:40:10 +0200 Subject: [PATCH 028/102] fix (#2545) Signed-off-by: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> --- .../features/gui/customscoreboard/ScoreboardPattern.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt index 0324da855ae3..fb014cc29243 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardPattern.kt @@ -246,13 +246,17 @@ object ScoreboardPattern { "raffleuseless", "^(Find tickets on the|ground and bring them|to the raffle box)$", ) + + /** + * REGEX-TEST: Tickets: §a8 §7(17.4%) + */ val raffleTicketsPattern by miningSb.pattern( "raffletickets", - "^Tickets: §a\\d+ §7\\(\\d{1,3}\\.\\d%\\)$", + "Tickets: §a\\d+ §7\\(\\d+(\\.\\d)?%\\)", ) val rafflePoolPattern by miningSb.pattern( "rafflepool", - "^Pool: §6\\d+§8/500$", + "Pool: §6\\d+", ) val mithrilUselessPattern by miningSb.pattern( "mithriluseless", From a900bd4a3df60664c5de758a76644871cba8abbd Mon Sep 17 00:00:00 2001 From: jani270 <69345714+jani270@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:46:35 +0200 Subject: [PATCH 029/102] Fix: Corpse Tracker not being modifed by /shdefaultoptions (#2549) --- .../skyhanni/config/features/mining/CorpseTrackerConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/CorpseTrackerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/CorpseTrackerConfig.java index 61d3b19594a7..229883c83352 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/CorpseTrackerConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/CorpseTrackerConfig.java @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni.config.features.mining; +import at.hannibal2.skyhanni.config.FeatureToggle; import at.hannibal2.skyhanni.config.core.config.Position; import com.google.gson.annotations.Expose; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; @@ -11,6 +12,7 @@ public class CorpseTrackerConfig { @Expose @ConfigOption(name = "Enabled", desc = "Enable the Corpse Tracker overlay for Glacite Mineshafts.") @ConfigEditorBoolean + @FeatureToggle public boolean enabled = false; @Expose From 94c2cac32296cc3c003bab78ea425e20539d218a Mon Sep 17 00:00:00 2001 From: Miestiek <58041219+Miestiek@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:00:18 +0200 Subject: [PATCH 030/102] Improvment: Click on Garden Shopping List now uses /viewrecipe instead of /recipe (#2505) Co-authored-by: MysticKillerBear <58041219+MysticKillerBear@users.noreply.github.com> --- .../skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt index e10d1be36da4..6ecaa5491496 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt @@ -109,7 +109,7 @@ object GardenVisitorSupercraft { if (event.slotId != 31) return event.cancel() if (lastClick.passedSince() > 0.3.seconds) { - HypixelCommands.recipe(lastSuperCraftMaterial) + HypixelCommands.viewRecipe(lastSuperCraftMaterial) lastClick = SimpleTimeMark.now() } } From 20a0aeb8cc9e795adf54cafbf381ab5c9dfe2595 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 20 Sep 2024 12:52:14 +0200 Subject: [PATCH 031/102] Fix: Visitor Supercraft use internal name (#2552) --- .../skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt index 6ecaa5491496..f441d17ab6a1 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt @@ -84,7 +84,7 @@ object GardenVisitorSupercraft { hasIngredients = true for ((key, value) in ingredientReqs) { val sackItem = key.asInternalName().getAmountInSacks() - lastSuperCraftMaterial = internalName.itemName.removeColor() + lastSuperCraftMaterial = internalName.asString() if (sackItem < value * amount) { hasIngredients = false break From 23d779a47fdd114cad12552603b931fc2a31c318 Mon Sep 17 00:00:00 2001 From: ThatGravyBoat Date: Fri, 20 Sep 2024 17:06:51 -0230 Subject: [PATCH 032/102] Fix: github source checking (#2548) --- .../tweaker/DownloadSourceChecker.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java b/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java index 5fb46592eab4..5672ee87cd7e 100644 --- a/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java +++ b/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java @@ -1,7 +1,5 @@ package at.hannibal2.skyhanni.tweaker; -import at.hannibal2.skyhanni.SkyHanniMod; - import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; @@ -16,7 +14,8 @@ public class DownloadSourceChecker { - private static final String GITHUB_REPO_TEXT = "repo_id=511310721"; + private static final String GITHUB_REPO = "511310721"; + private static final String GITHUB_REPO_TEXT = "repo_id=" + GITHUB_REPO; private static final String MODRINTH_URL = "/data/byNkmv5G/"; private static final String THE_PASSWORD = "danger"; @@ -96,11 +95,12 @@ private static void openMenu(URI host) { } )); - String version = SkyHanniMod.Companion.getVersion(); + // TODO FIX THIS CAUSING CLASS LOADING AND CRASHING + //String version = SkyHanniMod.Companion.getVersion(); JOptionPane.showOptionDialog( frame, String.format(String.join("\n", SECURITY_POPUP), uriToSimpleString(host)), - "SkyHanni " + version + " Security Error", + "SkyHanni Security Error", JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, @@ -124,8 +124,12 @@ private static URI getDangerousHost() { URI host = getHost(file); if (host == null) return null; - if (host.getHost().equals("objects.githubusercontent.com") && host.getQuery().contains(GITHUB_REPO_TEXT)) { - return null; + if (host.getHost().equals("objects.githubusercontent.com")) { + if (host.getQuery().contains(GITHUB_REPO_TEXT)) { + return null; + } else if (host.getPath().contains("/" + GITHUB_REPO + "/")) { + return null; + } } else if (host.getHost().equals("cdn.modrinth.com") && host.getPath().startsWith(MODRINTH_URL)) { return null; } From a267da2c57fdc765a540540505db8c9c6ed66231 Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:44:00 -0400 Subject: [PATCH 033/102] Fixes & Improvements: Hoppity Rework (#2488) --- .../skyhanni/events/hoppity/EggFoundEvent.kt | 10 + .../features/event/hoppity/HoppityAPI.kt | 141 ++++++----- .../event/hoppity/HoppityEggLocator.kt | 11 +- .../features/event/hoppity/HoppityEggType.kt | 4 +- .../event/hoppity/HoppityEggsCompactChat.kt | 138 ++++++----- .../event/hoppity/HoppityEggsManager.kt | 91 +++---- .../ChocolateFactoryBarnManager.kt | 2 +- .../ChocolateFactoryStrayTracker.kt | 230 +++++++++--------- 8 files changed, 334 insertions(+), 293 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/events/hoppity/EggFoundEvent.kt diff --git a/src/main/java/at/hannibal2/skyhanni/events/hoppity/EggFoundEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/hoppity/EggFoundEvent.kt new file mode 100644 index 000000000000..2cd81811f272 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/hoppity/EggFoundEvent.kt @@ -0,0 +1,10 @@ +package at.hannibal2.skyhanni.events.hoppity + +import at.hannibal2.skyhanni.api.event.SkyHanniEvent +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType + +class EggFoundEvent( + val type: HoppityEggType, + val slotIndex: Int?, + val note: String?, +) : SkyHanniEvent() diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt index 5c63fe3a800a..6c42f8a17002 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt @@ -3,50 +3,55 @@ package at.hannibal2.skyhanni.features.event.hoppity import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.GuiContainerEvent import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.SecondPassedEvent +import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.eggFoundPattern import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.SIDE_DISH +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_SHOP_MILESTONE +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_FACTORY_MILESTONE import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI +import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.itemName import at.hannibal2.skyhanni.utils.LorenzRarity import at.hannibal2.skyhanni.utils.LorenzRarity.DIVINE +import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getMinecraftId import at.hannibal2.skyhanni.utils.SkyblockSeason -import net.minecraft.util.ChatComponentText import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @SkyHanniModule object HoppityAPI { - private var hoppityEggChat = mutableListOf() + private var messageCount = 0 private var duplicate = false private var lastRarity = "" private var lastName = "" private var newRabbit = false - private var lastChatMeal: HoppityEggType? = null + private var lastMeal: HoppityEggType? = null private var lastDuplicateAmount: Long? = null - private var rabbitBought = false val hoppityRarities by lazy { LorenzRarity.entries.filter { it <= DIVINE } } - private fun resetChatData() { - this.hoppityEggChat = mutableListOf() + private fun resetRabbitData() { + this.messageCount = 0 this.duplicate = false this.newRabbit = false this.lastRarity = "" this.lastName = "" - this.lastChatMeal = null + this.lastMeal = null this.lastDuplicateAmount = null - this.rabbitBought = false } fun isHoppityEvent() = (SkyblockSeason.currentSeason == SkyblockSeason.SPRING || SkyHanniMod.feature.dev.debug.alwaysHoppitys) - fun rarityByRabbit(rabbit: String): LorenzRarity? = hoppityRarities.firstOrNull { it.chatColorCode == rabbit.substring(0, 2) } /** @@ -59,6 +64,14 @@ object HoppityAPI { "(?:§.)*?(?\\d{1,2})[a-z]{2} Chocolate Milestone", ) + /** + * REGEX-TEST: §6§lGolden Rabbit §8- §aSide Dish + */ + private val sideDishNamePattern by ChocolateFactoryAPI.patternGroup.pattern( + "rabbit.sidedish", + "(?:§.)*?Golden Rabbit (?:§.)?- (?:§.)?Side Dish" + ) + /** * REGEX-TEST: §7Reach §6300B Chocolate §7all-time to * REGEX-TEST: §7Reach §61k Chocolate §7all-time to unlock @@ -77,99 +90,99 @@ object HoppityAPI { "§7Spend §6(?[\\d.MBk]*) Chocolate §7in.*", ) - fun fireSideDishMessage() { - LorenzChatEvent( - "§d§lHOPPITY'S HUNT §r§dYou found a §r§6§lSide Dish §r§6Egg §r§din the Chocolate Factory§r§d!", - ChatComponentText(""), - ).postAndCatch() + @SubscribeEvent(priority = EventPriority.HIGH) + fun onTick(event: SecondPassedEvent) { + if (!ChocolateFactoryAPI.inChocolateFactory) return + InventoryUtils.getItemsInOpenChest().filter { + it.stack.hasDisplayName() && + it.stack.getMinecraftId().toString() == "minecraft:skull" && + it.stack.getLore().isNotEmpty() + }.forEach { + ChocolateFactoryStrayTracker.strayCaughtPattern.matchMatcher(it.stack.displayName) { + ChocolateFactoryStrayTracker.handleStrayClicked(it) + when(groupOrNull("name") ?: return@matchMatcher) { + "Fish the Rabbit" -> EggFoundEvent(HoppityEggType.STRAY, it.slotNumber, null).post() + "El Dorado" -> EggFoundEvent(HoppityEggType.STRAY, it.slotNumber, null).post() + else -> return@matchMatcher + } + } + } } @SubscribeEvent(priority = EventPriority.HIGH) fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { - val index = event.slot?.slotIndex ?: return - if (index == -999) return + val index = event.slot?.slotIndex?.takeIf { it != -999 } ?: return val clickedStack = InventoryUtils.getItemsInOpenChest() .find { it.slotNumber == event.slot.slotNumber && it.hasStack } ?.stack ?: return val nameText = (if (clickedStack.hasDisplayName()) clickedStack.displayName else clickedStack.itemName) + sideDishNamePattern.matchMatcher(nameText) { + EggFoundEvent(SIDE_DISH, index, null).post() + lastMeal = SIDE_DISH + attemptFireRabbitFound() + } milestoneNamePattern.matchMatcher(nameText) { - val itemLore = clickedStack.getLore() - if (!itemLore.any { it == "§eClick to claim!" }) return - - // Will never match both all time and shop patterns together - allTimeLorePattern.firstMatcher(clickedStack.getLore()) { - LorenzChatEvent( - "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lChocolate Milestone Rabbit §r§din the Chocolate Factory§r§d!", - ChatComponentText(""), - ).postAndCatch() - } - - shopLorePattern.firstMatcher(clickedStack.getLore()) { - LorenzChatEvent( - "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lShop Milestone Rabbit §r§din the Chocolate Factory§r§d!", - ChatComponentText(""), - ).postAndCatch() + clickedStack.getLore().let { + if (!it.any { line -> line == "§eClick to claim!" }) return + allTimeLorePattern.firstMatcher(it) { + EggFoundEvent(CHOCOLATE_FACTORY_MILESTONE, index, null).post() + lastMeal = CHOCOLATE_FACTORY_MILESTONE + attemptFireRabbitFound() + } + shopLorePattern.firstMatcher(it) { + EggFoundEvent(CHOCOLATE_SHOP_MILESTONE, index, null).post() + lastMeal = CHOCOLATE_SHOP_MILESTONE + attemptFireRabbitFound() + } } } } - // Dumbed down version of the Compact Chat for Hoppity's, - // with the additional native context of side dishes - fun handleChat(event: LorenzChatEvent) { - HoppityEggsManager.eggFoundPatterns.forEach { - it.matchMatcher(event.message) { - resetChatData() - lastChatMeal = getEggType(event) - attemptFire(event) - } + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!LorenzUtils.inSkyBlock) return + eggFoundPattern.matchMatcher(event.message) { + resetRabbitData() + lastMeal = getEggType(event) + lastMeal?.let { EggFoundEvent(it, null, groupOrNull("note")).post() } + attemptFireRabbitFound() } HoppityEggsManager.eggBoughtPattern.matchMatcher(event.message) { if (group("rabbitname").equals(lastName)) { - rabbitBought = true - lastChatMeal = HoppityEggType.BOUGHT - attemptFire(event) + lastMeal = HoppityEggType.BOUGHT + EggFoundEvent(HoppityEggType.BOUGHT, null, null).post() + attemptFireRabbitFound() } } HoppityEggsManager.rabbitFoundPattern.matchMatcher(event.message) { - // The only cases where "You found ..." will come in with more than 1 message, - // or empty for hoppityEggChat, is where the rabbit was purchased from hoppity, - // In the case of buying, we want to reset variables to a clean state during this capture, - // as the important capture for the purchased message is the final message in - // the chain; "You found [rabbit]" -> "Dupe/New Rabbit" -> "You bought [rabbit]" - if ((hoppityEggChat.isEmpty() || hoppityEggChat.size > 1)) { - resetChatData() - } - lastName = group("name") lastRarity = group("rarity") - attemptFire(event) + attemptFireRabbitFound() } HoppityEggsManager.newRabbitFound.matchMatcher(event.message) { newRabbit = true groupOrNull("other")?.let { - attemptFire(event) + attemptFireRabbitFound() return } - attemptFire(event) + attemptFireRabbitFound() } } - fun attemptFire(event: LorenzChatEvent, lastDuplicateAmount: Long? = null) { + fun attemptFireRabbitFound(lastDuplicateAmount: Long? = null) { lastDuplicateAmount?.let { this.lastDuplicateAmount = it - } - hoppityEggChat.add(event.message) - if (lastDuplicateAmount != null) { this.duplicate = true } - val lastChatMeal = lastChatMeal ?: return - if (hoppityEggChat.size == 3) { - RabbitFoundEvent(lastChatMeal, duplicate, lastName, lastDuplicateAmount ?: 0).post() - } + messageCount++ + val lastChatMeal = lastMeal ?: return + if (messageCount != 3) return + RabbitFoundEvent(lastChatMeal, duplicate, lastName, lastDuplicateAmount ?: 0).post() + resetRabbitData() } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt index 0d5bfefa8878..8e9e68e16731 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni.features.event.hoppity +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.data.ClickType import at.hannibal2.skyhanni.data.IslandGraphs import at.hannibal2.skyhanni.events.DebugDataCollectEvent @@ -8,6 +9,7 @@ import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.ReceiveParticleEvent +import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.features.fame.ReminderUtils import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule @@ -59,6 +61,11 @@ object HoppityEggLocator { var currentEggType: HoppityEggType? = null var currentEggNote: String? = null + @HandleEvent + fun onEggFound(event: EggFoundEvent) { + if (event.type.isResetting) resetData() + } + @SubscribeEvent fun onWorldChange(event: LorenzWorldChangeEvent) { resetData() @@ -168,10 +175,6 @@ object HoppityEggLocator { private fun shouldShowAllEggs() = config.showAllWaypoints && !locatorInHotbar && HoppityEggType.eggsRemaining() - fun eggFound() { - resetData() - } - @SubscribeEvent fun onReceiveParticle(event: ReceiveParticleEvent) { if (!isEnabled()) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt index 23e00cf88cdb..5371816e1319 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt @@ -17,7 +17,8 @@ enum class HoppityEggType( SIDE_DISH("Side Dish", "§6§l", -1), BOUGHT("Bought", "§a", -1), CHOCOLATE_SHOP_MILESTONE("Shop Milestone", "§6", -1), - CHOCOLATE_FACTORY_MILESTONE("Chocolate Milestone", "§6", -1) + CHOCOLATE_FACTORY_MILESTONE("Chocolate Milestone", "§6", -1), + STRAY("Stray Rabbit", "§a", -1) ; fun timeUntil(): Duration { @@ -39,6 +40,7 @@ enum class HoppityEggType( } fun isClaimed() = claimed + val isResetting get() = resettingEntries.contains(this) val formattedName get() = "${if (isClaimed()) "§7§m" else mealColor}$mealName:$mealColor" val coloredName get() = "$mealColor$mealName" diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt index 6bc1c8dfc276..abbdff1f023b 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt @@ -1,25 +1,34 @@ package at.hannibal2.skyhanni.features.event.hoppity +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEggsConfig import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.BOUGHT import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_FACTORY_MILESTONE import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_SHOP_MILESTONE import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.SIDE_DISH -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.eggFoundPatterns +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.STRAY +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.eggFoundPattern import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.DelayedRun +import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.fromNow import at.hannibal2.skyhanni.utils.TimeUtils.format +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds typealias RarityType = HoppityEggsConfig.CompactRarityTypes +@SkyHanniModule object HoppityEggsCompactChat { private var hoppityEggChat = mutableListOf() @@ -30,31 +39,32 @@ object HoppityEggsCompactChat { private var newRabbit = false private var lastChatMeal: HoppityEggType? = null private var lastDuplicateAmount: Long? = null - private var rabbitBought = false private val config get() = ChocolateFactoryAPI.config + private val eventConfig get() = SkyHanniMod.feature.event.hoppityEggs - fun compactChat(event: LorenzChatEvent, lastDuplicateAmount: Long? = null) { + fun compactChat(event: LorenzChatEvent? = null, lastDuplicateAmount: Long? = null) { + if (!HoppityEggsManager.config.compactChat) return lastDuplicateAmount?.let { this.lastDuplicateAmount = it - } - if (!HoppityEggsManager.config.compactChat) return - event.blockedReason = "compact_hoppity" - hoppityEggChat.add(event.message) - if (lastDuplicateAmount != null) { this.duplicate = true } - if (hoppityEggChat.size == 3) { - DelayedRun.runDelayed(200.milliseconds) { - sendCompact() - } + event?.let { + it.blockedReason = "compact_hoppity" + hoppityEggChat.add(it.message) } + if (hoppityEggChat.size == 3) sendCompact() } private fun sendCompact() { - if (hoppityEggChat.isNotEmpty()) { + if (lastChatMeal.let { HoppityEggType.resettingEntries.contains(it) } && eventConfig.sharedWaypoints) { + DelayedRun.runDelayed(5.milliseconds) { + clickableCompact(HoppityEggsManager.getAndDisposeWaypointOnclick()) + resetCompactData() + } + } else { ChatUtils.hoverableChat(createCompactMessage(), hover = hoppityEggChat, prefix = false) + resetCompactData() } - resetCompactData() } private fun resetCompactData() { @@ -66,15 +76,16 @@ object HoppityEggsCompactChat { this.lastProfit = "" this.lastChatMeal = null this.lastDuplicateAmount = null - this.rabbitBought = false } private fun createCompactMessage(): String { - val mealName = lastChatMeal?.coloredName ?: "" - val mealNameFormatted = if (rabbitBought) "§aBought Rabbit" - else if (lastChatMeal == SIDE_DISH) "§6§lSide Dish §r§6Egg" - else if (lastChatMeal == CHOCOLATE_SHOP_MILESTONE || lastChatMeal == CHOCOLATE_FACTORY_MILESTONE) "§6§lMilestone Rabbit" - else "$mealName Egg" + val mealNameFormat = when (lastChatMeal) { + BOUGHT -> "§aBought Rabbit" + SIDE_DISH -> "§6§lSide Dish §r§6Egg" + CHOCOLATE_SHOP_MILESTONE, CHOCOLATE_FACTORY_MILESTONE -> "§6§lMilestone Rabbit" + STRAY -> "§aStray Rabbit" + else -> "${lastChatMeal?.coloredName ?: ""} Egg" + } val rarityConfig = HoppityEggsManager.config.rarityInCompact return if (duplicate) { @@ -85,39 +96,72 @@ object HoppityEggsCompactChat { val showDupeRarity = rarityConfig.let { it == RarityType.BOTH || it == RarityType.DUPE } val timeStr = if (config.showDuplicateTime) ", §a+§b$timeFormatted§7" else "" - "$mealNameFormatted! §7Duplicate ${if (showDupeRarity) "$lastRarity " else ""}$lastName §7(§6+$format Chocolate§7$timeStr)" + "$mealNameFormat! §7Duplicate ${if (showDupeRarity) "$lastRarity " else ""}$lastName §7(§6+$format Chocolate§7$timeStr)" } else if (newRabbit) { val showNewRarity = rarityConfig.let { it == RarityType.BOTH || it == RarityType.NEW } - "$mealNameFormatted! §d§lNEW ${if (showNewRarity) "$lastRarity " else ""}$lastName §7(${lastProfit}§7)" + "$mealNameFormat! §d§lNEW ${if (showNewRarity) "$lastRarity " else ""}$lastName §7(${lastProfit}§7)" } else "?" } - fun handleChat(event: LorenzChatEvent) { - eggFoundPatterns.forEach { - it.matchMatcher(event.message) { - resetCompactData() - lastChatMeal = getEggType(event) - compactChat(event) - } + private fun clickableCompact(onClick: () -> Unit) { + val hover = hoppityEggChat.joinToString("\n") + " \n§eClick here to share the location of this chocolate egg with the server!" + hoppityEggChat.clear() + ChatUtils.clickableChat( + createCompactMessage(), + hover = hover, + onClick = onClick, + expireAt = 30.seconds.fromNow(), + oneTimeClick = true, + prefix = false, + ) + } + + @HandleEvent + fun onEggFound(event: EggFoundEvent) { + if (!HoppityEggsManager.config.compactChat || HoppityEggType.resettingEntries.contains(event.type) || event.type == BOUGHT) return + lastChatMeal = event.type + hoppityEggChat.add( + when (event.type) { + SIDE_DISH -> + "§d§lHOPPITY'S HUNT §r§dYou found a §r§6§lSide Dish §r§6Egg §r§din the Chocolate Factory§r§d!" + + CHOCOLATE_FACTORY_MILESTONE -> + "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lChocolate Milestone Rabbit §r§din the Chocolate Factory§r§d!" + + CHOCOLATE_SHOP_MILESTONE -> + "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lShop Milestone Rabbit §r§din the Chocolate Factory§r§d!" + + STRAY -> { + "§d§lHOPPITY'S HUNT §r§dYou found a §r§aStray Rabbit§r§d!".also { + // If it was an El Dorado dupe stray, we don't want hanging data + DelayedRun.runDelayed(300.milliseconds) { resetCompactData() } + } + } + + else -> + "§d§lHOPPITY'S HUNT §r§7Unknown Egg Type?" + }, + ) + if (hoppityEggChat.size == 3) sendCompact() + } + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!LorenzUtils.inSkyBlock) return + eggFoundPattern.matchMatcher(event.message) { + resetCompactData() + lastChatMeal = getEggType(event) + compactChat(event) } HoppityEggsManager.eggBoughtPattern.matchMatcher(event.message) { if (group("rabbitname").equals(lastName)) { - rabbitBought = true + lastChatMeal = BOUGHT compactChat(event) } } HoppityEggsManager.rabbitFoundPattern.matchMatcher(event.message) { - // The only cases where "You found ..." will come in with more than 1 message, - // or empty for hoppityEggChat, is where the rabbit was purchased from hoppity, - // In the case of buying, we want to reset variables to a clean state during this capture, - // as the important capture for the purchased message is the final message in - // the chain; "You found [rabbit]" -> "Dupe/New Rabbit" -> "You bought [rabbit]" - if ((hoppityEggChat.isEmpty() || hoppityEggChat.size > 1)) { - resetCompactData() - } - lastName = group("name") lastRarity = group("rarity") compactChat(event) @@ -138,20 +182,4 @@ object HoppityEggsCompactChat { compactChat(event) } } - - fun clickableCompact(onClick: () -> Unit): Boolean = if (hoppityEggChat.isNotEmpty() && !rabbitBought && lastChatMeal != null && - HoppityEggType.resettingEntries.contains(lastChatMeal) - ) { - val hover = hoppityEggChat.joinToString("\n") + " \n§eClick here to share the location of this chocolate egg with the server!" - hoppityEggChat.clear() - ChatUtils.clickableChat( - createCompactMessage(), - hover = hover, - onClick = onClick, - expireAt = 30.seconds.fromNow(), - oneTimeClick = true, - prefix = false, - ) - true - } else false } diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt index bec966128dd3..75b65fdc48ac 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt @@ -1,10 +1,13 @@ package at.hannibal2.skyhanni.features.event.hoppity import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.SecondPassedEvent +import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent +import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent import at.hannibal2.skyhanni.features.fame.ReminderUtils import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule @@ -21,11 +24,9 @@ import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.fromNow import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.now import at.hannibal2.skyhanni.utils.SkyBlockTime import at.hannibal2.skyhanni.utils.SoundUtils -import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.TimeUtils.format import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.util.regex.Matcher -import java.util.regex.Pattern import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @@ -38,36 +39,12 @@ object HoppityEggsManager { * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§9Chocolate Lunch Egg §r§don a ledge next to the stairs up§r§d! * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§aChocolate Dinner Egg §r§dbehind Emissary Sisko§r§d! * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§9Chocolate Lunch Egg §r§dnear the Diamond Essence Shop§r§d! - * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§6§lSide Dish §r§6Egg §r§din the Chocolate Factory§r§d! */ - private val eggFoundPattern by ChocolateFactoryAPI.patternGroup.pattern( + val eggFoundPattern by ChocolateFactoryAPI.patternGroup.pattern( "egg.found", "§d§lHOPPITY'S HUNT §r§dYou found a §r§.Chocolate (?\\w+) Egg §r§d(?.*)§r§d!", ) - /** - * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§6§lSide Dish §r§6Egg §r§din the Chocolate Factory§r§d! - */ - private val sideDishEggFoundPattern by ChocolateFactoryAPI.patternGroup.pattern( - "sidedish.found", - "§d§lHOPPITY'S HUNT §r§dYou found a §r§6§l(?.*) §r§6Egg §r§din the Chocolate Factory§r§d!", - ) - - /** - * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lShop Milestone Rabbit §r§din the Chocolate Factory§r§d! - * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lChocolate Milestone Rabbit §r§din the Chocolate Factory§r§d! - */ - private val milestoneRabbitFoundPattern by ChocolateFactoryAPI.patternGroup.pattern( - "milestone.claimed", - "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§l(?[\\w ]+) Rabbit §r§din the Chocolate Factory§r§d!", - ) - - val eggFoundPatterns: List = listOf( - eggFoundPattern, - sideDishEggFoundPattern, - milestoneRabbitFoundPattern, - ) - /** * REGEX-TEST: §aYou bought §r§9Casanova §r§afor §r§6970,000 Coins§r§a! * REGEX-TEST: §aYou bought §r§fHeidie §r§afor §r§6194,000 Coins§r§a! @@ -126,12 +103,28 @@ object HoppityEggsManager { private var warningActive = false private var lastWarnTime = SimpleTimeMark.farPast() + private var latestWaypointOnclick: () -> Unit = {} + @SubscribeEvent fun onWorldChange(event: LorenzWorldChangeEvent) { lastMeal = null lastNote = null } + @HandleEvent + fun onEggFound(event: EggFoundEvent) { + if (!HoppityAPI.isHoppityEvent() || !event.type.isResetting) return + HoppityEggLocations.saveNearestEgg() + event.type.markClaimed() + lastMeal = event.type + lastNote = event.note + } + + @HandleEvent + fun onRabbitFound(event: RabbitFoundEvent) { + HoppityCollectionStats.incrementRabbit(event.rabbitName) + } + @SubscribeEvent fun onChat(event: LorenzChatEvent) { if (!LorenzUtils.inSkyBlock) return @@ -147,22 +140,8 @@ object HoppityEggsManager { return } - HoppityEggsCompactChat.handleChat(event) - HoppityAPI.handleChat(event) - if (!HoppityAPI.isHoppityEvent()) return - eggFoundPattern.matchMatcher(event.message) { - HoppityEggLocations.saveNearestEgg() - HoppityEggLocator.eggFound() - val meal = getEggType(event) - val note = group("note").removeColor() - meal.markClaimed() - lastMeal = meal - lastNote = note - return - } - noEggsLeftPattern.matchMatcher(event.message) { HoppityEggType.allFound() @@ -188,10 +167,6 @@ object HoppityEggsManager { getEggType(event).markSpawned() return } - - rabbitFoundPattern.matchMatcher(event.message) { - HoppityCollectionStats.incrementRabbit(group("name")) - } } internal fun Matcher.getEggType(event: LorenzChatEvent): HoppityEggType = @@ -202,6 +177,12 @@ object HoppityEggsManager { ) } + fun getAndDisposeWaypointOnclick(): () -> Unit { + val onClick = latestWaypointOnclick + latestWaypointOnclick = {} + return onClick + } + fun shareWaypointPrompt() { if (!config.sharedWaypoints) return val meal = lastMeal ?: return @@ -211,16 +192,16 @@ object HoppityEggsManager { val currentLocation = LocationUtils.playerLocation() DelayedRun.runNextTick { - val onClick = { HoppityEggsShared.shareNearbyEggLocation(currentLocation, meal, note) } - if (!HoppityEggsCompactChat.clickableCompact(onClick)) { - ChatUtils.clickableChat( - "Click here to share the location of this chocolate egg with the server!", - onClick = onClick, - "§eClick to share!", - expireAt = 30.seconds.fromNow(), - oneTimeClick = true, - ) - } + latestWaypointOnclick = { HoppityEggsShared.shareNearbyEggLocation(currentLocation, meal, note) } + if (config.compactChat) return@runNextTick + ChatUtils.clickableChat( + "Click here to share the location of this chocolate egg with the server!", + onClick = latestWaypointOnclick, + "§eClick to share!", + expireAt = 30.seconds.fromNow(), + oneTimeClick = true, + ) + latestWaypointOnclick = {} } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt index bd1fc87174e1..ccb6712c0538 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt @@ -57,7 +57,7 @@ object ChocolateFactoryBarnManager { } ChocolateAmount.addToAll(amount) HoppityEggsCompactChat.compactChat(event, lastDuplicateAmount = amount) - HoppityAPI.attemptFire(event, lastDuplicateAmount = amount) + HoppityAPI.attemptFireRabbitFound(lastDuplicateAmount = amount) } rabbitCrashedPattern.matchMatcher(event.message) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt index 5c964c639122..805d2c8617b2 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryStrayTracker.kt @@ -1,24 +1,29 @@ package at.hannibal2.skyhanni.features.inventory.chocolatefactory -import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent import at.hannibal2.skyhanni.events.SecondPassedEvent +import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.features.event.hoppity.HoppityAPI +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType import at.hannibal2.skyhanni.features.event.hoppity.HoppityEventSummary import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut +import at.hannibal2.skyhanni.utils.CollectionUtils.sortedDesc import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore -import at.hannibal2.skyhanni.utils.ItemUtils.itemName import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzRarity +import at.hannibal2.skyhanni.utils.LorenzRarity.LEGENDARY import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NumberUtil.formatLong import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.StringUtils import at.hannibal2.skyhanni.utils.StringUtils.removeResets import at.hannibal2.skyhanni.utils.TimeUtils.format import at.hannibal2.skyhanni.utils.renderables.Renderable @@ -26,8 +31,10 @@ import at.hannibal2.skyhanni.utils.renderables.Searchable import at.hannibal2.skyhanni.utils.renderables.toSearchable import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker import at.hannibal2.skyhanni.utils.tracker.TrackerData +import com.google.gson.JsonElement +import com.google.gson.JsonObject import com.google.gson.annotations.Expose -import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraft.inventory.Slot import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -43,14 +50,14 @@ object ChocolateFactoryStrayTracker { * REGEX-TEST: §6§lGolden Rabbit §d§lCAUGHT! * REGEX-TEST: §fAudi §d§lCAUGHT! */ - private val strayCaughtPattern by ChocolateFactoryAPI.patternGroup.pattern( + val strayCaughtPattern by ChocolateFactoryAPI.patternGroup.pattern( "stray.caught", - "^§[a-f0-9].* §d§lCAUGHT!", + "^(?:§.)*(?.*) §d§lCAUGHT!", ) /** * REGEX-TEST: §7You caught a stray §fMandy §7and §7gained §6+283,574 Chocolate§7! - * REGEX-TEST: §7You caught a stray §aSven §7and gained §7§6+397,004 Chocolate§7!' + * REGEX-TEST: §7You caught a stray §aSven §7and gained §7§6+397,004 Chocolate§7! */ private val strayLorePattern by ChocolateFactoryAPI.patternGroup.pattern( "stray.loreinfo", @@ -101,14 +108,6 @@ object ChocolateFactoryStrayTracker { "§7You caught a stray (?§.)Fish the Rabbit§7! §7You have already found (?:§.)?Fish the (?:§.)?Rabbit§7, so you received §6(?[\\d,]*) (?:§6)?Chocolate§7!", ) - private val rarityFormatMap = mapOf( - "common" to "§f", - "uncommon" to "§a", - "rare" to "§9", - "epic" to "§5", - "legendary" to "§6", - ) - private val tracker = SkyHanniTracker("Stray Tracker", { Data() }, { it.chocolateFactory.strayTracker }) { drawDisplay(it) } @@ -120,10 +119,10 @@ object ChocolateFactoryStrayTracker { } @Expose - var straysCaught: MutableMap = mutableMapOf() + var straysCaught: MutableMap = mutableMapOf() @Expose - var straysExtraChocMs: MutableMap = mutableMapOf() + var straysExtraChocMs: MutableMap = mutableMapOf() @Expose var goldenTypesCaught: MutableMap = mutableMapOf() @@ -134,15 +133,12 @@ object ChocolateFactoryStrayTracker { return notEmptyLines.joinToString(" ") } - private fun incrementRarity(rarity: String, chocAmount: Long = 0) { + private fun incrementRarity(rarity: LorenzRarity, chocAmount: Long = 0) { tracker.modify { it.straysCaught.addOrPut(rarity, 1) } val extraTime = ChocolateFactoryAPI.timeUntilNeed(chocAmount + 1) tracker.modify { it.straysExtraChocMs.addOrPut(rarity, extraTime.inWholeMilliseconds) } - if (HoppityAPI.isHoppityEvent()) { - LorenzRarity.getByName(rarity)?.let { - HoppityEventSummary.addStrayCaught(it, chocAmount) - } - } + if (!HoppityAPI.isHoppityEvent()) return + HoppityEventSummary.addStrayCaught(rarity, chocAmount) } private fun incrementGoldenType(typeCaught: String, amount: Int = 1) { @@ -159,107 +155,99 @@ object ChocolateFactoryStrayTracker { tips = listOf("§a+§b${formattedExtraTime} §afrom strays§7"), ).toSearchable(), ) - rarityFormatMap.keys.forEach { rarity -> + HoppityAPI.hoppityRarities.forEach { rarity -> extractHoverableOfRarity(rarity, data)?.let { add(it) } } } - private fun extractHoverableOfRarity(rarity: String, data: Data): Searchable? { + private fun extractHoverableOfRarity(rarity: LorenzRarity, data: Data): Searchable? { val caughtOfRarity = data.straysCaught[rarity] val caughtString = caughtOfRarity?.toString() ?: return null val rarityExtraChocMs = data.straysExtraChocMs[rarity]?.milliseconds val extraChocFormat = rarityExtraChocMs?.format() ?: "" - val colorCode = rarityFormatMap[rarity] ?: "" - val lineHeader = "$colorCode${rarity.substring(0, 1).uppercase()}${rarity.substring(1)}§7: §r$colorCode" + val colorCode = rarity.chatColorCode + val lineHeader = "$colorCode${rarity.toString().lowercase().replaceFirstChar { it.uppercase() }}§7: §r$colorCode" val lineFormat = "${lineHeader}${caughtString}" val renderable = rarityExtraChocMs?.let { - val tip = - "§a+§b$extraChocFormat §afrom $colorCode$rarity strays§7${if (rarity == "legendary") extractGoldenTypesCaught(data) else ""}" + var tip = "§a+§b$extraChocFormat §afrom $colorCode${rarity.toString().lowercase()} strays§7" + if (rarity == LEGENDARY) tip += extractGoldenTypesCaught(data) Renderable.hoverTips(Renderable.string(lineFormat), tips = tip.split("\n")) } ?: Renderable.string(lineFormat) - return renderable.toSearchable(rarity) + return renderable.toSearchable(rarity.toString()) + } + + private val goldenTypesMap: Map) -> Unit> by lazy { + mapOf( + "sidedish" to { count, list -> list.add("§b$count §6Side ${StringUtils.pluralize(count, "Dish", "Dishes")}") }, + "jackpot" to { count, list -> list.add("§b$count §6Chocolate ${StringUtils.pluralize(count, "Jackpot")}") }, + "mountain" to { count, list -> list.add("§b$count §6Chocolate ${StringUtils.pluralize(count, "Mountain")}") }, + "dorado" to { count, list -> list.add("§b$count §6El Dorado ${StringUtils.pluralize(count, "Sighting")}") }, + "stampede" to { count, list -> list.add("§b$count §6${StringUtils.pluralize(count, "Stampede")}") }, + "goldenclick" to { count, list -> list.add("§b$count §6Golden ${StringUtils.pluralize(count, "Click")}") } + ) } private fun extractGoldenTypesCaught(data: Data): String { val goldenList = mutableListOf() - data.goldenTypesCaught["sidedish"]?.let { - goldenList.add("§b$it §6Side Dish" + if (it > 1) "es" else "") + data.goldenTypesCaught.sortedDesc().forEach { (key, count) -> goldenTypesMap[key]?.invoke(count, goldenList) } + return if (goldenList.isEmpty()) "" else ("\n" + goldenList.joinToString("\n")) + } + + fun handleStrayClicked(slot: Slot) { + if (!isEnabled() || claimedStraysSlots.contains(slot.slotNumber)) return + + claimedStraysSlots.add(slot.slotIndex) + val loreLine = formLoreToSingleLine(slot.stack.getLore()) + + // "Base" strays - Common -> Epic, raw choc only reward. + strayLorePattern.matchMatcher(loreLine) { + //Pretty sure base strays max at Epic, but... + val rarity = HoppityAPI.rarityByRabbit(group("rabbit")) ?: return@matchMatcher + incrementRarity(rarity, group("amount").formatLong()) } - data.goldenTypesCaught["jackpot"]?.let { - goldenList.add("§b$it §6Chocolate Jackpot" + if (it > 1) "s" else "") + + // Fish the Rabbit + fishTheRabbitPattern.matchMatcher(loreLine) { + //Also fairly sure that Fish maxes out at Rare, but... + val rarity = HoppityAPI.rarityByRabbit(group("color")) ?: return@matchMatcher + incrementRarity(rarity, group("amount").formatLong()) } - data.goldenTypesCaught["mountain"]?.let { - goldenList.add("§b$it §6Chocolate Mountain" + if (it > 1) "s" else "") + + // Golden Strays, Jackpot and Mountain, raw choc only reward. + goldenStrayJackpotMountainPattern.matchMatcher(loreLine) { + val amount = group("amount").formatLong().also { am -> incrementRarity(LEGENDARY, am) } + val multiplier = amount / ChocolateFactoryAPI.chocolatePerSecond + when (multiplier) { + in 479.0..481.0 -> incrementGoldenType("jackpot") + in 1499.0..1501.0 -> incrementGoldenType("mountain") + } } - data.goldenTypesCaught["dorado"]?.let { - goldenList.add((if (it >= 3) "§a" else "§b") + "$it§7/§a3 §6El Dorado §7Sighting" + if (it > 1) "s" else "") + + // Golden Strays, "Golden Click" + goldenStrayClick.matchMatcher(loreLine) { + incrementGoldenType("goldenclick") } - data.goldenTypesCaught["stampede"]?.let { - goldenList.add("§b$it §6Stampede" + if (it > 1) "s" else "") + + // Golden Strays, hoard/stampede + strayHoardPattern.matchMatcher(loreLine.removeResets()) { + incrementGoldenType("stampede") } - data.goldenTypesCaught["goldenclick"]?.let { - goldenList.add("§b$it §6Golden Click" + if (it > 1) "s" else "") + + // El Dorado - all catches + strayDoradoPattern.matchMatcher(loreLine) { + groupOrNull("amount")?.let { amount -> + incrementRarity(LEGENDARY, amount.formatLong()) + } + incrementGoldenType("dorado") } - return if (goldenList.size == 0) "" else ("\n" + goldenList.joinToString("\n")) } @SubscribeEvent fun onTick(event: SecondPassedEvent) { if (!isEnabled()) return - InventoryUtils.getItemsInOpenChest().filter { - !claimedStraysSlots.contains(it.slotIndex) - }.forEach { - strayCaughtPattern.matchMatcher(it.stack.name) { - if (it.stack.getLore().isEmpty()) return - claimedStraysSlots.add(it.slotIndex) - val loreLine = formLoreToSingleLine(it.stack.getLore()) - - // "Base" strays - Common -> Epic, raw choc only reward. - strayLorePattern.matchMatcher(loreLine) { - //Pretty sure base strays max at Epic, but... - val rarity = rarityFormatMap.entries.find { e -> e.value == group("rabbit").substring(0, 2) }?.key ?: "common" - incrementRarity(rarity, group("amount").formatLong()) - } - - // Fish the Rabbit - fishTheRabbitPattern.matchMatcher(loreLine) { - //Also fairly sure that Fish maxes out at Rare, but... - val rarity = rarityFormatMap.entries.find { e -> e.value == group("color").substring(0, 2) }?.key ?: "common" - incrementRarity(rarity, group("amount").formatLong()) - } - - // Golden Strays, Jackpot and Mountain, raw choc only reward. - goldenStrayJackpotMountainPattern.matchMatcher(loreLine) { - val amount = group("amount").formatLong().also { am -> incrementRarity("legendary", am) } - val multiplier = amount / ChocolateFactoryAPI.chocolatePerSecond - when (multiplier) { - in 479.0..481.0 -> incrementGoldenType("jackpot") - in 1499.0..1501.0 -> incrementGoldenType("mountain") - } - } - - // Golden Strays, "Golden Click" - goldenStrayClick.matchMatcher(loreLine) { - incrementGoldenType("goldenclick") - } - - // Golden Strays, hoard/stampede - strayHoardPattern.matchMatcher(loreLine.removeResets()) { - incrementGoldenType("stampede") - } - - // El Dorado - all catches - strayDoradoPattern.matchMatcher(loreLine) { - groupOrNull("amount")?.let { amount -> - incrementRarity("legendary", amount.formatLong()) - } - incrementGoldenType("dorado") - } - } - } InventoryUtils.getItemsInOpenChest().filter { claimedStraysSlots.contains(it.slotIndex) }.forEach { @@ -269,27 +257,17 @@ object ChocolateFactoryStrayTracker { } } - @SubscribeEvent(priority = EventPriority.HIGH) - fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { - val index = event.slot?.slotIndex ?: return - if (index == -999) return - if (claimedStraysSlots.contains(index)) return - - val clickedStack = InventoryUtils.getItemsInOpenChest() - .find { it.slotNumber == event.slot.slotNumber && it.hasStack } - ?.stack ?: return - val nameText = (if (clickedStack.hasDisplayName()) clickedStack.displayName else clickedStack.itemName) - if (!nameText.equals("§6§lGolden Rabbit §8- §aSide Dish")) return - - HoppityAPI.fireSideDishMessage() - if (!isEnabled()) return - - claimedStraysSlots.add(index) - incrementGoldenType("sidedish") - incrementRarity("legendary", 0) - DelayedRun.runDelayed(1.seconds) { - claimedStraysSlots.remove(claimedStraysSlots.indexOf(index)) + @HandleEvent + fun onEggFound(event: EggFoundEvent) { + if (!isEnabled() || event.type != HoppityEggType.SIDE_DISH) return + event.slotIndex?.let { + claimedStraysSlots.add(it) + DelayedRun.runDelayed(1.seconds) { + claimedStraysSlots.remove(claimedStraysSlots.indexOf(it)) + } } + incrementRarity(LEGENDARY, 0) + incrementGoldenType("sidedish") } @SubscribeEvent @@ -304,6 +282,32 @@ object ChocolateFactoryStrayTracker { tracker.firstUpdate() } + private fun migrateJsonStringKeyToRarityKey(jElement: JsonElement, enumClass: Class): JsonElement { + if (!jElement.isJsonObject) return jElement + val newElement = JsonObject() + + for ((key, value) in jElement.asJsonObject.entrySet()) { + val enum = try { + enumClass.javaClass.enumConstants.first { it.name.equals(key, ignoreCase = true) } + } catch (e: IllegalArgumentException) { + continue + } + value?.asInt?.let { newElement.addProperty(enum.toString(), it) } + } + + return newElement + } + + @SubscribeEvent + fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { + event.transform(58, "chocolateFactory.strayTracker.straysCaught") { element -> + migrateJsonStringKeyToRarityKey(element, LorenzRarity::class.java) + } + event.transform(58, "chocolateFactory.strayTracker.straysExtraChocMs") { element -> + migrateJsonStringKeyToRarityKey(element, LorenzRarity::class.java) + } + } + fun resetCommand() { tracker.resetCommand() } From f3a70ae089fed6172b1526c38e1fe006c1c89974 Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Fri, 20 Sep 2024 21:46:47 +0200 Subject: [PATCH 034/102] Fix: Mining Commission Blocks Color and Connected Textures (#2553) Co-authored-by: ItsEmpa --- .../transformers/MixinOptifineConfig.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java new file mode 100644 index 000000000000..97b20f19a621 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java @@ -0,0 +1,18 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Pseudo +@Mixin(targets = "Config", remap = false) +public class MixinOptifineConfig { + + @Inject(method = {"isConnectedTextures", "isConnectedTexturesFancy"}, at = @At("HEAD"), cancellable = true) + private static void isConnectedTextures(CallbackInfoReturnable cir) { + if (MiningCommissionsBlocksColor.INSTANCE.getActive()) cir.setReturnValue(false); + } +} From 6eed91d18561e77e052f365cb4815c4f6f9b465a Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 20 Sep 2024 21:47:22 +0200 Subject: [PATCH 035/102] Fix: Update Sky Mall patterns (#2551) --- src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt index 97e041a89475..6217c56c21d0 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt @@ -91,10 +91,10 @@ object HotmAPI { var mineshaftMayhem: MayhemPerk? = null enum class SkymallPerk(chat: String, itemString: String) { - MINING_SPEED("Gain §r§a\\+100 §r§6⸕ Mining Speed§r§f\\.", "Gain §a\\+100 §6⸕ Mining Speed§7\\."), - MINING_FORTUNE("Gain §r§a\\+50 §r§6☘ Mining Fortune§r§f\\.", "Gain §a\\+50 §6☘ Mining Fortune§7\\."), + MINING_SPEED("Gain §r§6\\+100⸕ Mining Speed§r§f\\.", "Gain §6\\+100⸕ Mining Speed§7\\."), + MINING_FORTUNE("Gain §r§6\\+50☘ Mining Fortune§r§f\\.", "Gain §6\\+50☘ Mining Fortune§7\\."), EXTRA_POWDER("Gain §r§a\\+15% §r§fmore Powder while mining\\.", "Gain §a\\+15% §7more Powder while mining\\."), - ABILITY_COOLDOWN("Reduce Pickaxe Ability cooldown by §r§a20%§r§f\\.", "Reduce Pickaxe Ability cooldown by"), + ABILITY_COOLDOWN("§r§a-20%§r§f Pickaxe Ability cooldowns\\.", "§a-20%§7 Pickaxe Ability cooldowns\\."), GOBLIN_CHANCE("§r§a10x §r§fchance to find Golden and Diamond Goblins\\.", "§a10x §7chance to find Golden and"), TITANIUM("Gain §r§a5x §r§9Titanium §r§fdrops", "Gain §a5x §9Titanium §7drops\\.") ; From 3f0a42c5e6eeda3576729c0ac28bf5feaa8217cc Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 20 Sep 2024 21:48:03 +0200 Subject: [PATCH 036/102] Fix: Kuudra armor always showing as starred (#2550) --- .../features/misc/items/EstimatedItemValueCalculator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt index 6842ccc77620..953809b963d9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt @@ -528,7 +528,7 @@ object EstimatedItemValueCalculator { return kuudraUpgradeTiers.indexOf(tier) } } - return 0 + return -1 } // private fun getKuudraTier(internalName: NEUInternalName): Int? = // kuudraUpgradeTiers.firstOrNull { it in internalName.toString() }?.let { kuudraUpgradeTiers.indexOf(it) } From a99f0bdd1c2c5df1495996844dcf62791c19498d Mon Sep 17 00:00:00 2001 From: Tryp0xd <54819490+Tryp0xd@users.noreply.github.com> Date: Fri, 20 Sep 2024 20:53:53 +0100 Subject: [PATCH 037/102] Fix: HotmData perk rework (#2543) --- .../at/hannibal2/skyhanni/data/HotmData.kt | 337 ++++++++++-------- .../features/mining/MineshaftPityDisplay.kt | 2 +- 2 files changed, 181 insertions(+), 158 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt b/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt index 0392eab25af8..1d8182328b6e 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt @@ -26,22 +26,21 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NumberUtil.formatLong +import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher import at.hannibal2.skyhanni.utils.RegexUtils.indexOfFirstMatch -import at.hannibal2.skyhanni.utils.RegexUtils.matchFirst import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.StringUtils.allLettersFirstUppercase import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.inventory.Slot import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -import kotlin.math.ceil import kotlin.math.pow -private fun calculatePeakOfTheMountainLoot(level: Int): Map = buildMap { +private fun calculateCoreOfTheMountainLoot(level: Int): Map = buildMap { for (i in 1..level) { when (i) { 1, 5, 7 -> addOrPut(HotmReward.EXTRA_TOKENS, 1.0) - 2 -> addOrPut(HotmReward.EXTRA_FORGE_SLOTS, 1.0) + 2 -> addOrPut(HotmReward.ABILITY_LEVEL, 1.0) 3 -> addOrPut(HotmReward.EXTRA_COMMISSION_SLOTS, 1.0) 4 -> addOrPut(HotmReward.MORE_BASE_MITHRIL_POWER, 1.0) 6 -> addOrPut(HotmReward.MORE_BASE_GEMSTONE_POWER, 2.0) @@ -62,20 +61,14 @@ enum class HotmData( MINING_SPEED( "Mining Speed", 50, - { level -> (level + 1.0).pow(3) }, + { level -> (level + 1.0).pow(3.0) }, { level -> mapOf(HotmReward.MINING_SPEED to level * 20.0) }, ), MINING_FORTUNE( "Mining Fortune", 50, { level -> (level + 1.0).pow(3.05) }, - { level -> mapOf(HotmReward.MINING_FORTUNE to level * 5.0) }, - ), - QUICK_FORGE( - "Quick Forge", - 20, - { level -> (level + 1.0).pow(4) }, - { level -> mapOf(HotmReward.FORGE_TIME_DECREASE to if (level >= 20) 30.0 else 10.0 + (level * 0.5)) }, + { level -> mapOf(HotmReward.MINING_FORTUNE to level * 2.0) }, ), TITANIUM_INSANIUM( "Titanium Insanium", @@ -83,40 +76,47 @@ enum class HotmData( { level -> (level + 1.0).pow(3.1) }, { level -> mapOf(HotmReward.TITANIUM_CHANCE to 2.0 + (level * 0.1)) }, ), - DAILY_POWDER( - "Daily Powder", - 100, - { level -> 200 + ((level - 1) * 18.0) }, - { level -> mapOf(HotmReward.DAILY_POWDER to (200.0 + ((level - 1.0) * 18.0)) * 2.0) }, - ), LUCK_OF_THE_CAVE( "Luck of the Cave", 45, { level -> (level + 1.0).pow(3.07) }, { level -> mapOf(HotmReward.EXTRA_CHANCE_TRIGGER_RARE_OCCURRENCES to 5.0 + level) }, ), - CRYSTALLIZED( - "Crystallized", - 30, - { level -> (level + 1.0).pow(3.4) }, - { level -> - mapOf( - HotmReward.MINING_SPEED to 20.0 + ((level - 1.0) * 6.0), - HotmReward.MINING_FORTUNE to 20.0 + ((level - 1.0) * 5.0), - ) - }, - ), EFFICIENT_MINER( "Efficient Miner", 100, { level -> (level + 1.0).pow(2.6) }, - { level -> mapOf(HotmReward.AVERAGE_BLOCK_BREAKS to (10.0 + (level * 0.4)) * (1.0 + (level * 0.05))) }, + { level -> mapOf(HotmReward.MINING_SPREAD to 3.0 * level) }, ), - ORBITER( - "Orbiter", - 80, - { level -> level * 70.0 }, - { level -> mapOf(HotmReward.CHANCE_EXTRA_XP_ORBS to 0.2 + (level * 0.01)) }, + QUICK_FORGE( + "Quick Forge", + 20, + { level -> (level + 1.0).pow(3.2) }, + { level -> mapOf(HotmReward.FORGE_TIME_DECREASE to if (level >= 20) 30.0 else 10.0 + (level * 0.5)) }, + ), + OLD_SCHOOL( + "Old-School", + 20, + { level -> (level + 1.0).pow(4.0) }, + { level -> mapOf(HotmReward.ORE_FORTUNE to level * 5.0) }, + ), + PROFESSIONAL( + "Professional", + 140, + { level -> (level + 1.0).pow(2.3) }, + { level -> mapOf(HotmReward.MINING_SPEED to 50.0 + (level * 5.0)) }, + ), + MOLE( + "Mole", + 200, + { level -> (level + 1.0).pow(2.17883) }, + { level -> mapOf(HotmReward.MINING_SPREAD to 50.0 + ((level - 1) * (350 / 199))) }, + ), + GEM_LOVER( + "Gem Lover", + 20, + { level -> (level + 1.0).pow(4.0) }, + { level -> mapOf(HotmReward.GEMSTONE_FORTUNE to 20.0 + (level * 4.0)) }, ), SEASONED_MINEMAN( "Seasoned Mineman", @@ -124,18 +124,25 @@ enum class HotmData( { level -> (level + 1.0).pow(2.3) }, { level -> mapOf(HotmReward.MINING_WISDOM to 5.0 + (level * 0.1)) }, ), - MOLE( - "Mole", - 190, - { level -> (level + 1.0).pow(2.2) }, - { level -> mapOf(HotmReward.AVERAGE_BLOCK_BREAKS to 1.0 + ((level + 9.0) * 0.05 * ((level + 8) % 20))) }, + FORTUNATE_MINEMAN( + "Fortunate Mineman", + 50, + { level -> (level + 1.0).pow(3.2) }, + { level -> mapOf(HotmReward.MINING_FORTUNE to level * 3.0) }, ), - PROFESSIONAL( - "Professional", - 140, - { level -> (level + 1.0).pow(2.3) }, - { level -> mapOf(HotmReward.MINING_SPEED to 50.0 + (level * 5.0)) }, + BLOCKHEAD( + "Blockhead", + 20, + { level -> (level + 1.0).pow(4.0) }, + { level -> mapOf(HotmReward.BLOCK_FORTUNE to level * 5.0) }, + ), + KEEP_IT_COOL( + "Keep It Cool", + 50, + { level -> (level + 1.0).pow(3.07) }, + { level -> mapOf(HotmReward.HEAT_RESISTANCE to level * 0.4) }, ), + LONESOME_MINER( "Lonesome Miner", 45, @@ -153,12 +160,7 @@ enum class HotmData( ) }, ), - FORTUNATE( - "Fortunate", - 20, - { level -> (level + 1.0).pow(3.05) }, - { level -> mapOf(HotmReward.MINING_FORTUNE to 20.0 + (level * 4.0)) }, - ), + POWDER_BUFF( "Powder Buff", 50, @@ -170,32 +172,32 @@ enum class HotmData( ) }, ), - MINING_SPEED_II( - "Mining Speed II", + SPEEDY_MINEMAN( + "Speedy Mineman", 50, { level -> (level + 1.0).pow(3.2) }, { level -> mapOf(HotmReward.MINING_SPEED to level * 40.0) }, ), - MINING_FORTUNE_II( - "Mining Fortune II", - 50, - { level -> (level + 1.0).pow(3.2) }, - { level -> mapOf(HotmReward.MINING_FORTUNE to level * 5.0) }, - ), - // Static - MINING_MADNESS( - "Mining Madness", - 1, - { null }, - { + + + SUBTERRANEAN_FISHER( + "Subterranean Fisher", + 40, + { level -> (level + 1.0).pow(3.07) }, + { level -> mapOf( - HotmReward.MINING_SPEED to 50.0, - HotmReward.MINING_FORTUNE to 50.0, + HotmReward.FISHING_SPEED to 5 + (level * 0.5), + HotmReward.SEA_CREATURE_CHANCE to 1 + (level * 0.1), ) }, + ), + + + // Static + SKY_MALL("Sky Mall", 1, { null }, { emptyMap() }), PRECISION_MINING("Precision Mining", 1, { null }, { mapOf(HotmReward.MINING_SPEED_BOOST to 30.0) }), FRONT_LOADED( @@ -204,16 +206,14 @@ enum class HotmData( { null }, { mapOf( - HotmReward.MINING_SPEED to 100.0, - HotmReward.MINING_FORTUNE to 100.0, - HotmReward.MORE_BASE_MITHRIL_POWER to 2.0, - HotmReward.MORE_BASE_GEMSTONE_POWER to 2.0, + HotmReward.MINING_SPEED to 250.0, + HotmReward.GEMSTONE_FORTUNE to 150.0, + HotmReward.MORE_GEMSTONE_POWER to 200.0, ) }, ), - STAR_POWDER("Star Powder", 1, { null }, { mapOf(HotmReward.MORE_MITHRIL_POWER to 300.0) }), - GOBLIN_KILLER("Goblin Killer", 1, { null }, { emptyMap() }), - + DAILY_GRIND("Daily Grind", 1, { null }, { emptyMap() }), + DAILY_POWDER("Daily Powder", 1, { null }, { emptyMap() }), // Abilities PICKOBULUS( @@ -222,8 +222,8 @@ enum class HotmData( { null }, { level -> mapOf( - HotmReward.ABILITY_RADIUS to ceil(level * 0.5) + 1.0, - HotmReward.ABILITY_COOLDOWN to 130.0 - 10.0 * level, + HotmReward.ABILITY_RADIUS to 3.0, + HotmReward.ABILITY_COOLDOWN to 60.0 - 10.0 * (level - 1), ) }, ), @@ -233,66 +233,57 @@ enum class HotmData( { null }, { level -> mapOf( - HotmReward.ABILITY_DURATION to level + 1.0, - HotmReward.ABILITY_COOLDOWN to 10.0 + 5.0 * level, + HotmReward.MINING_SPEED_BOOST to 200.0 + 50.0 * (level - 1), + HotmReward.ABILITY_DURATION to 10.0 + 5 * (level - 1), + HotmReward.ABILITY_COOLDOWN to 120.0, ) }, ), - VEIN_SEEKER( - "Vein Seeker", + MANIAC_MINER( + "Maniac Miner", 3, { null }, { level -> mapOf( - HotmReward.ABILITY_RADIUS to level + 1.0, - HotmReward.ABILITY_DURATION to 10.0 + 2.0 * level, + HotmReward.ABILITY_DURATION to 20.0 + level * 5.0, HotmReward.ABILITY_COOLDOWN to 60.0, + HotmReward.BREAKING_POWER to 1.0, ) }, ), - MANIAC_MINER( - "Maniac Miner", + + SHEER_FORCE( + "Sheer Force", 3, { null }, { level -> mapOf( - HotmReward.ABILITY_DURATION to 5.0 + level * 5.0, - HotmReward.ABILITY_COOLDOWN to 60.0 - level, + HotmReward.ABILITY_DURATION to 20.0 + 5 * (level - 1), + HotmReward.MINING_SPREAD to 200.0, ) }, ), - PEAK_OF_THE_MOUNTAIN( - "Peak of the Mountain", 10, { null }, - { level -> calculatePeakOfTheMountainLoot(level) }, + ANOMALOUS_DESIRE( + "Anomalous Desire", + 3, + { null }, + { level -> + mapOf( + HotmReward.EXTRA_CHANCE_TRIGGER_RARE_OCCURRENCES to 30.0 + (level - 1) * 10.0, + HotmReward.ABILITY_COOLDOWN to 120.0 - (level - 1) * 10.0, + HotmReward.ABILITY_DURATION to 30.0, + ) + }, ), - // Mining V3 - DAILY_GRIND( - "Daily Grind", - 100, - { level -> 218 + (18 * (level - 1.0)) }, - { level -> mapOf(HotmReward.DAILY_POWDER to 50.0 * level) }, - ), - DUST_COLLECTOR( - "Dust Collector", - 20, - { level -> (level + 1.0).pow(4) }, - { level -> mapOf(HotmReward.FOSSIL_DUST to 1.0 * level) }, - ), - WARM_HEARTED( - "Warm Hearted", - 50, - { level -> (level + 1.0).pow(3.1) }, - { level -> mapOf(HotmReward.COLD_RESISTANCE to 0.2 * level) }, + CORE_OF_THE_MOUNTAIN( + "Core of the Mountain", 10, { null }, + { level -> calculateCoreOfTheMountainLoot(level) }, ), - STRONG_ARM( - "Strong Arm", - 100, - { level -> (level + 1.0).pow(2.3) }, - { level -> mapOf(HotmReward.MINING_SPEED to 5.0 * level) }, - ), + // Mining V3 + NO_STONE_UNTURNED( "No Stone Unturned", 50, @@ -300,63 +291,87 @@ enum class HotmData( { level -> mapOf(HotmReward.UNKNOWN to 0.5 * level) }, ), - SUB_ZERO_MINING( - "SubZero Mining", + STRONG_ARM( + "Strong Arm", 100, { level -> (level + 1.0).pow(2.3) }, - { level -> mapOf(HotmReward.MINING_FORTUNE to 1.0 * level) }, + { level -> mapOf(HotmReward.MINING_SPEED to 5.0 * level) }, + ), + STEADY_HAND( + "Steady Hand", + 100, + { level -> (level + 1.0).pow(2.6) }, + { level -> mapOf(HotmReward.GEMSTONE_SPREAD to 0.1 * level) }, + ), + WARM_HEART( + "Warm Heart", + 50, + { level -> (level + 1.0).pow(3.1) }, + { level -> mapOf(HotmReward.COLD_RESISTANCE to 0.4 * level) }, ), SURVEYOR( "Surveyor", 20, - { level -> (level + 1.0).pow(4) }, + { level -> (level + 1.0).pow(4.0) }, { level -> mapOf(HotmReward.MINESHAFT_CHANCE to 0.75 * level) }, ), + METAL_HEAD( + "Metal Head", + 20, + { level -> (level + 1.0).pow(4.0) }, + { level -> mapOf(HotmReward.DWARVEN_METAL_FORTUNE to 5.0 * level) }, + ), + RAGS_TO_RICHES( + "Rags to Riches", + 50, + { level -> (level + 1.0).pow(3.05) }, + { level -> mapOf(HotmReward.MINING_FORTUNE to 4.0 * level) }, + ), EAGER_ADVENTURER( "Eager Adventurer", 100, { level -> (level + 1.0).pow(2.3) }, - { level -> mapOf(HotmReward.MINING_SPEED to 2.0 * level) }, + { level -> mapOf(HotmReward.MINING_SPEED to 4.0 * level) }, ), - - DEAD_MANS_CHEST( - "Dead Man's Chest", + CRYSTALLINE( + "Crystalline", 50, - { level -> (level + 1.0).pow(3.2) }, - { level -> mapOf(HotmReward.UNKNOWN to 1.0 * level) }, + { level -> (level + 1.0).pow(3.3) }, + { level -> mapOf(HotmReward.UNKNOWN to 0.5 * level) }, ), - GIFTS_FROM_THE_DEPARTED( "Gifts from the Departed", 100, { level -> (level + 1.0).pow(2.45) }, { level -> mapOf(HotmReward.UNKNOWN to 0.2 * level) }, ), - - EXCAVATOR( - "Excavator", + MINING_MASTER( + "Mining Master", + 10, + { level -> (level + 7.0).pow(5.0) }, + { level -> mapOf(HotmReward.PRISTINE to 0.1 * level) }, + ), + DEAD_MANS_CHEST( + "Dead Man's Chest", 50, - { level -> (level + 1.0).pow(3) }, - { level -> mapOf(HotmReward.UNKNOWN to 0.5 * level) }, + { level -> (level + 1.0).pow(3.2) }, + { level -> mapOf(HotmReward.UNKNOWN to 1.0 * level) }, ), - RAGS_TO_RICHES( - "Rags to Riches", + VANGUARD_SEEKER( + "Vanguard Seeker", 50, - { level -> (level + 1.0).pow(3.05) }, - { level -> mapOf(HotmReward.MINING_FORTUNE to 2.0 * level) }, + { level -> (level + 1.0).pow(3.1) }, + { level -> mapOf(HotmReward.UNKNOWN to 1.0 * level) }, ), - KEEN_EYE("Keen Eye", 1, { null }, { emptyMap() }), MINESHAFT_MAYHEM("Mineshaft Mayhem", 1, { null }, { emptyMap() }), - FROZEN_SOLID("Frozen Solid", 1, { null }, { emptyMap() }), GEMSTONE_INFUSION("Gemstone Infusion", 1, { null }, { emptyMap() }), - HAZARDOUS_MINER("Hazardous Miner", 1, { null }, { emptyMap() }), - + MINERS_BLESSING("Miner's Blessing", 1, { null }, { mapOf(HotmReward.MAGIC_FIND to 30.0) }), ; private val guiNamePattern by patternGroup.pattern("perk.name.${name.lowercase().replace("_", "")}", "§.$guiName") - val printName = name.allLettersFirstUppercase() + val printName get() = name.allLettersFirstUppercase() /** Level which are actually paid with powder (does exclude [blueEgg])*/ var rawLevel: Int @@ -367,15 +382,15 @@ enum class HotmData( /** Level for which the effect that is present (considers [enabled] and [blueEgg])*/ val activeLevel: Int - get() = if (enabled) effectivLevel else 0 + get() = if (enabled) effectiveLevel else 0 /** Level that considering [blueEgg]*/ - val effectivLevel: Int get() = storage?.perks?.get(this.name)?.level?.plus(blueEgg()) ?: 0 + val effectiveLevel: Int get() = storage?.perks?.get(this.name)?.level?.plus(blueEgg()) ?: 0 val isMaxLevel: Boolean - get() = effectivLevel >= maxLevel // >= to account for +1 from Blue Cheese + get() = effectiveLevel >= maxLevel // >= to account for +1 from Blue Cheese - private fun blueEgg() = if (this != PEAK_OF_THE_MOUNTAIN && maxLevel != 1 && HotmAPI.isBlueEggActive) 1 else 0 + private fun blueEgg() = if (this != CORE_OF_THE_MOUNTAIN && maxLevel != 1 && HotmAPI.isBlueEggActive) 1 else 0 var enabled: Boolean get() = storage?.perks?.get(this.name)?.enabled ?: false @@ -406,7 +421,7 @@ enum class HotmData( val storage get() = ProfileStorageData.profileSpecific?.mining?.hotmTree - val abilities = listOf(PICKOBULUS, MINING_SPEED_BOOST, VEIN_SEEKER, MANIAC_MINER, HAZARDOUS_MINER, GEMSTONE_INFUSION) + val abilities = listOf(PICKOBULUS, MINING_SPEED_BOOST, MANIAC_MINER, GEMSTONE_INFUSION, ANOMALOUS_DESIRE, SHEER_FORCE) private val inventoryPattern by patternGroup.pattern( "inventory", @@ -514,10 +529,10 @@ enum class HotmData( HotmAPI.MayhemPerk.entries.forEach { it.chatPattern } - (0..PEAK_OF_THE_MOUNTAIN.maxLevel).forEach { level -> + (0..CORE_OF_THE_MOUNTAIN.maxLevel).forEach { level -> val map = mutableMapOf() if (level >= 1) map.addOrPut(HotmReward.EXTRA_TOKENS, 1.0) - if (level >= 2) map.addOrPut(HotmReward.EXTRA_FORGE_SLOTS, 1.0) + if (level >= 2) map.addOrPut(HotmReward.ABILITY_LEVEL, 1.0) if (level >= 3) map.addOrPut(HotmReward.EXTRA_COMMISSION_SLOTS, 1.0) if (level >= 4) map.addOrPut(HotmReward.MORE_BASE_MITHRIL_POWER, 1.0) if (level >= 5) map.addOrPut(HotmReward.EXTRA_TOKENS, 1.0) @@ -527,7 +542,7 @@ enum class HotmData( if (level >= 9) map.addOrPut(HotmReward.MINESHAFT_CHANCE, 10.0) if (level >= 10) map.addOrPut(HotmReward.EXTRA_TOKENS, 2.0) - peakOfTheMountainPerks[level] = map + coreOfTheMountainPerks[level] = map } } @@ -551,7 +566,7 @@ enum class HotmData( val lore = item.getLore().takeIf { it.isNotEmpty() } ?: return - if (entry != PEAK_OF_THE_MOUNTAIN && notUnlockedPattern.matches(lore.last())) { + if (entry != CORE_OF_THE_MOUNTAIN && notUnlockedPattern.matches(lore.last())) { entry.rawLevel = 0 entry.enabled = false entry.isUnlocked = false @@ -574,7 +589,7 @@ enum class HotmData( ) } - if (entry == PEAK_OF_THE_MOUNTAIN) { + if (entry == CORE_OF_THE_MOUNTAIN) { entry.enabled = entry.rawLevel != 0 return } @@ -663,7 +678,7 @@ enum class HotmData( fun onScoreboardUpdate(event: ScoreboardUpdateEvent) { if (!LorenzUtils.inSkyBlock) return - event.scoreboard.matchFirst(ScoreboardPattern.powderPattern) { + ScoreboardPattern.powderPattern.firstMatcher(event.scoreboard) { val type = HotmAPI.PowderType.entries.firstOrNull { it.displayName == group("type") } ?: return val amount = group("amount").formatLong() val difference = amount - type.getCurrent() @@ -690,7 +705,7 @@ enum class HotmData( DelayedRun.runNextTick { InventoryUtils.getItemsInOpenChest().forEach { it.parse() } abilities.filter { it.isUnlocked }.forEach { - it.rawLevel = if (PEAK_OF_THE_MOUNTAIN.rawLevel >= 1) 2 else 1 + it.rawLevel = if (CORE_OF_THE_MOUNTAIN.rawLevel >= 1) 2 else 1 } } } @@ -791,7 +806,7 @@ enum class HotmData( } } -private val peakOfTheMountainPerks = mutableMapOf>() +private val coreOfTheMountainPerks = mutableMapOf>() private val patternGroup = RepoPattern.group("mining.hotm") @@ -801,7 +816,6 @@ enum class HotmReward { MINING_WISDOM, FORGE_TIME_DECREASE, TITANIUM_CHANCE, - DAILY_POWDER, MORE_BASE_MITHRIL_POWER, MORE_BASE_GEMSTONE_POWER, MORE_BASE_GLACITE_POWER, @@ -811,17 +825,26 @@ enum class HotmReward { CHANCE_OF_TREASURE_CHEST, LOCKS_OF_TREASURE_CHEST, EXTRA_CHANCE_TRIGGER_RARE_OCCURRENCES, - AVERAGE_BLOCK_BREAKS, - CHANCE_EXTRA_XP_ORBS, MINING_SPEED_BOOST, ABILITY_DURATION, ABILITY_RADIUS, ABILITY_COOLDOWN, - FOSSIL_DUST, + ABILITY_LEVEL, MINESHAFT_CHANCE, EXTRA_TOKENS, - EXTRA_FORGE_SLOTS, EXTRA_COMMISSION_SLOTS, UNKNOWN, - COLD_RESISTANCE + COLD_RESISTANCE, + MINING_SPREAD, + GEMSTONE_SPREAD, + ORE_FORTUNE, + BLOCK_FORTUNE, + GEMSTONE_FORTUNE, + DWARVEN_METAL_FORTUNE, + HEAT_RESISTANCE, + MAGIC_FIND, + PRISTINE, + FISHING_SPEED, + SEA_CREATURE_CHANCE, + BREAKING_POWER, } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt index 77d580a0b194..3b89ae246bc2 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt @@ -164,7 +164,7 @@ object MineshaftPityDisplay { // if the chance is 1/1500, it will return 1500 private fun calculateChance(counter: Int): Double { val surveyorPercent = HotmData.SURVEYOR.getReward()[HotmReward.MINESHAFT_CHANCE] ?: 0.0 - val peakMountainPercent = HotmData.PEAK_OF_THE_MOUNTAIN.getReward()[HotmReward.MINESHAFT_CHANCE] ?: 0.0 + val peakMountainPercent = HotmData.CORE_OF_THE_MOUNTAIN.getReward()[HotmReward.MINESHAFT_CHANCE] ?: 0.0 val chance = counter / (1 + surveyorPercent / 100 + peakMountainPercent / 100) return chance } From 08da9f51266d8f514a047cd3f9ffb5adfd7b29e8 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:37:38 +0200 Subject: [PATCH 038/102] Improvement: Area Navigation updates (#2537) Co-authored-by: nea Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/config/commands/Commands.kt | 2 +- .../config/features/dev/GraphConfig.java | 5 + .../hannibal2/skyhanni/data/IslandGraphs.kt | 3 +- .../at/hannibal2/skyhanni/data/model/Graph.kt | 85 +++++++++++++---- .../skyhanni/data/model/GraphNodeTag.kt | 13 ++- .../skyhanni/features/misc/IslandAreas.kt | 17 +--- .../mixins/transformers/MixinKeyBinding.java | 2 +- .../skyhanni/test/SkyHanniDebugsAndTests.kt | 33 +------ .../skyhanni/test/{ => graph}/GraphEditor.kt | 39 ++++++-- .../test/graph/GraphEditorBugFinder.kt | 95 +++++++++++++++++++ .../test/{ => graph}/GraphNodeEditor.kt | 68 +++++++++++-- .../at/hannibal2/skyhanni/utils/GraphUtils.kt | 68 +++++++++++++ .../at/hannibal2/skyhanni/utils/LorenzVec.kt | 3 + .../hannibal2/skyhanni/utils/RaycastUtils.kt | 73 ++++++++++++++ 14 files changed, 429 insertions(+), 77 deletions(-) rename src/main/java/at/hannibal2/skyhanni/test/{ => graph}/GraphEditor.kt (93%) create mode 100644 src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt rename src/main/java/at/hannibal2/skyhanni/test/{ => graph}/GraphNodeEditor.kt (71%) create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/GraphUtils.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/RaycastUtils.kt 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 d0f58f0829d9..3f2ddace62d2 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -77,7 +77,6 @@ import at.hannibal2.skyhanni.features.rift.area.westvillage.VerminTracker import at.hannibal2.skyhanni.features.rift.everywhere.PunchcardHighlight import at.hannibal2.skyhanni.features.slayer.SlayerProfitTracker import at.hannibal2.skyhanni.test.DebugCommand -import at.hannibal2.skyhanni.test.GraphEditor import at.hannibal2.skyhanni.test.PacketTest import at.hannibal2.skyhanni.test.SkyBlockIslandTest import at.hannibal2.skyhanni.test.SkyHanniConfigSearchResetCommand @@ -92,6 +91,7 @@ import at.hannibal2.skyhanni.test.command.CopyScoreboardCommand import at.hannibal2.skyhanni.test.command.TestChatCommand import at.hannibal2.skyhanni.test.command.TrackParticlesCommand import at.hannibal2.skyhanni.test.command.TrackSoundsCommand +import at.hannibal2.skyhanni.test.graph.GraphEditor import at.hannibal2.skyhanni.utils.APIUtil import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ExtendedChatColor diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java index acf9ac67d9e0..dda1ddb1e50c 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java @@ -32,6 +32,11 @@ public class GraphConfig { @ConfigEditorKeybind(defaultKey = -98) // Middle Mouse public int selectKey = -98; + @Expose + @ConfigOption(name = "Select near look", desc = "Select the node closest to where you are looking.") + @ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE) + public int selectRaycastKey = Keyboard.KEY_NONE; + @Expose @ConfigOption(name = "Connect Key", desc = "Connect the nearest node with the active node. If the nodes are already connected removes the connection.") @ConfigEditorKeybind(defaultKey = Keyboard.KEY_C) diff --git a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt index 0b173db46e04..ca3143b952ab 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt @@ -45,13 +45,14 @@ import kotlin.math.abs * 12 starter NPC's * diana * farming: - * pelt farming + * pelt farming area * rift: * enigma souls * eyes * big quests * montezuma souls * blood effigies + * avoid area around enderman * spider: * relicts + throw spot * dwarven mines: diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt index 8c63be891fb3..b9ad1251ca8c 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt @@ -10,6 +10,7 @@ import com.google.gson.annotations.Expose import com.google.gson.stream.JsonToken import java.util.PriorityQueue +// TODO: This class should be disambiguated into a NodePath and a Graph class @JvmInline value class Graph( @Expose val nodes: List, @@ -49,7 +50,7 @@ value class Graph( out.name("Name").value(it) } - it.tagNames?.takeIf { it.isNotEmpty() }?.let { + it.tagNames.takeIf { list -> list.isNotEmpty() }?.let { out.name("Tags") out.beginArray() for (tagName in it) { @@ -79,8 +80,8 @@ value class Graph( reader.beginObject() var position: LorenzVec? = null var name: String? = null - var tags: List? = null - var neighbors = mutableListOf>() + var tags = emptyList() + val neighbors = mutableListOf>() while (reader.hasNext()) { if (reader.peek() != JsonToken.NAME) { reader.skipValue() @@ -140,10 +141,10 @@ value class Graph( } // The node object that gets parsed from/to json -class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null, val tagNames: List? = null) { +class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null, val tagNames: List = emptyList()) { val tags: List by lazy { - tagNames?.mapNotNull { GraphNodeTag.byId(it) } ?: emptyList() + tagNames.mapNotNull { GraphNodeTag.byId(it) } } /** Keys are the neighbours and value the edge weight (e.g. Distance) */ @@ -167,18 +168,50 @@ class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null, fun Graph.findShortestPathAsGraph(start: GraphNode, end: GraphNode): Graph = this.findShortestPathAsGraphWithDistance(start, end).first -fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode): Pair { +data class DijkstraTree( + val origin: GraphNode, + /** + * A map of distances between the [origin] and each node in a graph. This distance map is only accurate for nodes closer to the + * origin than the [lastVisitedNode]. In case there is no early bailout, this map will be accurate for all nodes in the graph. + */ + val distances: Map, + /** + * A map of nodes to the neighbouring node that is the quickest path towards the origin (the neighbouring node that has the lowest value + * in [distances]) + */ + val towardsOrigin: Map, + /** + * This is either the furthest away node in the graph, or the node that was bailed out on early because it fulfilled the search + * condition. In case the search condition matches nothing, this will *still* be the furthest away node, so an additional check might be + * necessary. + */ + val lastVisitedNode: GraphNode, +) + +/** + * Find a tree of distances to the [start] node using dijkstra's algorithm. + */ +fun Graph.findDijkstraDistances( + start: GraphNode, + /** + * Bail out early before collecting all the distances to all nodes in the graph. This will not collect valid distance data for *all* + * nodes for which bailout matches, but only the closest one. + */ + bailout: (GraphNode) -> Boolean, +): DijkstraTree { val distances = mutableMapOf() val previous = mutableMapOf() val visited = mutableSetOf() val queue = PriorityQueue(compareBy { distances.getOrDefault(it, Double.MAX_VALUE) }) + var lastVisitedNode: GraphNode = start distances[start] = 0.0 queue.add(start) while (queue.isNotEmpty()) { val current = queue.poll() - if (current == end) break + lastVisitedNode = current + if (bailout(current)) break visited.add(current) @@ -194,16 +227,34 @@ fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode): } } - return Graph( - buildList { - var current = end - while (current != start) { - add(current) - current = previous[current] ?: return Graph(emptyList()) to 0.0 - } - add(start) - }.reversed(), - ) to distances[end]!! + return DijkstraTree( + start, + distances, + previous, + lastVisitedNode, + ) +} + +fun Graph.findAllShortestDistances(start: GraphNode): DijkstraTree { + return findDijkstraDistances(start) { false } +} + +fun DijkstraTree.findPathToDestination(end: GraphNode): Pair { + val distances = this + val reversePath = buildList { + var current = end + while (true) { + add(current) + if (current == distances.origin) break + current = distances.towardsOrigin[current] ?: return Graph(emptyList()) to 0.0 + } + } + return Graph(reversePath.reversed()) to distances.distances[end]!! +} + +fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode): Pair { + val distances = findDijkstraDistances(start) { it == end } + return distances.findPathToDestination(end) } fun Graph.findShortestPath(start: GraphNode, end: GraphNode): List = this.findShortestPathAsGraph(start, end).toPositionsList() diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt index e1fa3317a8cf..e70786137bf6 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.utils.LorenzColor enum class GraphNodeTag( val internalName: String, val color: LorenzColor, - val cleanName: String, + cleanName: String, val description: String, val onlyIsland: IslandType? = null, ) { @@ -17,13 +17,17 @@ enum class GraphNodeTag( AREA("area", LorenzColor.DARK_GREEN, "Area", "A big SkyBlock area."), SMALL_AREA("small_area", LorenzColor.GREEN, "Small Area", "A small SkyBlock area, e.g. a house."), POI("poi", LorenzColor.WHITE, "PoI", "Point of interest."), - LAUNCH_PAD("launch", LorenzColor.WHITE, "Launch Pad", "Slime blocks sending you to another server."), +// LAUNCH_PAD("launch", LorenzColor.WHITE, "Launch Pad", "Slime blocks sending you to another server."), + TELEPORT("teleport", LorenzColor.BLUE, "Teleport", "A spot from/to teleport."), // on multiple islands ROMEO("romeo", LorenzColor.WHITE, "Romeo & Juliette Quest", "Blocks related to the Romeo and Juliette/Ring of Love quest line."), RACE("race", LorenzColor.WHITE, "Race Start/Stop", "A race start or stop point."), - SLAYER("slayer", LorenzColor.WHITE, "Slayer", "A Slayer area"), + SLAYER("slayer", LorenzColor.RED, "Slayer", "A Slayer area"), HOPPITY("hoppity", LorenzColor.AQUA, "Hoppity Egg", "An egg location in Hoppity's Hunt."), + GRIND_MOBS("grind_mobs", LorenzColor.RED, "Grind Mobs", "A spot where combat mobs spawn that can be killed."), + GRIND_ORES("grind_ores", LorenzColor.DARK_AQUA, "Grind Ores", "A spot where mining ores spawn that can be mines."), + GRIND_CROPS("grind_crops", LorenzColor.DARK_GREEN, "Grind Crops", "A spot where farming crops spawn that can be farmed."), // hoppity // Hub @@ -52,6 +56,9 @@ enum class GraphNodeTag( // Crimson Isles CRIMSON_MINIBOSS("crimson_miniboss", LorenzColor.RED, "Crimson Miniboss", "Mini bosses in Crimson Isle.", onlyIsland = IslandType.CRIMSON_ISLE), + // The End + END_GOLEM("end_golem", LorenzColor.RED, "Golem Spawn", "A spot where the golem in the end spawns", onlyIsland = IslandType.THE_END), + ; val displayName: String = color.getChatColor() + cleanName diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt index d85b34cc6936..311af056eab0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt @@ -6,7 +6,6 @@ import at.hannibal2.skyhanni.data.model.Graph import at.hannibal2.skyhanni.data.model.GraphNode import at.hannibal2.skyhanni.data.model.GraphNodeTag import at.hannibal2.skyhanni.data.model.TextInput -import at.hannibal2.skyhanni.data.model.findShortestPathAsGraphWithDistance import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.EntityMoveEvent import at.hannibal2.skyhanni.events.GuiRenderEvent @@ -19,6 +18,7 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString import at.hannibal2.skyhanni.utils.CollectionUtils.sorted import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor import at.hannibal2.skyhanni.utils.ConditionalUtils +import at.hannibal2.skyhanni.utils.GraphUtils import at.hannibal2.skyhanni.utils.LocationUtils.canBeSeen import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzColor @@ -65,19 +65,10 @@ object IslandAreas { val graph = IslandGraphs.currentIslandGraph ?: return val closedNote = IslandGraphs.closedNote ?: return - val paths = mutableMapOf() - - val map = mutableMapOf() - for (graphNode in graph.nodes) { - if (graphNode.getAreaTag() == null) continue - val (path, distance) = graph.findShortestPathAsGraphWithDistance(closedNote, graphNode) - paths[graphNode] = path - map[graphNode] = distance - } + val (paths, map) = GraphUtils.findFastestPaths(graph, closedNote) { it.getAreaTag() != null } this.paths = paths val finalNodes = mutableMapOf() - val alreadyFoundAreas = mutableListOf() for ((node, distance) in map.sorted()) { val areaName = node.name ?: continue @@ -271,8 +262,8 @@ object IslandAreas { private val allAreas = listOf(GraphNodeTag.AREA, GraphNodeTag.SMALL_AREA) private val onlyLargeAreas = listOf(GraphNodeTag.AREA) - private fun GraphNode.getAreaTag(): GraphNodeTag? = tags.firstOrNull { - it in (if (config.includeSmallAreas) allAreas else onlyLargeAreas) + fun GraphNode.getAreaTag(ignoreConfig: Boolean = false): GraphNodeTag? = tags.firstOrNull { + it in (if (config.includeSmallAreas || ignoreConfig) allAreas else onlyLargeAreas) } private fun setTarget(node: GraphNode) { diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinKeyBinding.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinKeyBinding.java index a070f5e8928a..01efda5cebb8 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinKeyBinding.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinKeyBinding.java @@ -2,7 +2,7 @@ import at.hannibal2.skyhanni.data.model.TextInput; import at.hannibal2.skyhanni.features.garden.farming.GardenCustomKeybinds; -import at.hannibal2.skyhanni.test.GraphEditor; +import at.hannibal2.skyhanni.test.graph.GraphEditor; import net.minecraft.client.settings.KeyBinding; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index 5b7333189b1e..045ce5bce1c2 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -131,36 +131,13 @@ object SkyHanniDebugsAndTests { } fun testCommand(args: Array) { + SkyHanniMod.coroutineScope.launch { + asyncTest() + } + } + private fun asyncTest() { -// val a = Thread { OSUtils.copyToClipboard("123") } -// val b = Thread { OSUtils.copyToClipboard("456") } -// a.start() -// b.start() - -// for ((i, s) in ScoreboardData.siedebarLinesFormatted().withIndex()) { -// println("$i: '$s'") -// } - -// val name = args[0] -// val pitch = args[1].toFloat() -// val sound = SoundUtils.createSound("note.harp", 1.35f) -// val sound = SoundUtils.createSound("random.orb", 11.2f) -// SoundUtils.createSound(name, pitch).playSound() - -// a = args[0].toDouble() -// b = args[1].toDouble() -// c = args[2].toDouble() - -// for (line in getPlayerTabOverlay().footer.unformattedText -// .split("\n")) { -// println("footer: '$line'") -// } -// -// -// for (line in TabListUtils.getTabList()) { -// println("tablist: '$line'") -// } } fun findNullConfig(args: Array) { diff --git a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditor.kt similarity index 93% rename from src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt rename to src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditor.kt index d4038162f865..1aefeda52925 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/GraphEditor.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditor.kt @@ -1,4 +1,4 @@ -package at.hannibal2.skyhanni.test +package at.hannibal2.skyhanni.test.graph import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.data.IslandGraphs @@ -26,6 +26,7 @@ import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.OSUtils +import at.hannibal2.skyhanni.utils.RaycastUtils import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine_nea import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled @@ -127,6 +128,7 @@ object GraphEditor { if (!inEditMode && !inTextMode) { add("§ePlace: §6${KeyboardManager.getKeyName(config.placeKey)}") add("§eSelect: §6${KeyboardManager.getKeyName(config.selectKey)}") + add("§eSelect (Look): §6${KeyboardManager.getKeyName(config.selectRaycastKey)}") add("§eConnect: §6${KeyboardManager.getKeyName(config.connectKey)}") add("§eTest: §6${KeyboardManager.getKeyName(config.dijkstraKey)}") add("§eVision: §6${KeyboardManager.getKeyName(config.throughBlocksKey)}") @@ -259,7 +261,9 @@ object GraphEditor { } } - private fun chatAtDisable() = ChatUtils.clickableChat("Graph Editor is now inactive. §lClick to activate.", ::commandIn) + private fun chatAtDisable() = ChatUtils.clickableChat("Graph Editor is now inactive. §lClick to activate.", + GraphEditor::commandIn + ) private fun input() { if (LorenzUtils.isAnyGuiActive()) return @@ -346,13 +350,35 @@ object GraphEditor { closedNode } } + if (config.selectRaycastKey.isKeyClicked()) { + val playerRay = RaycastUtils.createPlayerLookDirectionRay() + var minimumDistance = Double.MAX_VALUE + var minimumNode: GraphingNode? = null + for (node in nodes) { + val nodeCenterPosition = node.position.add(0.5, 0.5, 0.5) + val distance = RaycastUtils.findDistanceToRay(playerRay, nodeCenterPosition) + if (distance > minimumDistance) { + continue + } + if (minimumDistance > 1.0) { + minimumNode = node + minimumDistance = distance + continue + } + if (minimumNode == null || minimumNode.position.distanceSqToPlayer() > node.position.distanceSqToPlayer()) { + minimumNode = node + minimumDistance = distance + } + } + activeNode = minimumNode + } if (activeNode != closedNode && config.connectKey.isKeyClicked()) { val edge = getEdgeIndex(activeNode, closedNode) if (edge == null) { addEdge(activeNode, closedNode) feedBackInTutorial("Added new edge.") } else { - this.edges.removeAt(edge) + edges.removeAt(edge) checkDissolve() selectedEdge = findEdgeBetweenActiveAndClosed() feedBackInTutorial("Removed edge.") @@ -405,6 +431,7 @@ object GraphEditor { val compileGraph = compileGraph() if (config.useAsIslandArea) { IslandGraphs.setNewGraph(compileGraph) + GraphEditorBugFinder.runTests() } val json = compileGraph.toJson() OSUtils.copyToClipboard(json) @@ -457,7 +484,7 @@ object GraphEditor { nodes.remove(closedNode) edges.removeIf { it.isInEdge(closedNode) } if (closedNode == activeNode) activeNode = null - this.closedNode = null + GraphEditor.closedNode = null return } } @@ -505,7 +532,7 @@ object GraphEditor { prune() val indexedTable = nodes.mapIndexed { index, node -> node.id to index }.toMap() val nodes = nodes.mapIndexed { index, it -> GraphNode(index, it.position, it.name, it.tags.mapNotNull { it.internalName }) } - val neighbours = this.nodes.map { node -> + val neighbours = GraphEditor.nodes.map { node -> edges.filter { it.isInEdge(node) }.map { edge -> val otherNode = if (node == edge.node1) edge.node2 else edge.node1 nodes[indexedTable[otherNode.id]!!] to node.position.distance(otherNode.position) @@ -523,7 +550,7 @@ object GraphEditor { it.id, it.position, it.name, - it.tagNames?.mapNotNull { GraphNodeTag.byId(it) }?.toMutableList() ?: mutableListOf(), + it.tagNames.mapNotNull { tag -> GraphNodeTag.byId(tag) }.toMutableList(), ) }, ) diff --git a/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt new file mode 100644 index 000000000000..aee75ec5faf6 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt @@ -0,0 +1,95 @@ +package at.hannibal2.skyhanni.test.graph + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandGraphs +import at.hannibal2.skyhanni.data.model.GraphNode +import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent +import at.hannibal2.skyhanni.features.misc.IslandAreas.getAreaTag +import at.hannibal2.skyhanni.test.graph.GraphEditor.distanceSqToPlayer +import at.hannibal2.skyhanni.utils.GraphUtils +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText +import kotlinx.coroutines.launch +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.awt.Color + +// Trying to find errors in Area Graph for the current graph editor instance +object GraphEditorBugFinder { + private var errorsInWorld = emptyMap() + + fun runTests() { + SkyHanniMod.coroutineScope.launch { + asyncTest() + } + } + + private fun asyncTest() { + val graph = IslandGraphs.currentIslandGraph ?: return + val errorsInWorld: MutableMap = mutableMapOf() + val nodes = graph.nodes + + val nearestArea = mutableMapOf() + for (node in nodes) { + val pathToNearestArea = GraphUtils.findFastestPath(graph, node) { it.getAreaTag(ignoreConfig = true) != null }?.first + if (pathToNearestArea == null) { + continue + } + val areaNode = pathToNearestArea.lastOrNull() ?: error("Empty path to nearest area") + nearestArea[node] = areaNode + } + var bugs = 0 + for (node in nodes) { + val areaNode = nearestArea[node]?.name ?: continue + for (neighbour in node.neighbours.keys) { + val neighbouringAreaNode = nearestArea[neighbour]?.name ?: continue + if (neighbouringAreaNode == areaNode) continue + if ((null == node.getAreaTag(ignoreConfig = true))) { + bugs++ + errorsInWorld[node.position] = "§cConflicting areas $areaNode and $neighbouringAreaNode" + } + } + } + for (node in nodes) { + val nameNull = node.name.isNullOrBlank() + val tagsEmpty = node.tags.isEmpty() + if (nameNull > tagsEmpty) { + errorsInWorld[node.position] = "§cMissing name despite having tags" + bugs++ + } + if (tagsEmpty > nameNull) { + errorsInWorld[node.position] = "§cMissing tags despite having name" + bugs++ + } + } + + val clusters = GraphUtils.findDisjointClusters(graph) + if (clusters.size > 1) { + val closestCluster = clusters.minBy { it.minOf { it.position.distanceSqToPlayer() } } + val foreignClusters = clusters.filter { it !== closestCluster } + val closestForeignNodes = foreignClusters.map { network -> network.minBy { it.position.distanceSqToPlayer() } } + closestForeignNodes.forEach { + errorsInWorld[it.position] = "§cDisjoint node network" + bugs++ + } + val closestForeignNode = closestForeignNodes.minBy { it.position.distanceSqToPlayer() } + val closestNodeToForeignNode = closestCluster.minBy { it.position.distanceSq(closestForeignNode.position) } + IslandGraphs.pathFind(closestNodeToForeignNode.position, Color.RED) + } + + println("found $bugs bugs!") + this.errorsInWorld = errorsInWorld + if (clusters.size <= 1) { + IslandGraphs.pathFind(errorsInWorld.keys.minByOrNull { it.distanceSqToPlayer() } ?: return, Color.RED) + } + } + + @SubscribeEvent + fun onRenderWorld(event: LorenzRenderWorldEvent) { + if (!isEnabled()) return + + for ((location, text) in errorsInWorld) { + event.drawDynamicText(location, text, 1.5) + } + } + fun isEnabled() = GraphEditor.isEnabled() +} diff --git a/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphNodeEditor.kt similarity index 71% rename from src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt rename to src/main/java/at/hannibal2/skyhanni/test/graph/GraphNodeEditor.kt index f541b16e307c..e6c4536d8b84 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/GraphNodeEditor.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphNodeEditor.kt @@ -1,10 +1,10 @@ -package at.hannibal2.skyhanni.test +package at.hannibal2.skyhanni.test.graph import at.hannibal2.skyhanni.data.model.GraphNodeTag import at.hannibal2.skyhanni.data.model.TextInput import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.test.GraphEditor.distanceSqToPlayer +import at.hannibal2.skyhanni.test.graph.GraphEditor.distanceSqToPlayer import at.hannibal2.skyhanni.utils.CollectionUtils.addString import at.hannibal2.skyhanni.utils.KeyboardManager import at.hannibal2.skyhanni.utils.LorenzUtils @@ -29,12 +29,12 @@ object GraphNodeEditor { private val textInput = TextInput() private var nodesDisplay = emptyList() private var lastUpdate = SimpleTimeMark.farPast() + private val tagsToShow: MutableList = GraphNodeTag.entries.toMutableList() @SubscribeEvent fun onGuiRender(event: GuiRenderEvent) { if (!isEnabled()) return - config.namedNodesList.renderRenderables( getNodeNames(), posLabel = "Graph Nodes List", @@ -52,15 +52,66 @@ object GraphNodeEditor { lastUpdate = SimpleTimeMark.now() nodesDisplay = buildList { val list = drawNodeNames() - val size = list.size - addString("§eGraph Nodes: $size") - val height = (size * 10).coerceAtMost(250) + val total = GraphEditor.nodes.count { it.name?.isNotBlank() ?: false } + val shown = list.size + add( + Renderable.clickAndHover( + "§eGraph Nodes: $shown/$total", + listOf("§eClick to toggle node tags!"), + onClick = { + updateToggleTags() + }, + ), + ) + val height = (shown * 10).coerceAtMost(250) if (list.isNotEmpty()) { add(list.buildSearchableScrollable(height, textInput, scrollValueNodes, velocity = 10.0)) } } } + private fun updateToggleTags() { + lastUpdate = SimpleTimeMark.now() + 60.seconds + nodesDisplay = buildList { + addString("§eToggle Visible Tags") + for (tag in GraphNodeTag.entries) { + val isVisible = tag in tagsToShow + val nodes = GraphEditor.nodes.count { tag in it.tags } + val visibilityText = if (isVisible) " §aVisible" else " §7Invisible" + val name = " - ${tag.displayName} §8($nodes nodes) $visibilityText" + add( + Renderable.clickAndHover( + name, + listOf("§eClick to " + (if (isVisible) "hide" else "show") + " nodes with this tag!"), + onClick = { + toggleTag(tag) + updateToggleTags() + }, + ), + ) + } + addString("") + add( + Renderable.clickAndHover( + "§cGo Back!", + tips = listOf("§eClick to go back to the node list!"), + onClick = { + updateNodeNames() + }, + ), + ) + } + + } + + private fun toggleTag(tag: GraphNodeTag) { + if (tag in tagsToShow) { + tagsToShow.remove(tag) + } else { + tagsToShow.add(tag) + } + } + private fun updateTagView(node: GraphingNode) { lastUpdate = SimpleTimeMark.now() + 60.seconds nodesDisplay = buildList { @@ -123,7 +174,10 @@ object GraphNodeEditor { private fun drawNodeNames(): List = buildList { for ((node, distance: Double) in GraphEditor.nodes.map { it to it.position.distanceSqToPlayer() }.sortedBy { it.second }) { - val name = node.name?.takeIf { !it.isBlank() } ?: continue + if (node.tags.isNotEmpty()) { + if (!node.tags.any { it in tagsToShow }) continue + } + val name = node.name?.takeIf { it.isNotBlank() } ?: continue val color = if (node == GraphEditor.activeNode) "§a" else "§7" val distanceFormat = sqrt(distance).toInt().addSeparators() val tagText = node.tags.let { tags -> diff --git a/src/main/java/at/hannibal2/skyhanni/utils/GraphUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/GraphUtils.kt new file mode 100644 index 000000000000..4a245a894252 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/GraphUtils.kt @@ -0,0 +1,68 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.data.model.Graph +import at.hannibal2.skyhanni.data.model.GraphNode +import at.hannibal2.skyhanni.data.model.findAllShortestDistances +import at.hannibal2.skyhanni.data.model.findDijkstraDistances +import at.hannibal2.skyhanni.data.model.findPathToDestination +import java.util.Stack + +object GraphUtils { + /** + * Find the fastest path from [closestNode] to *any* node that matches [condition]. + */ + fun findFastestPath( + graph: Graph, + closestNode: GraphNode, + condition: (GraphNode) -> Boolean, + ): Pair? { + val distances = graph.findDijkstraDistances(closestNode, condition) + val entry = distances.lastVisitedNode.takeIf(condition) + return entry?.let { + distances.findPathToDestination(it) + } + } + + /** + * Find the fastest path from [closestNode] to *all* nodes that matches [condition]. + */ + fun findFastestPaths( + graph: Graph, + closestNode: GraphNode, + condition: (GraphNode) -> Boolean = { true }, + ): Pair, MutableMap> { + val paths = mutableMapOf() + + val map = mutableMapOf() + val distances = graph.findAllShortestDistances(closestNode) + for (graphNode in graph.nodes) { + if (!condition(graphNode)) continue + val (path, distance) = distances.findPathToDestination(graphNode) + paths[graphNode] = path + map[graphNode] = distance + } + return Pair(paths, map) + } + + /** + * Find all maximal sub graphs of the given graph which are not connected + */ + fun findDisjointClusters(graph: Graph): List> { + val universe = graph.toMutableSet() + val allClusters = mutableListOf>() + while (universe.isNotEmpty()) { + val cluster = mutableSetOf() + allClusters.add(cluster) + val queue = Stack() + queue.add(universe.first()) + while (queue.isNotEmpty()) { + val next = queue.pop() + universe.remove(next) + cluster.add(next) + queue.addAll(next.neighbours.keys) + queue.retainAll(universe) + } + } + return allClusters + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index 51bb8b72cd81..a573ed78daeb 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -9,6 +9,7 @@ import net.minecraft.util.BlockPos import net.minecraft.util.Rotations import net.minecraft.util.Vec3 import kotlin.math.abs +import kotlin.math.absoluteValue import kotlin.math.acos import kotlin.math.cos import kotlin.math.max @@ -126,6 +127,8 @@ data class LorenzVec( fun lengthSquared(): Double = x * x + y * y + z * z fun length(): Double = sqrt(this.lengthSquared()) + fun isNormalized(tolerance: Double = 0.01) = (lengthSquared() - 1.0).absoluteValue < tolerance + fun isZero(): Boolean = x == 0.0 && y == 0.0 && z == 0.0 fun clone(): LorenzVec = LorenzVec(x, y, z) diff --git a/src/main/java/at/hannibal2/skyhanni/utils/RaycastUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/RaycastUtils.kt new file mode 100644 index 000000000000..d5aafdcd3c8c --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/RaycastUtils.kt @@ -0,0 +1,73 @@ +package at.hannibal2.skyhanni.utils + +import net.minecraft.client.Minecraft + +object RaycastUtils { + + data class Ray( + val origin: LorenzVec, + val direction: LorenzVec, + ) { + init { + require(direction.isNormalized()) + } + } + + data class Plane( + val origin: LorenzVec, + val normal: LorenzVec, + ) { + init { + require(normal.isNormalized()) + } + } + + fun createPlayerLookDirectionRay(): Ray { + return Ray( + LocationUtils.playerEyeLocation(), + Minecraft.getMinecraft().thePlayer.lookVec.toLorenzVec() + ) + } + + /** + * Create a plane that contains [point] and is orthogonal to [ray]. + */ + fun createOrthogonalPlaneToRayAtPoint( + ray: Ray, + point: LorenzVec, + ): Plane { + return Plane(point, ray.direction) + } + + /** + * Intersect a plane (of any orientation) with a ray. The ray and plane may not be parallel to each other. + */ + fun intersectPlaneWithRay(plane: Plane, ray: Ray): LorenzVec { +// require(plane.normal.dotProduct(ray.direction).absoluteValue != 0.0) + val intersectionPointDistanceAlongRay = + (plane.normal.dotProduct(plane.origin) - plane.normal.dotProduct(ray.origin)) / plane.normal.dotProduct(ray.direction) + return ray.origin + ray.direction.scale(intersectionPointDistanceAlongRay) + } + + /** + * Finds the distance between the given ray and the point. If the point is behind the ray origin (according to the ray's direction), + * returns [Double.MAX_VALUE] instead. + */ + fun findDistanceToRay(ray: Ray, point: LorenzVec): Double { + val plane = createOrthogonalPlaneToRayAtPoint(ray, point) + val intersectionPoint = intersectPlaneWithRay(plane, ray) + if ((intersectionPoint - ray.origin).dotProduct(ray.direction) < 0) return Double.MAX_VALUE + return intersectionPoint.distance(point) + } + + inline fun createDistanceToRayEstimator(ray: Ray, crossinline position: (T) -> LorenzVec): (T) -> Double { + return { + findDistanceToRay(ray, position(it)) + } + } + + fun List.findClosestPointToRay(ray: Ray, positionExtractor: (T) -> LorenzVec): T? { + return minByOrNull(createDistanceToRayEstimator(ray, positionExtractor)) + } + +} From ed91bbeaca46b3bbbf51cbeb208b14aa87de1cf6 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:38:27 +0200 Subject: [PATCH 039/102] misc changes --- src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt | 2 +- src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt b/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt index 0646b884cdec..15fcbe8a9a62 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt @@ -111,7 +111,7 @@ class TextInput { } if (KeyboardManager.isPastingKeysDown()) { runBlocking { - textBox = OSUtils.readFromClipboard() ?: return@runBlocking + textBox = OSUtils.readFromClipboard()?.take(2024) ?: return@runBlocking updated() } return diff --git a/src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt index ae0028b33f2b..ae7ec12de390 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/BlockUtils.kt @@ -41,8 +41,7 @@ object BlockUtils { } fun rayTrace(start: LorenzVec, direction: LorenzVec, distance: Double = 50.0): LorenzVec? { - val help = direction.normalize() * distance - val target = start + help + val target = start + direction.normalize() * distance val result = Minecraft.getMinecraft().theWorld.rayTraceBlocks(start.toVec3(), target.toVec3()) return result?.blockPos?.toLorenzVec() From 40288e4d66dd53ab6865ac08a90ea843c1bc0f62 Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Fri, 20 Sep 2024 22:48:17 +0200 Subject: [PATCH 040/102] Fix: Hardstone and Tungsten detection in Mineshafts (#2536) Co-authored-by: ItsEmpa --- .../at/hannibal2/skyhanni/data/MiningAPI.kt | 11 ++- .../features/mining/MineshaftPityDisplay.kt | 1 - .../mining/MiningCommissionsBlocksColor.kt | 1 - .../skyhanni/features/mining/OreBlock.kt | 76 +++++++++++----- .../skyhanni/features/mining/OreType.kt | 90 ++++++++++--------- 5 files changed, 113 insertions(+), 66 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt index fd399ae8b037..758294ffc580 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt @@ -8,7 +8,6 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.PlaySoundEvent import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent -import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.events.ServerBlockChangeEvent import at.hannibal2.skyhanni.events.mining.OreMinedEvent import at.hannibal2.skyhanni.events.player.PlayerDeathEvent @@ -59,6 +58,8 @@ object MiningAPI { private var waitingForEffMinerBlock = false var inGlacite = false + var inTunnels = false + var inMineshaft = false var inDwarvenMines = false var inCrystalHollows = false var inCrimsonIsle = false @@ -66,12 +67,13 @@ object MiningAPI { var inSpidersDen = false var currentAreaOreBlocks = setOf() + private set private var lastSkyblockArea: String? = null private val recentClickedBlocks = ConcurrentSet>() private val surroundingMinedBlocks = ConcurrentLinkedQueue>() - private val allowedSoundNames = listOf("dig.glass", "dig.stone", "dig.gravel", "dig.cloth", "random.orb") + private val allowedSoundNames = setOf("dig.glass", "dig.stone", "dig.gravel", "dig.cloth", "random.orb") var cold: Int = 0 private set @@ -117,6 +119,7 @@ object MiningAPI { fun onBlockClick(event: BlockClickEvent) { if (!inCustomMiningIsland()) return if (event.clickType != ClickType.LEFT_CLICK) return + //println(event.getBlockState.properties) if (OreBlock.getByStateOrNull(event.getBlockState) == null) return recentClickedBlocks += event.position to SimpleTimeMark.now() } @@ -142,6 +145,7 @@ object MiningAPI { fun onPlaySound(event: PlaySoundEvent) { if (!inCustomMiningIsland()) return if (event.soundName !in allowedSoundNames) return + //println("Sound: ${event.soundName} ${event.pitch} ${event.volume} ${event.location.toCleanString()}") if (waitingForInitSound) { if (event.soundName != "random.orb" && event.pitch == 0.7936508f) { val pos = event.location.roundLocationToBlock() @@ -176,6 +180,7 @@ object MiningAPI { val pos = event.location if (pos.distanceToPlayer() > 7) return + //println("Block change: $oldState -> $newState ${pos.toCleanString()}") if (lastInitSound.passedSince() > 100.milliseconds) return @@ -288,6 +293,8 @@ object MiningAPI { lastSkyblockArea = currentArea inGlacite = inGlaciteArea() + inTunnels = inGlacialTunnels() + inMineshaft = inMineshaft() inDwarvenMines = inRegularDwarven() inCrystalHollows = inCrystalHollows() inCrimsonIsle = IslandType.CRIMSON_ISLE.isInIsland() diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt index 3b89ae246bc2..8497951febb1 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt @@ -15,7 +15,6 @@ import at.hannibal2.skyhanni.events.mining.OreMinedEvent import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay.PityBlock.Companion.getPity import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay.PityBlock.Companion.getPityBlock import at.hannibal2.skyhanni.features.mining.OreType.Companion.getOreType -import at.hannibal2.skyhanni.features.mining.OreType.Companion.isGemstone import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.LorenzUtils.round diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt index bf7c8c2ac81f..5eaff2b44604 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt @@ -10,7 +10,6 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.TabListUpdateEvent -import at.hannibal2.skyhanni.features.mining.OreType.Companion.isGemstone import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt index d9b76a35be94..8acf4fc86f9f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt @@ -6,7 +6,9 @@ import at.hannibal2.skyhanni.data.MiningAPI.inCrystalHollows import at.hannibal2.skyhanni.data.MiningAPI.inDwarvenMines import at.hannibal2.skyhanni.data.MiningAPI.inEnd import at.hannibal2.skyhanni.data.MiningAPI.inGlacite +import at.hannibal2.skyhanni.data.MiningAPI.inMineshaft import at.hannibal2.skyhanni.data.MiningAPI.inSpidersDen +import at.hannibal2.skyhanni.data.MiningAPI.inTunnels import at.hannibal2.skyhanni.utils.CollectionUtils.equalsOneOf import net.minecraft.block.BlockColored import net.minecraft.block.BlockSand @@ -14,11 +16,13 @@ import net.minecraft.block.BlockSilverfish import net.minecraft.block.BlockStainedGlass import net.minecraft.block.BlockStainedGlassPane import net.minecraft.block.BlockStone +import net.minecraft.block.BlockStoneSlab import net.minecraft.block.BlockStoneSlabNew import net.minecraft.block.state.IBlockState import net.minecraft.init.Blocks import net.minecraft.item.EnumDyeColor + enum class OreBlock( val checkBlock: (IBlockState) -> Boolean, val checkArea: () -> Boolean, @@ -128,9 +132,13 @@ enum class OreBlock( checkBlock = ::isHardStoneHollows, checkArea = { inCrystalHollows }, ), - HARD_STONE_GLACIAL( - checkBlock = ::isHardstoneGlacite, - checkArea = { inGlacite }, + HARD_STONE_TUNNELS( + checkBlock = ::isHardstoneTunnels, + checkArea = { inTunnels }, + ), + HARD_STONE_MINESHAFT( + checkBlock = ::isHardstoneMineshaft, + checkArea = { inMineshaft }, ), // DWARVEN BLOCKS @@ -139,10 +147,12 @@ enum class OreBlock( checkArea = { inDwarvenMines || inCrystalHollows }, ), PURE_IRON( + // currently not detected checkBlock = { it.block == Blocks.iron_block }, checkArea = { inDwarvenMines || inCrystalHollows }, ), PURE_GOLD( + // currently not detected checkBlock = { it.block == Blocks.gold_block }, checkArea = { inDwarvenMines || inCrystalHollows }, ), @@ -151,14 +161,17 @@ enum class OreBlock( checkArea = { inDwarvenMines || inCrystalHollows }, ), PURE_REDSTONE( + // currently not detected checkBlock = { it.block == Blocks.redstone_block }, checkArea = { inDwarvenMines || inCrystalHollows }, ), PURE_EMERALD( + // currently not detected checkBlock = { it.block == Blocks.emerald_block }, checkArea = { inDwarvenMines || inCrystalHollows }, ), PURE_DIAMOND( + // currently not detected checkBlock = { it.block == Blocks.diamond_block }, checkArea = { inDwarvenMines || inCrystalHollows }, ), @@ -222,14 +235,20 @@ enum class OreBlock( checkBlock = ::isHighTierUmber, checkArea = { inGlacite }, ), - LOW_TIER_TUNGSTEN( - checkBlock = ::isLowTierTungsten, - checkArea = { inGlacite }, + + LOW_TIER_TUNGSTEN_TUNNELS( + checkBlock = ::isLowTierTungstenTunnels, + checkArea = { inTunnels }, + ), + LOW_TIER_TUNGSTEN_MINESHAFT( + checkBlock = ::isLowTierTungstenMineshaft, + checkArea = { inMineshaft }, ), HIGH_TIER_TUNGSTEN( checkBlock = { it.block == Blocks.clay }, checkArea = { inGlacite }, ), + GLACITE( checkBlock = { it.block == Blocks.packed_ice }, checkArea = { inGlacite }, @@ -261,33 +280,50 @@ private fun isStone(state: IBlockState): Boolean { private fun isHardStoneHollows(state: IBlockState): Boolean { return when (state.block) { - Blocks.wool -> (state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY) - Blocks.stained_hardened_clay -> (state.getValue(BlockColored.COLOR) == EnumDyeColor.CYAN) - Blocks.stone -> (state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.STONE) + Blocks.wool -> { + val color = state.getValue(BlockColored.COLOR) + color == EnumDyeColor.GRAY || color == EnumDyeColor.GREEN + } + Blocks.stained_hardened_clay -> when (state.getValue(BlockColored.COLOR)) { + EnumDyeColor.CYAN, EnumDyeColor.BROWN, EnumDyeColor.GRAY, EnumDyeColor.BLACK, + EnumDyeColor.LIME, EnumDyeColor.GREEN, EnumDyeColor.BLUE, EnumDyeColor.RED, + EnumDyeColor.SILVER -> true + else -> false + } + Blocks.clay, Blocks.stonebrick, Blocks.stone -> true else -> false } } -private fun isHardstoneGlacite(state: IBlockState): Boolean = +private fun isHardstoneTunnels(state: IBlockState): Boolean = (state.block == Blocks.monster_egg && state.getValue(BlockSilverfish.VARIANT) == BlockSilverfish.EnumType.STONE) || - (state.block == Blocks.wool && state.getValue(BlockColored.COLOR) == EnumDyeColor.GRAY) + (state.block == Blocks.wool && state.getValue(BlockColored.COLOR) == EnumDyeColor.SILVER) + +private fun isHardstoneMineshaft(state: IBlockState): Boolean = + (state.block == Blocks.stone && state.getValue(BlockStone.VARIANT) == BlockStone.EnumType.STONE) || + (state.block == Blocks.wool && state.getValue(BlockColored.COLOR) == EnumDyeColor.SILVER) private fun isRedSand(state: IBlockState): Boolean = (state.block == Blocks.sand && state.getValue(BlockSand.VARIANT) == BlockSand.EnumType.RED_SAND) private fun isLowTierUmber(state: IBlockState): Boolean = - state.block == Blocks.hardened_clay || (state.block == Blocks.stained_hardened_clay && state.getValue(BlockColored.COLOR) == EnumDyeColor.BROWN) + state.block == Blocks.hardened_clay || + (state.block == Blocks.stained_hardened_clay && state.getValue(BlockColored.COLOR) == EnumDyeColor.BROWN) private fun isHighTierUmber(state: IBlockState): Boolean = (state.block == Blocks.double_stone_slab2 && state.getValue(BlockStoneSlabNew.VARIANT) == BlockStoneSlabNew.EnumType.RED_SANDSTONE) -private fun isLowTierTungsten(state: IBlockState): Boolean = - state.block == Blocks.monster_egg && state.getValue(BlockSilverfish.VARIANT) == BlockSilverfish.EnumType.COBBLESTONE +private fun isLowTierTungstenTunnels(state: IBlockState): Boolean = + state.block == Blocks.monster_egg && state.getValue(BlockSilverfish.VARIANT) == BlockSilverfish.EnumType.STONE -private fun IBlockState.isGemstoneWithColor(color: EnumDyeColor): Boolean { - return when (this.block) { - Blocks.stained_glass -> color == this.getValue(BlockStainedGlass.COLOR) - Blocks.stained_glass_pane -> color == this.getValue(BlockStainedGlassPane.COLOR) - else -> false - } +private fun isLowTierTungstenMineshaft(state: IBlockState): Boolean = when (state.block) { + Blocks.stone_slab -> state.getValue(BlockStoneSlab.VARIANT) == BlockStoneSlab.EnumType.COBBLESTONE + Blocks.cobblestone, Blocks.stone_stairs -> true + else -> false +} + +private fun IBlockState.isGemstoneWithColor(color: EnumDyeColor): Boolean = when (block) { + Blocks.stained_glass -> color == getValue(BlockStainedGlass.COLOR) + Blocks.stained_glass_pane -> color == getValue(BlockStainedGlassPane.COLOR) + else -> false } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt index 5f9743b68a0e..66c12837114a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreType.kt @@ -7,187 +7,190 @@ import net.minecraft.block.state.IBlockState enum class OreType( val oreName: String, - private val internalNameString: String, - val oreBlocks: List, - val internalName: NEUInternalName = internalNameString.asInternalName(), + internalName: String, + vararg val oreBlocks: OreBlock, ) { MITHRIL( "Mithril", "MITHRIL_ORE", - listOf(OreBlock.LOW_TIER_MITHRIL, OreBlock.MID_TIER_MITHRIL, OreBlock.HIGH_TIER_MITHRIL), + OreBlock.LOW_TIER_MITHRIL, OreBlock.MID_TIER_MITHRIL, OreBlock.HIGH_TIER_MITHRIL, ), TITANIUM( "Titanium", "TITANIUM_ORE", - listOf(OreBlock.TITANIUM), + OreBlock.TITANIUM, ), COBBLESTONE( "Cobblestone", "COBBLESTONE", - listOf(OreBlock.STONE, OreBlock.COBBLESTONE), + OreBlock.STONE, OreBlock.COBBLESTONE, ), COAL( "Coal", "COAL", - listOf(OreBlock.COAL_ORE, OreBlock.PURE_COAL), + OreBlock.COAL_ORE, OreBlock.PURE_COAL, ), IRON( "Iron", "IRON_INGOT", - listOf(OreBlock.IRON_ORE, OreBlock.PURE_IRON), + OreBlock.IRON_ORE, OreBlock.PURE_IRON, ), GOLD( "Gold", "GOLD_INGOT", - listOf(OreBlock.GOLD_ORE, OreBlock.PURE_GOLD), + OreBlock.GOLD_ORE, OreBlock.PURE_GOLD, ), LAPIS( "Lapis Lazuli", "INK_SACK-4", - listOf(OreBlock.LAPIS_ORE, OreBlock.PURE_LAPIS), + OreBlock.LAPIS_ORE, OreBlock.PURE_LAPIS, ), REDSTONE( "Redstone", "REDSTONE", - listOf(OreBlock.REDSTONE_ORE, OreBlock.PURE_REDSTONE), + OreBlock.REDSTONE_ORE, OreBlock.PURE_REDSTONE, ), EMERALD( "Emerald", "EMERALD", - listOf(OreBlock.EMERALD_ORE, OreBlock.PURE_EMERALD), + OreBlock.EMERALD_ORE, OreBlock.PURE_EMERALD, ), DIAMOND( "Diamond", "DIAMOND", - listOf(OreBlock.DIAMOND_ORE, OreBlock.PURE_DIAMOND), + OreBlock.DIAMOND_ORE, OreBlock.PURE_DIAMOND, ), NETHERRACK( "Netherrack", "NETHERRACK", - listOf(OreBlock.NETHERRACK), + OreBlock.NETHERRACK, ), QUARTZ( "Nether Quartz", "QUARTZ", - listOf(OreBlock.QUARTZ_ORE), + OreBlock.QUARTZ_ORE, ), GLOWSTONE( "Glowstone", "GLOWSTONE_DUST", - listOf(OreBlock.GLOWSTONE), + OreBlock.GLOWSTONE, ), MYCELIUM( "Mycelium", "MYCEL", - listOf(OreBlock.MYCELIUM), + OreBlock.MYCELIUM, ), RED_SAND( "Red Sand", "SAND-1", - listOf(OreBlock.RED_SAND), + OreBlock.RED_SAND, ), SULPHUR( "Sulphur", "SULPHUR_ORE", - listOf(OreBlock.SULPHUR), + OreBlock.SULPHUR, ), GRAVEL( "Gravel", "GRAVEL", - listOf(OreBlock.GRAVEL), + OreBlock.GRAVEL, ), END_STONE( "End Stone", "ENDER_STONE", - listOf(OreBlock.END_STONE), + OreBlock.END_STONE, ), OBSIDIAN( "Obsidian", "OBSIDIAN", - listOf(OreBlock.OBSIDIAN), + OreBlock.OBSIDIAN, ), HARD_STONE( "Hard Stone", "HARD_STONE", - listOf(OreBlock.HARD_STONE_HOLLOWS, OreBlock.HARD_STONE_GLACIAL), + OreBlock.HARD_STONE_HOLLOWS, OreBlock.HARD_STONE_TUNNELS, OreBlock.HARD_STONE_MINESHAFT, ), RUBY( "Ruby", "ROUGH_RUBY_GEM", - listOf(OreBlock.RUBY), + OreBlock.RUBY, ), AMBER( "Amber", "ROUGH_AMBER_GEM", - listOf(OreBlock.AMBER), + OreBlock.AMBER, ), AMETHYST( "Amethyst", "ROUGH_AMETHYST_GEM", - listOf(OreBlock.AMETHYST), + OreBlock.AMETHYST, ), JADE( "Jade", "ROUGH_JADE_GEM", - listOf(OreBlock.JADE), + OreBlock.JADE, ), SAPPHIRE( "Sapphire", "ROUGH_SAPPHIRE_GEM", - listOf(OreBlock.SAPPHIRE), + OreBlock.SAPPHIRE, ), TOPAZ( "Topaz", "ROUGH_TOPAZ_GEM", - listOf(OreBlock.TOPAZ), + OreBlock.TOPAZ, ), JASPER( "Jasper", "ROUGH_JASPER_GEM", - listOf(OreBlock.JASPER), + OreBlock.JASPER, ), OPAL( "Opal", "ROUGH_OPAL_GEM", - listOf(OreBlock.OPAL), + OreBlock.OPAL, ), AQUAMARINE( "Aquamarine", "ROUGH_AQUAMARINE_GEM", - listOf(OreBlock.AQUAMARINE), + OreBlock.AQUAMARINE, ), CITRINE( "Citrine", "ROUGH_CITRINE_GEM", - listOf(OreBlock.CITRINE), + OreBlock.CITRINE, ), ONYX( "Onyx", "ROUGH_ONYX_GEM", - listOf(OreBlock.ONYX), + OreBlock.ONYX, ), PERIDOT( "Peridot", "ROUGH_PERIDOT_GEM", - listOf(OreBlock.PERIDOT), + OreBlock.PERIDOT, ), UMBER( "Umber", "UMBER", - listOf(OreBlock.LOW_TIER_UMBER, OreBlock.HIGH_TIER_UMBER), + OreBlock.LOW_TIER_UMBER, OreBlock.HIGH_TIER_UMBER, ), TUNGSTEN( "Tungsten", "TUNGSTEN", - listOf(OreBlock.LOW_TIER_TUNGSTEN, OreBlock.HIGH_TIER_TUNGSTEN), + OreBlock.LOW_TIER_TUNGSTEN_TUNNELS, OreBlock.LOW_TIER_TUNGSTEN_MINESHAFT, OreBlock.HIGH_TIER_TUNGSTEN, ), GLACITE( "Glacite", "GLACITE", - listOf(OreBlock.GLACITE), + OreBlock.GLACITE, ), ; + val internalName: NEUInternalName = internalName.asInternalName() + + fun isGemstone(): Boolean = this in gemstones + companion object { private val gemstones = setOf( @@ -197,12 +200,15 @@ enum class OreType( ) fun IBlockState.isOreType(oreType: OreType): Boolean { - return oreType.oreBlocks.intersect(MiningAPI.currentAreaOreBlocks) - .any { it.checkBlock.invoke(this) } + for (oreBlock in oreType.oreBlocks) { + if (oreBlock !in MiningAPI.currentAreaOreBlocks) continue + if (oreBlock.checkBlock(this)) { + return true + } + } + return false } - fun OreType.isGemstone(): Boolean = this in gemstones - fun OreBlock.getOreType(): OreType? { return OreType.entries.firstOrNull { this in it.oreBlocks } } From 8a7be2963b08d8f5f2ca6217545a9043f4792598 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Fri, 20 Sep 2024 23:06:17 +0200 Subject: [PATCH 041/102] fixed hardstone --- src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt index 8acf4fc86f9f..8e7cbf0821da 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt @@ -314,7 +314,7 @@ private fun isHighTierUmber(state: IBlockState): Boolean = (state.block == Blocks.double_stone_slab2 && state.getValue(BlockStoneSlabNew.VARIANT) == BlockStoneSlabNew.EnumType.RED_SANDSTONE) private fun isLowTierTungstenTunnels(state: IBlockState): Boolean = - state.block == Blocks.monster_egg && state.getValue(BlockSilverfish.VARIANT) == BlockSilverfish.EnumType.STONE + state.block == Blocks.monster_egg && state.getValue(BlockSilverfish.VARIANT) == BlockSilverfish.EnumType.COBBLESTONE private fun isLowTierTungstenMineshaft(state: IBlockState): Boolean = when (state.block) { Blocks.stone_slab -> state.getValue(BlockStoneSlab.VARIANT) == BlockStoneSlab.EnumType.COBBLESTONE From e026faba64c611d9b3cb6bde0337d59f02ffce63 Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:13:19 -0400 Subject: [PATCH 042/102] Feature: Hoppity Collection Dye Recolor (#2522) --- .../ChocolateFactoryConfig.java | 5 +++ .../event/hoppity/HoppityCollectionStats.kt | 32 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java index fa817ede042f..5d1c63f8d7e1 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/chocolatefactory/ChocolateFactoryConfig.java @@ -192,6 +192,11 @@ public class ChocolateFactoryConfig { HighlightRabbitTypes.STRAYS )); + @Expose + @ConfigOption(name = "Re-color Missing Rabbit Dyes", desc = "Replace the gray dye in Hoppity's Collection with a color for the rarity of the rabbit.") + @ConfigEditorBoolean + public boolean rarityDyeRecolor = true; + @Expose @ConfigOption( name = "Show Missing Location Rabbits", diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt index 800ac5729aaf..d513c3becfb6 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.GuiContainerEvent import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent +import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils @@ -15,8 +16,10 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.consumeWhile import at.hannibal2.skyhanni.utils.DisplayTableEntry import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.ItemUtils.setLore import at.hannibal2.skyhanni.utils.KSerializable import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzRarity import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUInternalName @@ -29,9 +32,11 @@ import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables +import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getMinecraftId import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraft.init.Items import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.util.regex.Pattern @@ -50,7 +55,7 @@ object HoppityCollectionStats { */ private val pagePattern by patternGroup.pattern( "page.current", - "(?:\\((?\\d+)\\/(?\\d+)\\) )?Hoppity's Collection", + "(?:\\((?\\d+)/(?\\d+)\\) )?Hoppity's Collection", ) private val duplicatesFoundPattern by patternGroup.pattern( "duplicates.found", @@ -182,6 +187,31 @@ object HoppityCollectionStats { strayRabbit to HighlightRabbitTypes.STRAYS, ) + @SubscribeEvent + fun replaceItem(event: ReplaceItemEvent) { + if (!config.rarityDyeRecolor || !pagePattern.matches(event.inventory.name)) return + val item = event.originalItem + // The "base" missing rabbits are Gray Dye (minecraft:dye with metadata 8) + if (item.getMinecraftId().toString() != "minecraft:dye" || item.metadata != 8) return + + val rarity = HoppityAPI.rarityByRabbit(item.displayName) + // Add NBT for the dye color itself + val newItemStack = ItemStack(Items.dye, 1, when (rarity) { + LorenzRarity.COMMON -> 7 // Light gray dye + LorenzRarity.UNCOMMON -> 10 // Lime dye + LorenzRarity.RARE -> 4 // Lapis lazuli + LorenzRarity.EPIC -> 5 // Purple dye + LorenzRarity.LEGENDARY -> 14 // Orange dye + LorenzRarity.MYTHIC -> 13 // Magenta dye + LorenzRarity.DIVINE -> 12 // Light blue dye + LorenzRarity.SPECIAL -> 1 // Rose Red - Covering bases for future (?) + else -> return + }) + newItemStack.setLore(item.getLore()) + newItemStack.setStackDisplayName(item.displayName) + event.replace(newItemStack) + } + @SubscribeEvent fun onInventoryOpen(event: InventoryFullyOpenedEvent) { if (!(LorenzUtils.inSkyBlock)) return From 62d45680f79c47479fe7d16a651eb8c7081586fe Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Fri, 20 Sep 2024 17:19:57 -0400 Subject: [PATCH 043/102] Improvement (?): Descriptive Milestones in Hoppity Collection (#2523) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../repo/HoppityEggLocationsJson.kt | 7 ++ .../event/hoppity/HoppityCollectionStats.kt | 85 ++++++++++++++----- .../chocolatefactory/ChocolateFactoryAPI.kt | 15 +++- 3 files changed, 84 insertions(+), 23 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt index 4ad81b06a663..ead4eb83c298 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/HoppityEggLocationsJson.kt @@ -23,6 +23,13 @@ data class HoppityEggLocationsJson( @Expose val maxRabbits: Int, @Expose val maxPrestige: Int, @Expose val chocolateMilestones: TreeSet, + @Expose val chocolateShopMilestones: List, + @Expose val chocolateFactoryMilestones: List, @Expose val apiEggLocations: Map>, @Expose val specialRabbits: List, ) + +data class MilestoneJson( + @Expose val amount: Long, + @Expose val rabbit: String, +) diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt index d513c3becfb6..3782919666dd 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt @@ -26,13 +26,13 @@ import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatInt +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.RegexUtils.anyMatches import at.hannibal2.skyhanni.utils.RegexUtils.findMatcher import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables -import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getMinecraftId import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern @@ -187,29 +187,14 @@ object HoppityCollectionStats { strayRabbit to HighlightRabbitTypes.STRAYS, ) + private fun missingRabbitStackNeedsFix(stack: ItemStack): Boolean = + stack.item == Items.dye && (stack.metadata == 8 || stack.getLore().any { it.lowercase().contains("milestone") }) + + private val replacementCache: MutableMap = mutableMapOf() + @SubscribeEvent fun replaceItem(event: ReplaceItemEvent) { - if (!config.rarityDyeRecolor || !pagePattern.matches(event.inventory.name)) return - val item = event.originalItem - // The "base" missing rabbits are Gray Dye (minecraft:dye with metadata 8) - if (item.getMinecraftId().toString() != "minecraft:dye" || item.metadata != 8) return - - val rarity = HoppityAPI.rarityByRabbit(item.displayName) - // Add NBT for the dye color itself - val newItemStack = ItemStack(Items.dye, 1, when (rarity) { - LorenzRarity.COMMON -> 7 // Light gray dye - LorenzRarity.UNCOMMON -> 10 // Lime dye - LorenzRarity.RARE -> 4 // Lapis lazuli - LorenzRarity.EPIC -> 5 // Purple dye - LorenzRarity.LEGENDARY -> 14 // Orange dye - LorenzRarity.MYTHIC -> 13 // Magenta dye - LorenzRarity.DIVINE -> 12 // Light blue dye - LorenzRarity.SPECIAL -> 1 // Rose Red - Covering bases for future (?) - else -> return - }) - newItemStack.setLore(item.getLore()) - newItemStack.setStackDisplayName(item.displayName) - event.replace(newItemStack) + replacementCache[event.originalItem.displayName]?.let { event.replace(it) } } @SubscribeEvent @@ -221,6 +206,29 @@ object HoppityCollectionStats { return } + event.inventoryItems.values.filter { it.hasDisplayName() && missingRabbitStackNeedsFix(it) }.forEach { stack -> + val rarity = HoppityAPI.rarityByRabbit(stack.displayName) + // Add NBT for the dye color itself + val newItemStack = if (config.rarityDyeRecolor) ItemStack( + Items.dye, 1, + when (rarity) { + LorenzRarity.COMMON -> 7 // Light gray dye + LorenzRarity.UNCOMMON -> 10 // Lime dye + LorenzRarity.RARE -> 4 // Lapis lazuli + LorenzRarity.EPIC -> 5 // Purple dye + LorenzRarity.LEGENDARY -> 14 // Orange dye + LorenzRarity.MYTHIC -> 13 // Magenta dye + LorenzRarity.DIVINE -> 12 // Light blue dye + LorenzRarity.SPECIAL -> 1 // Rose Red - Covering bases for future (?) + else -> return + }, + ) else ItemStack(Items.dye, 8) + + newItemStack.setLore(buildDescriptiveMilestoneLore(stack)) + newItemStack.setStackDisplayName(stack.displayName) + replacementCache[stack.displayName] = newItemStack + } + inInventory = true if (config.hoppityCollectionStats) { display = buildDisplay(event) @@ -231,6 +239,38 @@ object HoppityCollectionStats { } } + private fun buildDescriptiveMilestoneLore(itemStack: ItemStack): List { + val existingLore = itemStack.getLore().toMutableList() + var replaceIndex: Int? = null + var milestoneType: HoppityEggType = HoppityEggType.BREAKFAST + + if (factoryMilestone.anyMatches(existingLore)) { + milestoneType = HoppityEggType.CHOCOLATE_FACTORY_MILESTONE + replaceIndex = existingLore.indexOfFirst { loreMatch -> factoryMilestone.matches(loreMatch) } + } else if (shopMilestone.anyMatches(existingLore)) { + milestoneType = HoppityEggType.CHOCOLATE_SHOP_MILESTONE + replaceIndex = existingLore.indexOfFirst { loreMatch -> shopMilestone.matches(loreMatch) } + } + + replaceIndex?.let { + ChocolateFactoryAPI.milestoneByRabbit(itemStack.displayName)?.let { + val displayAmount = it.amount.shortFormat() + val operationFormat = when (milestoneType) { + HoppityEggType.CHOCOLATE_SHOP_MILESTONE -> "spending" + HoppityEggType.CHOCOLATE_FACTORY_MILESTONE -> "reaching" + else -> "" // Never happens + } + + //List indexing is weird + existingLore[replaceIndex - 1] = "§7Obtained by $operationFormat §6$displayAmount" + existingLore[replaceIndex] = "§7all-time §6Chocolate." + return existingLore + } + } + + return existingLore + } + private fun filterRabbitToHighlight(stack: ItemStack) { val lore = stack.getLore() @@ -259,6 +299,7 @@ object HoppityCollectionStats { fun onInventoryClose(event: InventoryCloseEvent) { inInventory = false display = emptyList() + replacementCache.clear() } @SubscribeEvent diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryAPI.kt index 40cd700c4eb4..112e8a7a7383 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryAPI.kt @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.config.features.inventory.chocolatefactory.Chocolat import at.hannibal2.skyhanni.config.storage.ProfileSpecificStorage.ChocolateFactoryStorage import at.hannibal2.skyhanni.data.ProfileStorageData import at.hannibal2.skyhanni.data.jsonobjects.repo.HoppityEggLocationsJson +import at.hannibal2.skyhanni.data.jsonobjects.repo.MilestoneJson import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent import at.hannibal2.skyhanni.events.RepositoryReloadEvent import at.hannibal2.skyhanni.features.event.hoppity.HoppityCollectionStats @@ -80,7 +81,9 @@ object ChocolateFactoryAPI { var shrineIndex = 41 var coachRabbitIndex = 42 var maxRabbits = 395 - var chocolateMilestones = TreeSet() + private var chocolateMilestones = TreeSet() + private var chocolateFactoryMilestones: MutableList = mutableListOf() + private var chocolateShopMilestones: MutableList = mutableListOf() private var maxPrestige = 5 var inChocolateFactory = false @@ -143,6 +146,8 @@ object ChocolateFactoryAPI { maxRabbits = data.maxRabbits maxPrestige = data.maxPrestige chocolateMilestones = data.chocolateMilestones + chocolateFactoryMilestones = data.chocolateFactoryMilestones.toMutableList() + chocolateShopMilestones = data.chocolateShopMilestones.toMutableList() specialRabbitTextures = data.specialRabbits ChocolateFactoryUpgrade.updateIgnoredSlots() @@ -226,4 +231,12 @@ object ChocolateFactoryAPI { val basePerSecond = rawChocolatePerSecond * baseMultiplier return (needed / basePerSecond + secondsUntilTowerExpires).seconds } + + fun milestoneByRabbit(rabbitName: String): MilestoneJson? { + return chocolateFactoryMilestones.firstOrNull { + it.rabbit.removeColor() == rabbitName.removeColor() + } ?: chocolateShopMilestones.firstOrNull { + it.rabbit.removeColor() == rabbitName.removeColor() + } + } } From 19f23335cc4852e8f9c7d56036e7e89c52d230fe Mon Sep 17 00:00:00 2001 From: ILike2WatchMemes Date: Fri, 20 Sep 2024 23:32:10 +0200 Subject: [PATCH 044/102] Feature: Inventory - Experiments rework (#2171) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> --- .../skyhanni/config/ConfigUpdaterMigrator.kt | 2 +- .../skyhanni/config/commands/Commands.kt | 5 + .../features/inventory/InventoryConfig.java | 5 + .../ExperimentationTableConfig.java | 50 ++++ .../ExperimentsDryStreakConfig.java | 31 ++ .../ExperimentsProfitTrackerConfig.java | 37 +++ .../inventory/helper/HelperConfig.java | 26 -- .../storage/ProfileSpecificStorage.java | 24 ++ .../ExperimentationTableAPI.kt | 157 ++++++++++ .../ExperimentationTableEnums.kt | 28 ++ .../ExperimentsDryStreakDisplay.kt | 113 ++++++++ .../ExperimentsProfitTracker.kt | 258 +++++++++++++++++ .../GuardianReminder.kt | 32 +-- .../SuperpairExperimentInformationDisplay.kt | 272 ++++++++++++++++++ .../SuperpairsClicksAlert.kt | 6 +- .../UltraRareBookAlert.kt | 39 +-- 16 files changed, 1011 insertions(+), 74 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentationTableConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsDryStreakConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsProfitTrackerConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableAPI.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableEnums.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsDryStreakDisplay.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt rename src/main/java/at/hannibal2/skyhanni/features/inventory/{experiments => experimentationtable}/GuardianReminder.kt (78%) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt rename src/main/java/at/hannibal2/skyhanni/features/inventory/{ => experimentationtable}/SuperpairsClicksAlert.kt (91%) rename src/main/java/at/hannibal2/skyhanni/features/inventory/{experiments => experimentationtable}/UltraRareBookAlert.kt (75%) diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt index 8eb47d3949b2..b9f21ca2255c 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt @@ -12,7 +12,7 @@ import com.google.gson.JsonPrimitive object ConfigUpdaterMigrator { val logger = LorenzLogger("ConfigMigration") - const val CONFIG_VERSION = 58 + const val CONFIG_VERSION = 59 fun JsonElement.at(chain: List, init: Boolean): JsonElement? { if (chain.isEmpty()) return this if (this !is JsonObject) return null 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 3f2ddace62d2..9b85c2cf9590 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -57,6 +57,7 @@ import at.hannibal2.skyhanni.features.garden.pests.PestFinder import at.hannibal2.skyhanni.features.garden.pests.PestProfitTracker import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorDropStatistics import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentsProfitTracker import at.hannibal2.skyhanni.features.mining.KingTalismanHelper import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker @@ -276,6 +277,10 @@ object Commands { "shresetpestprofittracker", "Resets the Pest Profit Tracker", ) { PestProfitTracker.resetCommand() } + registerCommand( + "shresetexperimentsprofittracker", + "Resets the Experiments Profit Tracker", + ) { ExperimentsProfitTracker.resetCommand() } registerCommand( "shresetmythologicalcreaturetracker", "Resets the Mythological Creature Tracker", diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java index 5c3bf3170920..062f29b30e33 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java @@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.config.HasLegacyId; import at.hannibal2.skyhanni.config.features.inventory.chocolatefactory.ChocolateFactoryConfig; import at.hannibal2.skyhanni.config.features.inventory.customwardrobe.CustomWardrobeConfig; +import at.hannibal2.skyhanni.config.features.inventory.experimentationtable.ExperimentationTableConfig; import at.hannibal2.skyhanni.config.features.inventory.helper.HelperConfig; import at.hannibal2.skyhanni.config.features.itemability.ItemAbilityConfig; import at.hannibal2.skyhanni.config.features.misc.EstimatedItemValueConfig; @@ -38,6 +39,10 @@ public class InventoryConfig { @Category(name = "Bazaar", desc = "Be smart when buying or selling many items in the Bazaar.") public BazaarConfig bazaar = new BazaarConfig(); + @Expose + @Category(name = "Experimentation Table", desc = "QOL features for the Experimentation Table.") + public ExperimentationTableConfig experimentationTable = new ExperimentationTableConfig(); + @Expose @Category(name = "Enchant Parsing", desc = "Settings for SkyHanni's Enchant Parsing") public EnchantParsingConfig enchantParsing = new EnchantParsingConfig(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentationTableConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentationTableConfig.java new file mode 100644 index 000000000000..dfdad248256e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentationTableConfig.java @@ -0,0 +1,50 @@ +package at.hannibal2.skyhanni.config.features.inventory.experimentationtable; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.Accordion; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +public class ExperimentationTableConfig { + + @Expose + @ConfigOption(name = "Profit Tracker", desc = "") + @Accordion + public ExperimentsProfitTrackerConfig experimentsProfitTracker = new ExperimentsProfitTrackerConfig(); + + @Expose + @ConfigOption(name = "Dry-Streak Display", desc = "") + @Accordion + public ExperimentsDryStreakConfig dryStreak = new ExperimentsDryStreakConfig(); + + @Expose + @ConfigOption(name = "Superpair Data", desc = "Shows a display with useful information while doing the Superpair experiment.") + @ConfigEditorBoolean + @FeatureToggle + public boolean superpairDisplay = false; + + @Expose + @ConfigLink(owner = ExperimentationTableConfig.class, field = "superpairDisplay") + public Position superpairDisplayPosition = new Position(-372, 161, false, true); + + @Expose + @ConfigOption(name = "Superpairs Clicks Alert", desc = "Display an alert when you reach the maximum clicks gained from Chronomatron or Ultrasequencer.") + @ConfigEditorBoolean + @FeatureToggle + public boolean superpairsClicksAlert = false; + + @Expose + @ConfigOption(name = "ULTRA-RARE Book Alert", desc = "Send a chat message, title and sound when you find an ULTRA-RARE book.") + @ConfigEditorBoolean + @FeatureToggle + public boolean ultraRareBookAlert = false; + + @Expose + @ConfigOption(name = "Guardian Reminder", desc = "Sends a warning when opening the Experimentation Table without a §9§lGuardian Pet §7equipped.") + @ConfigEditorBoolean + @FeatureToggle + public boolean guardianReminder = false; +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsDryStreakConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsDryStreakConfig.java new file mode 100644 index 000000000000..dfba626a764f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsDryStreakConfig.java @@ -0,0 +1,31 @@ +package at.hannibal2.skyhanni.config.features.inventory.experimentationtable; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +public class ExperimentsDryStreakConfig { + + @Expose + @ConfigOption(name = "Enabled", desc = "Display attempts and or XP since your last ULTRA-RARE.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption(name = "Attempts", desc = "Display Attempts since.") + @ConfigEditorBoolean + public boolean attemptsSince = true; + + @Expose + @ConfigOption(name = "XP", desc = "Display XP since.") + @ConfigEditorBoolean + public boolean xpSince = true; + + @Expose + @ConfigLink(owner = ExperimentsDryStreakConfig.class, field = "enabled") + public Position position = new Position(200, -187, false, true); +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsProfitTrackerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsProfitTrackerConfig.java new file mode 100644 index 000000000000..62ee4f5407ba --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/experimentationtable/ExperimentsProfitTrackerConfig.java @@ -0,0 +1,37 @@ +package at.hannibal2.skyhanni.config.features.inventory.experimentationtable; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import at.hannibal2.skyhanni.config.core.config.Position; +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentMessages; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +import java.util.ArrayList; +import java.util.List; + +public class ExperimentsProfitTrackerConfig { + + @Expose + @ConfigOption(name = "Enabled", desc = "Tracker for drops/XP you get from experiments.") + @ConfigEditorBoolean + @FeatureToggle + public boolean enabled = false; + + @Expose + @ConfigOption(name = "Hide Messages", desc = "Change the messages to be hidden after completing Add-on/Main experiments.") + @ConfigEditorDraggableList + public List hideMessages = new ArrayList<>(); + + @Expose + @ConfigOption(name = "Time displayed", desc = "Time displayed after completing an experiment.") + @ConfigEditorSlider(minValue = 5, maxValue = 60, minStep = 1) + public int timeDisplayed = 30; + + @Expose + @ConfigLink(owner = ExperimentsProfitTrackerConfig.class, field = "enabled") + public Position position = new Position(20, 20, false, true); +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java index 5f54e707d010..717dd76591de 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java @@ -56,30 +56,4 @@ public static class HarpConfig { @ConfigOption(name = "Reforge Helper", desc = "") @Accordion public ReforgeHelperConfig reforge = new ReforgeHelperConfig(); - - @Expose - @ConfigOption(name = "Enchanting", desc = "") - @Accordion - public EnchantingConfig enchanting = new EnchantingConfig(); - - public static class EnchantingConfig { - @Expose - @ConfigOption(name = "Superpairs Clicks Alert", desc = "Display an alert when you reach the maximum clicks gained from Chronomatron or Ultrasequencer.") - @ConfigEditorBoolean - @FeatureToggle - public boolean superpairsClicksAlert = false; - - @Expose - @ConfigOption(name = "ULTRA-RARE Book Alert", desc = "Send a chat message, title and sound when you find an ULTRA-RARE book.") - @ConfigEditorBoolean - @FeatureToggle - public boolean ultraRareBookAlert = false; - - @Expose - @ConfigOption(name = "Guardian Reminder", desc = "Sends a warning when opening the Experimentation Table without a §9§lGuardian Pet §7equipped.") - @ConfigEditorBoolean - @FeatureToggle - public boolean guardianReminder = false; - } - } diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java index 83e7ea47c254..754eea3bba59 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java +++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java @@ -31,6 +31,7 @@ import at.hannibal2.skyhanni.features.garden.pests.VinylType; import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward; import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker; +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentsProfitTracker; import at.hannibal2.skyhanni.features.inventory.wardrobe.WardrobeAPI; import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay; import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker; @@ -65,6 +66,29 @@ private static SimpleTimeMark SimpleTimeMarkFarPast() { @Expose public String currentPet = ""; + @Expose + public ExperimentationStorage experimentation = new ExperimentationStorage(); + + public static class ExperimentationStorage { + + @Expose + public LorenzVec tablePos = new LorenzVec(); + + @Expose + public ExperimentsDryStreakStorage dryStreak = new ExperimentsDryStreakStorage(); + + public static class ExperimentsDryStreakStorage { + @Expose + public int attemptsSince = 0; + + @Expose + public int xpSince = 0; + } + + @Expose + public ExperimentsProfitTracker.Data experimentsProfitTracker = new ExperimentsProfitTracker.Data(); + } + @Expose public ChocolateFactoryStorage chocolateFactory = new ChocolateFactoryStorage(); diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableAPI.kt new file mode 100644 index 000000000000..80433b68e665 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableAPI.kt @@ -0,0 +1,157 @@ +package at.hannibal2.skyhanni.features.inventory.experimentationtable + +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.events.InventoryUpdatedEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.EntityUtils +import at.hannibal2.skyhanni.utils.EntityUtils.hasSkullTexture +import at.hannibal2.skyhanni.utils.InventoryUtils.openInventoryName +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.getLorenzVec +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraft.entity.item.EntityArmorStand +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object ExperimentationTableAPI { + + private val storage get() = ProfileStorageData.profileSpecific?.experimentation + + val inTable get() = inventoriesPattern.matches(openInventoryName()) + + fun inDistanceToTable(vec: LorenzVec, max: Double): Boolean = + storage?.tablePos?.let { it.distance(vec) <= max } ?: false + + fun getCurrentExperiment(): Experiment? = + superpairsPattern.matchMatcher(openInventoryName()) { + Experiment.entries.find { it.nameString == group("experiment") } + } + + @SubscribeEvent + fun onInventoryUpdated(event: InventoryUpdatedEvent) { + if (LorenzUtils.skyBlockIsland != IslandType.PRIVATE_ISLAND || !inTable) return + + val entity = EntityUtils.getEntities().find { it.hasSkullTexture(experimentationTableSkull) } ?: return + val vec = entity.getLorenzVec() + if (storage?.tablePos != vec) storage?.tablePos = vec + } + + private val experimentationTableSkull = + "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTUyOWF" + + "iYzg4MzA5NTNmNGQ5MWVkZmZmMjQ2OTVhOWY2Mjc1OGZhNGM1MWIyOWFjMjQ2YzM3NDllYWFlODliMyJ9fX0=" + + private val patternGroup = RepoPattern.group("enchanting.experiments") + + /** + * REGEX-TEST: Superpairs (Metaphysical) + */ + private val superpairsPattern by patternGroup.pattern( + "superpairs", + "Superpairs \\((?\\w+)\\)", + ) + + /** + * REGEX-TEST: Gained +3 Clicks + */ + val powerUpPattern by patternGroup.pattern( + "powerups", + "Gained \\+\\d Clicks?|Instant Find|\\+\\S* XP", + ) + + /** + * REGEX-TEST: 123k Enchanting Exp + * REGEX-TEST: Titanic Experience Bottle + */ + val rewardPattern by patternGroup.pattern( + "rewards", + "\\d{1,3}k Enchanting Exp|Enchanted Book|(?:Titanic |Grand |\\b)Experience Bottle|Metaphysical Serum|Experiment The Fish", + ) + + /** + * REGEX-TEST: Superpairs (Metaphysical) + * REGEX-TEST: Chronomatron (Metaphysical) + */ + val inventoriesPattern by patternGroup.pattern( + "inventories", + "(?:Superpairs|Chronomatron|Ultrasequencer) (?:\\(.+\\)|➜ Stakes|Rewards)|Experimentation Table", + ) + + /** + * REGEX-TEST: +42,000 Enchanting Exp + */ + val enchantingExpChatPattern by patternGroup.pattern( + "chatexp", + "^ \\+(?\\d+|\\d+,\\d+)k? Enchanting Exp$", + ) + + /** + * REGEX-TEST: +Smite VII + * REGEX-TEST: +42,000 Enchanting Exp + */ + val experimentsDropPattern by patternGroup.pattern( + "drop", + "^ \\+(?.*)\$", + ) + + /** + * REGEX-TEST: You claimed the Superpairs rewards! + */ + val claimMessagePattern by patternGroup.pattern( + "claim", + "You claimed the \\S+ rewards!", + ) + + /** + * REGEX-TEST: 131k Enchanting Exp + * REGEX-TEST: 42,000 Enchanting Exp + */ + val enchantingExpPattern by patternGroup.pattern( + "exp", + "(?\\d+|\\d+,\\d+)k? Enchanting Exp", + ) + + /** + * REGEX-TEST: Titanic Experience Bottle + */ + val experienceBottlePattern by patternGroup.pattern( + "xpbottle", + "(?:Titanic |Grand |\\b)Experience Bottle", + ) + + /** + * REGEX-TEST: ☕ You renewed the experiment table! (1/3) + */ + val experimentRenewPattern by patternGroup.pattern( + "renew", + "^☕ You renewed the experiment table! \\((?\\d)/3\\)$", + ) + + /** + * REGEX-TEST: §d§kXX§5 ULTRA-RARE BOOK! §d§kXX + */ + val ultraRarePattern by patternGroup.pattern( + "ultrarare", + "§d§kXX§5 ULTRA-RARE BOOK! §d§kXX", + ) + + /** + * REGEX-TEST: §9Smite VII + */ + val bookPattern by patternGroup.pattern( + "book", + "§9(?.*)", + ) + + /** + * REGEX-TEST: §dGuardian + * REGEX-TEST: §9Guardian§e + */ + val petNamePattern by patternGroup.pattern( + "guardianpet", + "§[956d]Guardian.*", + ) +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableEnums.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableEnums.kt new file mode 100644 index 000000000000..0071d3af35fa --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentationTableEnums.kt @@ -0,0 +1,28 @@ +package at.hannibal2.skyhanni.features.inventory.experimentationtable + +enum class ExperimentMessages(private val str: String) { + DONE("§eYou claimed the §dSuperpairs §erewards! §8(§7Claim§8)"), + EXPERIENCE("§8 +§3141k Experience §8(§7Experience Drops§8)"), + ENCHANTMENTS("§8 +§9Smite VII §8(§7Enchantment Drops§8)"), + BOTTLES("§8 +§9Titanic Experience Bottle §8(§7Bottle Drops§8)"), + MISC("§8 +§5Metaphysical Serum §8(§7Misc Drops§8)"); + + override fun toString(): String { + return str + } +} + +enum class Experiment(val nameString: String, val gridSize: Int, val startSlot: Int, val endSlot: Int, val sideSpace: Int) { + NONE("", 0, 0, 0, 0), + BEGINNER("Beginner", 14, 18, 35, 1), + HIGH("High", 20, 10, 43, 2), + GRAND("Grand", 20, 10, 43, 2), + SUPREME("Supreme", 28, 9, 44, 1), + TRANSCENDENT("Transcendent", 28, 9, 44, 1), + METAPHYSICAL("Metaphysical", 28, 9, 44, 1), + ; + + override fun toString(): String { + return nameString + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsDryStreakDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsDryStreakDisplay.kt new file mode 100644 index 000000000000..50d033842792 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsDryStreakDisplay.kt @@ -0,0 +1,113 @@ +package at.hannibal2.skyhanni.features.inventory.experimentationtable + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryOpenEvent +import at.hannibal2.skyhanni.events.InventoryUpdatedEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.bookPattern +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.ultraRarePattern +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object ExperimentsDryStreakDisplay { + + private val config get() = SkyHanniMod.feature.inventory.experimentationTable.dryStreak + private val storage get() = ProfileStorageData.profileSpecific?.experimentation?.dryStreak + + private var display = emptyList() + + private var didJustFind = false + + @SubscribeEvent + fun onChestGuiOverlayRendered(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { + if (!isEnabled()) return + if (!ExperimentationTableAPI.inventoriesPattern.matches(InventoryUtils.openInventoryName())) return + + display = drawDisplay() + config.position.renderStrings( + display, + posLabel = "Experimentation Table Dry Streak", + ) + } + + @SubscribeEvent + fun onInventoryOpen(event: InventoryOpenEvent) { + if (event.inventoryName == "Experimentation Table" && didJustFind) didJustFind = false + } + + @SubscribeEvent + fun onInventoryUpdated(event: InventoryUpdatedEvent) { + if (!isEnabled() || didJustFind || ExperimentationTableAPI.getCurrentExperiment() == null) return + + for (lore in event.inventoryItems.map { it.value.getLore() }) { + val firstLine = lore.firstOrNull() ?: continue + if (!ultraRarePattern.matches(firstLine)) continue + val bookNameLine = lore.getOrNull(2) ?: continue + bookPattern.matchMatcher(bookNameLine) { + val storage = storage ?: return + ChatUtils.chat( + "§a§lDRY-STREAK ENDED! §eYou have (finally) " + + "found a §5ULTRA-RARE §eafter §3${storage.xpSince.shortFormat()} Enchanting Exp " + + "§e and §2${storage.attemptsSince} attempts§e!", + ) + storage.attemptsSince = 0 + storage.xpSince = 0 + didJustFind = true + } + } + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + if (didJustFind || ExperimentationTableAPI.getCurrentExperiment() == null) return + + val storage = storage ?: return + storage.attemptsSince += 1 + } + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!isEnabled() || didJustFind) return + + ExperimentationTableAPI.enchantingExpChatPattern.matchMatcher(event.message.removeColor()) { + val storage = storage ?: return + storage.xpSince += group("amount").substringBefore(",").toInt() * 1000 + } + } + + private fun drawDisplay() = buildList { + val storage = storage ?: return@buildList + + add("§cDry-Streak since last §5ULTRA-RARE") + + val colorPrefix = "§e" + val attemptsSince = storage.attemptsSince + val xpSince = storage.xpSince.shortFormat() + val attemptsSuffix = if (attemptsSince == 1) "" else "s" + + if (config.attemptsSince && config.xpSince) { + add("$colorPrefix ├ $attemptsSince Attempt$attemptsSuffix") + add("$colorPrefix └ $xpSince XP") + } else if (config.attemptsSince) { + add("$colorPrefix └ $attemptsSince Attempt$attemptsSuffix") + } else { + add("$colorPrefix └ $xpSince XP") + } + } + + private fun isEnabled() = + LorenzUtils.inSkyBlock && config.enabled && (config.xpSince || config.attemptsSince) +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt new file mode 100644 index 000000000000..16c0305cbedd --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt @@ -0,0 +1,258 @@ +package at.hannibal2.skyhanni.features.inventory.experimentationtable + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.ClickType +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryUpdatedEvent +import at.hannibal2.skyhanni.events.IslandChangeEvent +import at.hannibal2.skyhanni.events.ItemClickEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.claimMessagePattern +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.enchantingExpPattern +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.experienceBottlePattern +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.experimentRenewPattern +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.experimentsDropPattern +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.inventoriesPattern +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut +import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemPriceUtils.getNpcPriceOrNull +import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.renderables.Searchable +import at.hannibal2.skyhanni.utils.renderables.toSearchable +import at.hannibal2.skyhanni.utils.tracker.ItemTrackerData +import at.hannibal2.skyhanni.utils.tracker.SkyHanniItemTracker +import com.google.gson.annotations.Expose +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.math.absoluteValue + +@SkyHanniModule +object ExperimentsProfitTracker { + + private val config get() = SkyHanniMod.feature.inventory.experimentationTable.experimentsProfitTracker + + private val tracker = SkyHanniItemTracker( + "Experiments Profit Tracker", + { Data() }, + { it.experimentation.experimentsProfitTracker }, + ) { drawDisplay(it) } + + private var lastSplashes = mutableListOf() + private var lastSplashTime = SimpleTimeMark.farPast() + private var lastBottlesInInventory = mutableMapOf() + private var currentBottlesInInventory = mutableMapOf() + + class Data : ItemTrackerData() { + override fun resetItems() { + experimentsDone = 0L + xpGained = 0L + bitCost = 0L + startCost = 0L + } + + override fun getDescription(timesGained: Long): List { + val percentage = timesGained.toDouble() / experimentsDone + val dropRate = LorenzUtils.formatPercentage(percentage.coerceAtMost(1.0)) + return listOf( + "§7Dropped §e${timesGained.addSeparators()} §7times.", + "§7Your drop rate: §c$dropRate.", + ) + } + + override fun getCoinName(item: TrackedItem) = "" + + override fun getCoinDescription(item: TrackedItem) = listOf() + + @Expose + var experimentsDone = 0L + + @Expose + var xpGained = 0L + + @Expose + var bitCost = 0L + + @Expose + var startCost = 0L + } + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!isEnabled()) return + + val message = event.message.removeColor() + if (claimMessagePattern.matches(message) && ExperimentMessages.DONE.isSelected()) + event.blockedReason = "CLAIM_MESSAGE" + + experimentsDropPattern.matchMatcher(message) { + val reward = group("reward") + + event.blockedReason = when { + enchantingExpPattern.matches(reward) && ExperimentMessages.EXPERIENCE.isSelected() -> "EXPERIENCE_DROP" + experienceBottlePattern.matches(reward) && ExperimentMessages.BOTTLES.isSelected() -> "BOTTLE_DROP" + listOf("Metaphysical Serum", "Experiment The Fish").contains(reward) && ExperimentMessages.MISC.isSelected() -> "MISC_DROP" + ExperimentMessages.ENCHANTMENTS.isSelected() -> "ENCHANT_DROP" + else -> "" + } + + enchantingExpPattern.matchMatcher(reward) { + tracker.modify { + it.xpGained += group("amount").substringBefore(",").toInt() * 1000 + } + return + } + + val internalName = NEUInternalName.fromItemNameOrNull(reward) ?: return + if (!experienceBottlePattern.matches(group("reward"))) tracker.addItem(internalName, 1, false) + return + } + + experimentRenewPattern.matchMatcher(message) { + val increments = mapOf(1 to 150, 2 to 300, 3 to 500) + tracker.modify { + it.bitCost += increments.getValue(group("current").toInt()) + } + } + } + + @SubscribeEvent + fun onItemClick(event: ItemClickEvent) { + if (event.clickType == ClickType.RIGHT_CLICK) { + val item = event.itemInHand ?: return + if (experienceBottlePattern.matches(item.displayName.removeColor())) { + lastSplashTime = SimpleTimeMark.now() + lastSplashes.add(item) + } + } + } + + @SubscribeEvent + fun onInventoryUpdated(event: InventoryUpdatedEvent) { + if (!isEnabled()) return + + if (inventoriesPattern.matches(event.inventoryName)) { + var startCostTemp = 0 + val iterator = lastSplashes.iterator() + while (iterator.hasNext()) { + val item = iterator.next() + val internalName = item.getInternalName() + val price = internalName.getPrice() + val npcPrice = internalName.getNpcPriceOrNull() ?: 0.0 + val maxPrice = npcPrice.coerceAtLeast(price) + startCostTemp += maxPrice.round(0).toInt() + iterator.remove() + } + tracker.modify { + it.startCost -= startCostTemp + } + lastSplashTime = SimpleTimeMark.farPast() + } + + handleExpBottles(false) + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + if (!isEnabled()) return + + if (ExperimentationTableAPI.getCurrentExperiment() != null) { + tracker.modify { + it.experimentsDone++ + } + } + if (ExperimentationTableAPI.inTable && InventoryUtils.openInventoryName() == "Superpairs Rewards") { + handleExpBottles(true) + } + } + + private fun drawDisplay(data: Data): List = buildList { + addSearchString("§e§lExperiments Profit Tracker") + val profit = tracker.drawItems(data, { true }, this) + data.startCost + + val experimentsDone = data.experimentsDone + addSearchString("") + addSearchString("§eExperiments Done: §a${experimentsDone.addSeparators()}") + val startCostFormat = data.startCost.absoluteValue.shortFormat() + val bitCostFormat = data.bitCost.shortFormat() + add( + Renderable.hoverTips( + "§eTotal Cost: §c-$startCostFormat§e/§b-$bitCostFormat", + listOf( + "§7You paid §c$startCostFormat §7coins and", "§b$bitCostFormat §7bits for starting", + "§7experiments.", + ), + ).toSearchable(), + ) + add(tracker.addTotalProfit(profit, data.experimentsDone, "experiment")) + addSearchString("§eTotal Enchanting Exp: §b${data.xpGained.shortFormat()}") + + tracker.addPriceFromButton(this) + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent) { + if (!isEnabled()) return + + tracker.renderDisplay(config.position) + } + + @SubscribeEvent + fun onIslandChange(event: IslandChangeEvent) { + if (event.newIsland == IslandType.PRIVATE_ISLAND) { + tracker.firstUpdate() + } + } + + fun resetCommand() { + tracker.resetCommand() + } + + private fun handleExpBottles(addToTracker: Boolean) { + for (item in InventoryUtils.getItemsInOwnInventory()) { + val internalName = item.getInternalNameOrNull() ?: continue + if (internalName.asString() !in listOf("EXP_BOTTLE", "GRAND_EXP_BOTTLE", "TITANIC_EXP_BOTTLE")) continue + currentBottlesInInventory.addOrPut(internalName, item.stackSize) + } + for ((internalName, amount) in currentBottlesInInventory) { + val lastInInv = lastBottlesInInventory.getOrDefault(internalName, 0) + if (lastInInv >= amount) { + currentBottlesInInventory[internalName] = 0 + lastBottlesInInventory[internalName] = amount + continue + } + if (lastInInv == 0) { + currentBottlesInInventory[internalName] = 0 + lastBottlesInInventory[internalName] = amount + if (addToTracker) tracker.addItem(internalName, amount, false) + continue + } + + currentBottlesInInventory[internalName] = 0 + lastBottlesInInventory[internalName] = amount + if (addToTracker) tracker.addItem(internalName, amount - lastInInv, false) + } + } + + private fun ExperimentMessages.isSelected() = config.hideMessages.contains(this) + + private fun isEnabled() = + LorenzUtils.inSkyBlock && config.enabled + && ExperimentationTableAPI.inDistanceToTable(LorenzVec.getBlockBelowPlayer(), 5.0) +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experiments/GuardianReminder.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/GuardianReminder.kt similarity index 78% rename from src/main/java/at/hannibal2/skyhanni/features/inventory/experiments/GuardianReminder.kt rename to src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/GuardianReminder.kt index 9ae93f22717f..216cbf7c6ba8 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/experiments/GuardianReminder.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/GuardianReminder.kt @@ -1,6 +1,7 @@ -package at.hannibal2.skyhanni.features.inventory.experiments +package at.hannibal2.skyhanni.features.inventory.experimentationtable import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.data.PetAPI import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent @@ -16,7 +17,6 @@ import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.SoundUtils import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXYAligned -import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiContainer import net.minecraft.client.renderer.GlStateManager @@ -28,31 +28,16 @@ import kotlin.time.Duration.Companion.seconds @SkyHanniModule object GuardianReminder { - private val config get() = SkyHanniMod.feature.inventory.helper.enchanting + private val config get() = SkyHanniMod.feature.inventory.experimentationTable private var lastInventoryOpen = SimpleTimeMark.farPast() private var lastWarn = SimpleTimeMark.farPast() private var lastErrorSound = SimpleTimeMark.farPast() - private val patternGroup = RepoPattern.group("data.enchanting.inventory.experimentstable") - private val inventoryNamePattern by patternGroup.pattern( - "mainmenu", - "Experimentation Table", - ) - - /** - * REGEX-TEST: §dGuardian - * REGEX-TEST: §9Guardian§e - */ - private val petNamePattern by patternGroup.pattern( - "guardianpet", - "§[956d]Guardian.*", - ) - @SubscribeEvent fun onInventory(event: InventoryFullyOpenedEvent) { if (!isEnabled()) return - if (!inventoryNamePattern.matches(event.inventoryName)) return - if (petNamePattern.matches(PetAPI.currentPet)) return + if (event.inventoryName != "Experimentation Table") return + if (ExperimentationTableAPI.petNamePattern.matches(PetAPI.currentPet)) return lastInventoryOpen = SimpleTimeMark.now() @@ -69,7 +54,7 @@ object GuardianReminder { @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { if (!isEnabled()) return - if (!inventoryNamePattern.matches(InventoryUtils.openInventoryName())) return + if (InventoryUtils.openInventoryName() != "Experimentation Table") return if (lastInventoryOpen.passedSince() > 2.seconds) return val gui = Minecraft.getMinecraft().currentScreen as? GuiContainer ?: return @@ -95,5 +80,10 @@ object GuardianReminder { GlStateManager.popMatrix() } + @SubscribeEvent + fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { + event.move(59, "inventory.helper.enchanting.guardianReminder", "inventory.experimentationTable.guardianReminder") + } + private fun isEnabled() = LorenzUtils.inSkyBlock && config.guardianReminder } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt new file mode 100644 index 000000000000..21bc6052e099 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt @@ -0,0 +1,272 @@ +package at.hannibal2.skyhanni.features.inventory.experimentationtable + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.GuiContainerEvent.SlotClickEvent +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.milliseconds + +@SkyHanniModule +object SuperpairExperimentInformationDisplay { + + private val config get() = SkyHanniMod.feature.inventory.experimentationTable + + private var display = emptyList() + + private var uncoveredAt = 0 + private var uncoveredItems = mutableListOf>() + private var possiblePairs = 0 + + data class Item(val index: Int, val name: String) + data class ItemPair(val first: Item, val second: Item) + + private var found = mutableMapOf, String>() + + private var toCheck = mutableListOf>() + private var lastClicked = mutableListOf>() + private var lastClick = SimpleTimeMark.farPast() + private var currentExperiment = Experiment.NONE + private var instantFind = 0 + + private val sideSpaces1 = listOf(17, 18, 26, 27, 35, 36) + private val sideSpaces2 = listOf(16, 17, 18, 19, 25, 26, 27, 28, 34, 35, 36, 37) + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + display = emptyList() + + uncoveredAt = 0 + uncoveredItems.clear() + possiblePairs = 0 + + found.clear() + toCheck.clear() + lastClicked.clear() + lastClick = SimpleTimeMark.farPast() + currentExperiment = Experiment.NONE + instantFind = 0 + } + + @SubscribeEvent + fun onChestGuiOverlayRendered(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { + if (!isEnabled()) return + config.superpairDisplayPosition.renderStrings(display, posLabel = "Sperpair Experiment Information") + display = checkItems(toCheck) + } + + @SubscribeEvent + fun onSlotClick(event: SlotClickEvent) { + if (!isEnabled()) return + currentExperiment = ExperimentationTableAPI.getCurrentExperiment() ?: return + + if (isOutOfBounds(event.slotId, currentExperiment)) return + val item = event.item ?: return + if (item.displayName.removeColor() == "?") return + val clicksItem = InventoryUtils.getItemAtSlotIndex(4) + + if (lastClicked.none { it.first == event.slotId && it.second == uncoveredAt } && lastClick.passedSince() > 100.milliseconds) { + if (clicksItem != null && clicksItem.displayName.removeColor().split(" ")[1] == "0") return + lastClicked.add(Pair(event.slotId, uncoveredAt)) + lastClick = SimpleTimeMark.now() + toCheck.add(event.slotId to uncoveredAt) + uncoveredAt += 1 + } + } + + private fun checkItems(check: MutableList>): List { + currentExperiment = ExperimentationTableAPI.getCurrentExperiment() ?: return listOf() + if (check.isEmpty()) return drawDisplay() + + for ((slot, uncovered) in check) { + val itemNow = InventoryUtils.getItemAtSlotIndex(slot) ?: return drawDisplay() + val itemName = itemNow.displayName.removeColor() + + if (isWaiting(itemName) || isOutOfBounds(slot, currentExperiment)) return drawDisplay() + + val reward = convertToReward(itemNow) + if (uncoveredItems.none { it.first == slot }) uncoveredItems.add(Pair(slot, reward)) + + when { + isPowerUp(reward) -> handlePowerUp(slot, reward) + isReward(itemName) -> handleReward(slot, uncovered, reward) + } + + possiblePairs = calculatePossiblePairs() + + val since = clicksSinceSeparator(lastClicked) + + if ((since >= 2 || (since == -1 && lastClicked.size >= 2)) && instantFind == 0) { + lastClicked.add(-1 to uncoveredAt) + uncoveredAt += 1 + } + toCheck.removeIf { it.first == slot } + + return drawDisplay() + } + possiblePairs = calculatePossiblePairs() + return drawDisplay() + } + + private fun handlePowerUp(slot: Int, reward: String) { + val item = toEither(Item(slot, reward)) + + found[item] = "Powerup" + possiblePairs-- + lastClicked.removeIf { it.first == slot } + uncoveredAt -= 1 + if (reward == "Instant Find") instantFind += 1 + } + + private fun handleReward(slot: Int, uncovered: Int, reward: String) { + val lastSlotClicked = + if (instantFind == 0 && lastClicked.none { it.first == -1 && it.second == uncovered - 1 } && lastClicked.size != 1) lastClicked.find { it.second == uncovered - 1 } + ?: return else lastClicked.find { it.second == uncovered } ?: return + + val lastItem = InventoryUtils.getItemAtSlotIndex(lastSlotClicked.first) ?: return + val lastItemName = convertToReward(lastItem) + + if (isWaiting(lastItemName)) return + + when { + instantFind >= 1 -> { + handleFoundPair(slot, reward, lastSlotClicked.first) + instantFind -= 1 + lastClicked.add(-1 to uncoveredAt) + uncoveredAt += 1 + } + + hasFoundPair(slot, lastSlotClicked.first, reward, lastItemName) -> handleFoundPair( + slot, + reward, + lastSlotClicked.first, + ) + + hasFoundMatch(slot, reward) -> handleFoundMatch(slot, reward) + else -> handleNormalReward(slot, reward) + } + + } + + private fun handleFoundPair( + slot: Int, + reward: String, + lastSlotClicked: Int, + ) { + val pair = toEither(ItemPair(Item(slot, reward), Item(lastSlotClicked, reward))) + + found[pair] = "Pair" + found.entries.removeIf { + it.value == "Match" && right(it.key).first.name == reward + } + found.entries.removeIf { + it.value == "Normal" && (left(it.key).index == slot || left(it.key).index == lastSlotClicked) + } + } + + private fun handleFoundMatch(slot: Int, reward: String) { + val match = uncoveredItems.find { it.second == reward }?.first ?: return + val pair = toEither(ItemPair(Item(slot, reward), Item(match, reward))) + + found[pair] = "Match" + found.entries.removeIf { + it.value == "Normal" && (left(it.key).index == slot || left(it.key).index == match) + } + } + + private fun handleNormalReward(slot: Int, reward: String) { + val item = toEither(Item(slot, reward)) + + if (found.none { + listOf("Match", "Pair").contains(it.value) && (right(it.key).first.index == slot || right(it.key).second.index == slot) + } && found.none { it.value == "Normal" && left(it.key).index == slot }) found[item] = "Normal" + } + + private fun calculatePossiblePairs() = + ((currentExperiment.gridSize - 2) / 2) - found.filter { listOf("Pair", "Match", "Normal").contains(it.value) }.size + + private fun drawDisplay() = buildList { + add("§6Superpair Experimentation Data") + add("") + + val pairs = found.entries.filter { it.value == "Pair" } + val matches = found.entries.filter { it.value == "Match" } + val powerups = found.entries.filter { it.value == "Powerup" } + val normals = found.entries.filter { it.value == "Normal" } + + if (pairs.isNotEmpty()) add("§2Found") + for (pair in pairs) { + val prefix = determinePrefix(pairs.indexOf(pair), pairs.lastIndex) + add(" $prefix §a${right(pair.key).first.name}") + } + if (matches.isNotEmpty()) add("§eMatched") + for (match in matches) { + val prefix = determinePrefix(matches.indexOf(match), matches.lastIndex) + add(" $prefix §e${right(match.key).first.name}") + } + if (powerups.isNotEmpty()) add("§bPowerUp") + for (powerup in powerups) { + val prefix = determinePrefix(powerups.indexOf(powerup), powerups.size - 1) + add(" $prefix §b${left(powerup.key).name}") + } + val toAdd = mutableListOf() + if (possiblePairs >= 1) toAdd.add("§ePairs - $possiblePairs") + if (2 - powerups.size >= 1) toAdd.add("§bPowerUps - ${2 - powerups.size}") + if (normals.isNotEmpty()) toAdd.add("§7Normals - ${normals.size}") + + if (toAdd.isNotEmpty()) { + add("") + add("§4Not found") + } + for (string in toAdd) if (string != toAdd.last()) add(" ├ $string") else add(" └ $string") + } + + private fun convertToReward(item: ItemStack) = if (item.displayName.removeColor() == "Enchanted Book") item.getLore()[2].removeColor() + else item.displayName.removeColor() + + private fun determinePrefix(index: Int, lastIndex: Int) = if (index == lastIndex) "└" else "├" + + private fun hasFoundPair( + firstSlot: Int, + secondSlot: Int, + firstName: String, + secondName: String, + ) = firstSlot != secondSlot && firstName == secondName + + private fun hasFoundMatch(itemSlot: Int, reward: String) = + uncoveredItems.any { (slot, name) -> slot != itemSlot && name == reward } && found.none { + listOf("Pair", "Match").contains(it.value) && (right(it.key).first.index == itemSlot || right(it.key).second.index == itemSlot) + } + + private fun isPowerUp(reward: String) = ExperimentationTableAPI.powerUpPattern.matches(reward) + + private fun isReward(reward: String) = ExperimentationTableAPI.rewardPattern.matches(reward) + + private fun isWaiting(itemName: String) = + listOf("Click any button!", "Click a second button!", "Next button is instantly rewarded!").contains(itemName) + + private fun clicksSinceSeparator(list: MutableList>): Int { + val lastIndex = list.indexOfLast { it.first == -1 } + return if (lastIndex != -1) list.size - 1 - lastIndex else -1 + } + + private fun isOutOfBounds(slot: Int, experiment: Experiment): Boolean = + slot <= experiment.startSlot || slot >= experiment.endSlot || (if (experiment.sideSpace == 1) slot in sideSpaces1 else slot in sideSpaces2) + + private fun left(it: Pair): Item = it.first ?: Item(-1, "") + + private fun right(it: Pair): ItemPair = it.second ?: ItemPair(Item(-1, ""), Item(-1, "")) + + private fun toEither(it: Any): Pair = if (it is Item) it to null else null to it as ItemPair + + private fun isEnabled() = LorenzUtils.inSkyBlock && config.superpairDisplay && ExperimentationTableAPI.getCurrentExperiment() != null +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/SuperpairsClicksAlert.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairsClicksAlert.kt similarity index 91% rename from src/main/java/at/hannibal2/skyhanni/features/inventory/SuperpairsClicksAlert.kt rename to src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairsClicksAlert.kt index 5c5011c85b1c..ef99c40bab97 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/SuperpairsClicksAlert.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairsClicksAlert.kt @@ -1,4 +1,4 @@ -package at.hannibal2.skyhanni.features.inventory +package at.hannibal2.skyhanni.features.inventory.experimentationtable import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator @@ -14,7 +14,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @SkyHanniModule object SuperpairsClicksAlert { - private val config get() = SkyHanniMod.feature.inventory.helper.enchanting + private val config get() = SkyHanniMod.feature.inventory.experimentationTable private var roundsNeeded = -1 private val roundsNeededRegex = Regex("""(?:Chain|Series) of (\d+):""") @@ -66,5 +66,7 @@ object SuperpairsClicksAlert { @SubscribeEvent fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { event.move(46, "misc.superpairsClicksAlert", "inventory.helper.enchanting.superpairsClicksAlert") + + event.move(59, "inventory.helper.enchanting.superpairsClicksAlert", "inventory.experimentationTable.superpairsClicksAlert") } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experiments/UltraRareBookAlert.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/UltraRareBookAlert.kt similarity index 75% rename from src/main/java/at/hannibal2/skyhanni/features/inventory/experiments/UltraRareBookAlert.kt rename to src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/UltraRareBookAlert.kt index 1cc849cad60a..e6a9dda426e9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/experiments/UltraRareBookAlert.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/UltraRareBookAlert.kt @@ -1,13 +1,15 @@ -package at.hannibal2.skyhanni.features.inventory.experiments +package at.hannibal2.skyhanni.features.inventory.experimentationtable import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.InventoryUpdatedEvent +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.bookPattern +import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentationTableAPI.ultraRarePattern import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ColorUtils.withAlpha -import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher @@ -18,7 +20,6 @@ import at.hannibal2.skyhanni.utils.SoundUtils.createSound import at.hannibal2.skyhanni.utils.SoundUtils.playSound import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXYAligned -import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiContainer import net.minecraft.client.renderer.GlStateManager @@ -29,23 +30,9 @@ import kotlin.time.Duration.Companion.seconds @SkyHanniModule object UltraRareBookAlert { - private val config get() = SkyHanniMod.feature.inventory.helper.enchanting + private val config get() = SkyHanniMod.feature.inventory.experimentationTable private val dragonSound by lazy { createSound("mob.enderdragon.growl", 1f) } - private val patternGroup = RepoPattern.group("data.enchanting") - private val superpairsGui by patternGroup.pattern( - "inventory.experimentstable.gui", - "Superpairs.*" - ) - private val ultraRarePattern by patternGroup.pattern( - "inventory.experimentstable.ultrarare", - "§d§kXX§5 ULTRA-RARE BOOK! §d§kXX" - ) - private val bookPattern by patternGroup.pattern( - "inventory.experimentstable.book", - "§9(?.*)" - ) - private var enchantsFound = false private var lastNotificationTime = SimpleTimeMark.farPast() @@ -58,9 +45,7 @@ object UltraRareBookAlert { @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { - if (!LorenzUtils.inSkyBlock) return - if (!config.ultraRareBookAlert) return - if (!superpairsGui.matches(InventoryUtils.openInventoryName())) return + if (!isEnabled()) return if (lastNotificationTime.passedSince() > 5.seconds) return val gui = Minecraft.getMinecraft().currentScreen as? GuiContainer ?: return @@ -80,10 +65,8 @@ object UltraRareBookAlert { @SubscribeEvent fun onInventoryUpdated(event: InventoryUpdatedEvent) { - if (!LorenzUtils.inSkyBlock) return - if (!config.ultraRareBookAlert) return + if (!isEnabled()) return if (enchantsFound) return - if (!superpairsGui.matches(event.inventoryName)) return for (lore in event.inventoryItems.map { it.value.getLore() }) { val firstLine = lore.firstOrNull() ?: continue @@ -101,4 +84,12 @@ object UltraRareBookAlert { fun onInventoryClose(event: InventoryCloseEvent) { enchantsFound = false } + + @SubscribeEvent + fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { + event.move(59, "inventory.helper.enchanting.ultraRareBookAlert", "inventory.experimentationTable.ultraRareBookAlert") + } + + private fun isEnabled() = + LorenzUtils.inSkyBlock && config.ultraRareBookAlert && ExperimentationTableAPI.getCurrentExperiment() != null } From 1062c413cc17b0e99ca527b33568edf71642cab5 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 21 Sep 2024 00:28:43 +0200 Subject: [PATCH 045/102] Version 0.27 Beta 10 --- docs/CHANGELOG.md | 25 +++++++++++++++++++ docs/FEATURES.md | 7 ++++++ root.gradle.kts | 2 +- .../java/at/hannibal2/skyhanni/SkyHanniMod.kt | 2 +- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a0795097cb22..d351445de428 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -51,6 +51,7 @@ + Stray rabbits = Dark Aqua. + Abi = Dark Green. + Added a toggle to highlight found rabbits in Hoppity's Collection menu. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) ++ Added the ability to change the color of missing rabbit dyes in Hoppity's Collection to reflect rabbit rarity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2522) #### The Carnival Features @@ -85,6 +86,12 @@ + Added Accessory magical power display as stack size. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2243) + Only works inside the Accessory Bag and Auction House. + Added `/gfs` to fix a broken Piggy Bank. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2150) ++ Added Superpair Display. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171) + + Displays found and matched pairs, power-ups, and missing pairs/normals. ++ Added Experiments Dry-Streak Display. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171) + + Shows attempts and XP since the last ULTRA-RARE. ++ Added Experiments Profit Tracker. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171) + + Tracks profits in Coins and Enchanting XP. #### Chat Features @@ -196,6 +203,8 @@ + Added config option to disable rarity in Compact Hoppity messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2383) + Chocolate Factory: Shows "time to next milestone" instead of "max milestone" when you are at Prestige 6. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2503) ++ Added the required chocolate amount to the tooltip of milestone rabbits in Hoppity's Collection. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2523) ++ Improved the responsiveness of Hoppity Chat Compacting. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2488) #### Combat Improvements @@ -215,6 +224,7 @@ + Changed suggested cactus speed to 464 again. - DarkDash (https://github.com/hannibal002/SkyHanni/pull/2424) + Since the cactus knife now allows 500 max speed. + Made the waypoint to the middle of the plot for Pest Waypoint optional (off by default). - Luna (https://github.com/hannibal002/SkyHanni/pull/2469) ++ Reduced one click for supercrafting visitor materials by using `/viewrecipe` instead of `/recipe` when using the Shopping List button. - Miestiek (https://github.com/hannibal002/SkyHanni/pull/2505) #### Rift Improvements @@ -235,6 +245,7 @@ + Added search functionality to some SkyHanni GUIs. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2477) + Search works in all SkyHanni Trackers and the Sack Display. + In your inventory, hover over the GUI with your mouse, then start typing to search. ++ Improved the performance of pathfinding logic in Area Navigation. - nea (https://github.com/hannibal002/SkyHanni/pull/2537) ### Fixes @@ -264,6 +275,7 @@ + Fixed the Auction House Price Comparison appearing in your own inventory as well. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2483) + Fixed "click to show tunnel map" from the Glacite Mines commission menu appearing in the inventory. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2485) + Fixed Mouse Keys not working as Wardrobe Slot Hotkeys. - j10a1n15, ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2506) ++ Fixed Estimated Item Value incorrectly showing all Kuudra armor as starred. - Luna (https://github.com/hannibal002/SkyHanni/pull/2550) #### Mining Fixes @@ -278,6 +290,11 @@ + Fixed blocks mined by Efficient Miner not being detected. - Empa (https://github.com/hannibal002/SkyHanni/pull/2526) + Fixed Corpse Tracker resetting items between restarts. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2528) + Fixed the Corpse Tracker not displaying while in the Mineshaft. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2516) ++ Fixed Hardstone and Tungsten not being properly detected in Mineshafts. - Empa (https://github.com/hannibal002/SkyHanni/pull/2536) ++ Fixed Heart of the Mountain showing outdated perks and powder information. - Tryp0xd (https://github.com/hannibal002/SkyHanni/pull/2543) ++ Fixed Sky Mall perks not being read correctly after the Hypixel update. - Luna (https://github.com/hannibal002/SkyHanni/pull/2551) ++ Fixed Mining Commission Blocks Color not working properly with Connected Textures. - Empa (https://github.com/hannibal002/SkyHanni/pull/2553) ++ Fixed Gemstone Gauntlet item category. - Luna (https://github.com/hannibal002/SkyHanni/pull/2534) #### Scoreboard Fixes @@ -299,6 +316,7 @@ + Fixed a Custom Scoreboard error during the Magma Boss Fight and Goblin Raid. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2531) + Fixed a Custom Scoreboard error while in the Dojo. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2519) + Fixed a Custom Scoreboard error during M7 Dragons. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2510) ++ Fixed Custom Scoreboard error during the Raffle Event. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2545) #### Hoppity Fixes @@ -307,6 +325,7 @@ + Fixed "Highlight Requirement Rabbits" requiring "Hoppity Collection Stats" to function. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2398) + Fixed minor formatting issues with Hoppity Event Summary. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2416) + Fixed Hoppity Event stats resetting. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2489) ++ Fixed an issue where Fish the Rabbit and El Dorado did not have compacted chat messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2488) #### Chat Fixes @@ -360,6 +379,7 @@ + Fixed "Last Server Joined" spamming the chat. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2495) + Fixed lag spikes in Path Finder. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2525) + Fixed old mayor perks not being marked as inactive when the mayor changes. - Luna (https://github.com/hannibal002/SkyHanni/pull/2524) ++ Fixed direct GitHub downloads triggering the source checker. - ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2548) ### Technical Details @@ -405,6 +425,11 @@ + Added more graph node tags; only display certain tags on specific islands in the editor. + Added an option to limit the maximum render range of nodes and edges in the Graph Editor for better performance. + Hoppity's Collection highlighting now updates on event triggers instead of every frame. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) ++ Added `select near look` keybind in Graph Editor. - nea (https://github.com/hannibal002/SkyHanni/pull/2537) ++ Added checks to avoid creating bugs with the area structure in Graph Editor. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2537) ++ Added options to the graph node list to hide nodes with certain tags. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2537) ++ Removed the use of `LorenzChatEvent()` for 'manually' adding data to Hoppity Chat Compact. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2488) + + Now uses `EggFoundEvent()`. ### Removed Features diff --git a/docs/FEATURES.md b/docs/FEATURES.md index cbf3abacf756..e3c68e43d602 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -994,6 +994,7 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game. + Stray rabbits = Dark Aqua. + Abi = Dark Green. + Added a toggle to highlight found rabbits in Hoppity's Collection menu. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) ++ Added the ability to change the color of missing rabbit dyes in Hoppity's Collection to reflect rabbit rarity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2522) ### The Carnival @@ -1327,6 +1328,12 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game. + Added to the Item Number list. + Added Compact Experimentation Table chat rewards. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2209) + Uses a compact chat message of rewards gained from Add-ons/Experiments. ++ Added Superpair Display. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171) + + Displays found and matched pairs, power-ups, and missing pairs/normals. ++ Added Experiments Dry-Streak Display. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171) + + Shows attempts and XP since the last ULTRA-RARE. ++ Added Experiments Profit Tracker. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171) + + Tracks profits in Coins and Enchanting XP. + In-Water Display. - Stella (https://github.com/hannibal002/SkyHanni/pull/1892) + Useful when using a Prismarine Blade in Stranded Mode. + Added Beacon Power Display. - Empa (https://github.com/hannibal002/SkyHanni/pull/1901) diff --git a/root.gradle.kts b/root.gradle.kts index f799ed79d6a1..4c8333a202c6 100644 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -12,7 +12,7 @@ plugins { allprojects { group = "at.hannibal2.skyhanni" - version = "0.27.Beta.9" + version = "0.27.Beta.10" } preprocess { diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index cab1b690ffb5..41552ba66982 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -42,7 +42,7 @@ import org.apache.logging.log4j.Logger clientSideOnly = true, useMetadata = true, guiFactory = "at.hannibal2.skyhanni.config.ConfigGuiForgeInterop", - version = "0.27.Beta.9", + version = "0.27.Beta.10", ) class SkyHanniMod { From 1da7050a5b95e6e1cf0111c3a71b042168d3918e Mon Sep 17 00:00:00 2001 From: NopoTheGamer <40329022+NopoTheGamer@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:31:34 +1000 Subject: [PATCH 046/102] =?UTF-8?q?Make=20Mining=20Commissions=20Blocks=20?= =?UTF-8?q?Colour=20actually=20work=20with=20connected=20textures=20instea?= =?UTF-8?q?d=20of=20just=20turning=20them=20off=20=F0=9F=99=82=20(#2559)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hooks/OptifineConnectedTexturesHook.kt | 35 +++++++++++++++++++ .../transformers/MixinOptifineConfig.java | 18 ---------- .../MixinOptifineConnectedTextures.java | 23 ++++++++++++ 3 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt new file mode 100644 index 000000000000..f930e1008ca6 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt @@ -0,0 +1,35 @@ +package at.hannibal2.skyhanni.mixins.hooks + +import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor +import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.CommissionBlock.Companion.onColor +import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.replaceBlocksMapCache +import at.hannibal2.skyhanni.features.mining.OreType.Companion.isOreType +import at.hannibal2.skyhanni.test.command.ErrorManager +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.block.state.IBlockState + +fun modifyConnectedTexturesBlockState(state: IBlockState): IBlockState { + if (state == null) return state + var returnState: IBlockState = state + + if (!LorenzUtils.inSkyBlock) return state + + try { + if (MiningCommissionsBlocksColor.enabled && MiningCommissionsBlocksColor.active) { + returnState = replaceBlocksMapCache.getOrPut(state) { + MiningCommissionsBlocksColor.CommissionBlock.entries.firstOrNull { + state.isOreType(it.oreType) + }?.onColor(state) ?: state + } + } + } catch (e: Exception) { + ErrorManager.logErrorWithData(e, "Error in MiningCommissionsBlocksColor") + } + + if (returnState !== state) { + + return returnState + } + return state +} + diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java deleted file mode 100644 index 97b20f19a621..000000000000 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package at.hannibal2.skyhanni.mixins.transformers; - -import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Pseudo; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Pseudo -@Mixin(targets = "Config", remap = false) -public class MixinOptifineConfig { - - @Inject(method = {"isConnectedTextures", "isConnectedTexturesFancy"}, at = @At("HEAD"), cancellable = true) - private static void isConnectedTextures(CallbackInfoReturnable cir) { - if (MiningCommissionsBlocksColor.INSTANCE.getActive()) cir.setReturnValue(false); - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java new file mode 100644 index 000000000000..9f3087e8e0ed --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java @@ -0,0 +1,23 @@ +package at.hannibal2.skyhanni.mixins.transformers; + +import at.hannibal2.skyhanni.mixins.hooks.OptifineConnectedTexturesHookKt; +import net.minecraft.block.state.IBlockState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Pseudo +@Mixin(targets = "net.optifine.ConnectedTextures", remap = false) +public class MixinOptifineConnectedTextures { + + @ModifyArg(method = "getConnectedTexture", at = @At(value = "INVOKE", target = "getConnectedTextureMultiPass")) + private static IBlockState modifyGetConnectedTextureMultiPass(IBlockState state) { + return OptifineConnectedTexturesHookKt.modifyConnectedTexturesBlockState(state); + } + + @ModifyArg(method = "getConnectedTexture", at = @At(value = "INVOKE", target = "skipConnectedTexture")) + private static IBlockState modifySkipConnectedTexture(IBlockState state) { + return OptifineConnectedTexturesHookKt.modifyConnectedTexturesBlockState(state); + } +} From f056eb5a458ae95dff2cdbd84217e491f72a3d06 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 21 Sep 2024 10:32:43 +0200 Subject: [PATCH 047/102] MiningCommissionsBlocksColor.processState --- .../mining/MiningCommissionsBlocksColor.kt | 37 ++++++++++++++----- .../hooks/BlockRendererDispatcherHook.kt | 21 ++--------- .../hooks/OptifineConnectedTexturesHook.kt | 28 +------------- 3 files changed, 32 insertions(+), 54 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt index 5eaff2b44604..5de75e5d1364 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MiningCommissionsBlocksColor.kt @@ -10,7 +10,10 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.TabListUpdateEvent +import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.CommissionBlock.Companion.onColor +import at.hannibal2.skyhanni.features.mining.OreType.Companion.isOreType import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.TimeLimitedSet @@ -28,8 +31,8 @@ object MiningCommissionsBlocksColor { private val config get() = SkyHanniMod.feature.mining.commissionsBlocksColor - var enabled = false - var active = false + private var enabled = false + private var active = false private val patternGroup = RepoPattern.group("mining.commissions") @@ -41,14 +44,13 @@ object MiningCommissionsBlocksColor { "§a§l(?.*) §r§eCommission Complete! Visit the King §r§eto claim your rewards!", ) - var color: EnumDyeColor = EnumDyeColor.RED + private var color = EnumDyeColor.RED - private fun glass(state: IBlockState, result: Boolean): IBlockState = - if (result) { - state.withProperty(BlockCarpet.COLOR, color) - } else { - state.withProperty(BlockCarpet.COLOR, EnumDyeColor.GRAY) - } + private fun glass(state: IBlockState, result: Boolean): IBlockState = if (result) { + state.withProperty(BlockCarpet.COLOR, color) + } else { + state.withProperty(BlockCarpet.COLOR, EnumDyeColor.GRAY) + } private fun block(result: Boolean): IBlockState { val wool = Blocks.wool.defaultState @@ -64,7 +66,7 @@ object MiningCommissionsBlocksColor { private var dirty = false private var forceDirty = false - var replaceBlocksMapCache = mutableMapOf() + private var replaceBlocksMapCache = mutableMapOf() // TODO Commission API @SubscribeEvent @@ -244,4 +246,19 @@ object MiningCommissionsBlocksColor { if (oreType.isGemstone()) glass(state, highlight) else block(highlight) } } + + fun processState(state: IBlockState?): IBlockState? { + if (!enabled || !active) return state + if (state == null) return null + try { + return replaceBlocksMapCache.getOrPut(state) { + CommissionBlock.entries.firstOrNull { + state.isOreType(it.oreType) + }?.onColor(state) ?: state + } + } catch (e: Exception) { + ErrorManager.logErrorWithData(e, "Error in MiningCommissionsBlocksColor") + return state + } + } } diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt index f72d56739d6c..cf4e4ba40bfb 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/BlockRendererDispatcherHook.kt @@ -1,10 +1,6 @@ package at.hannibal2.skyhanni.mixins.hooks import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor -import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.CommissionBlock.Companion.onColor -import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.replaceBlocksMapCache -import at.hannibal2.skyhanni.features.mining.OreType.Companion.isOreType -import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.LorenzUtils import net.minecraft.block.state.IBlockState import net.minecraft.client.renderer.BlockRendererDispatcher @@ -21,24 +17,13 @@ fun modifyGetModelFromBlockState( pos: BlockPos?, cir: CallbackInfoReturnable, ) { - if (state == null || pos == null) return - var returnState: IBlockState = state + if (pos == null) return if (!LorenzUtils.inSkyBlock) return - try { - if (MiningCommissionsBlocksColor.enabled && MiningCommissionsBlocksColor.active) { - returnState = replaceBlocksMapCache.getOrPut(state) { - MiningCommissionsBlocksColor.CommissionBlock.entries.firstOrNull { - state.isOreType(it.oreType) - }?.onColor(state) ?: state - } - } - } catch (e: Exception) { - ErrorManager.logErrorWithData(e, "Error in MiningCommissionsBlocksColor") - } + val returnState = MiningCommissionsBlocksColor.processState(state) - if (returnState !== state) { + if (returnState != state) { cir.returnValue = blockRendererDispatcher.blockModelShapes.getModelForState(returnState) } } diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt index f930e1008ca6..fe4619cf27f3 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt +++ b/src/main/java/at/hannibal2/skyhanni/mixins/hooks/OptifineConnectedTexturesHook.kt @@ -1,35 +1,11 @@ package at.hannibal2.skyhanni.mixins.hooks import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor -import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.CommissionBlock.Companion.onColor -import at.hannibal2.skyhanni.features.mining.MiningCommissionsBlocksColor.replaceBlocksMapCache -import at.hannibal2.skyhanni.features.mining.OreType.Companion.isOreType -import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.LorenzUtils import net.minecraft.block.state.IBlockState -fun modifyConnectedTexturesBlockState(state: IBlockState): IBlockState { - if (state == null) return state - var returnState: IBlockState = state - +fun modifyConnectedTexturesBlockState(state: IBlockState?): IBlockState? { if (!LorenzUtils.inSkyBlock) return state - - try { - if (MiningCommissionsBlocksColor.enabled && MiningCommissionsBlocksColor.active) { - returnState = replaceBlocksMapCache.getOrPut(state) { - MiningCommissionsBlocksColor.CommissionBlock.entries.firstOrNull { - state.isOreType(it.oreType) - }?.onColor(state) ?: state - } - } - } catch (e: Exception) { - ErrorManager.logErrorWithData(e, "Error in MiningCommissionsBlocksColor") - } - - if (returnState !== state) { - - return returnState - } - return state + return MiningCommissionsBlocksColor.processState(state) } From 63d2c9e3d491c0e8ad5e4c5c18105668ad100f90 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 21 Sep 2024 10:48:47 +0200 Subject: [PATCH 048/102] improved graph editor bug finder behaviour slightly --- .../at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt index aee75ec5faf6..73c55b447426 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/graph/GraphEditorBugFinder.kt @@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.data.IslandGraphs import at.hannibal2.skyhanni.data.model.GraphNode import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent import at.hannibal2.skyhanni.features.misc.IslandAreas.getAreaTag +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.graph.GraphEditor.distanceSqToPlayer import at.hannibal2.skyhanni.utils.GraphUtils import at.hannibal2.skyhanni.utils.LorenzVec @@ -14,6 +15,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.awt.Color // Trying to find errors in Area Graph for the current graph editor instance +@SkyHanniModule object GraphEditorBugFinder { private var errorsInWorld = emptyMap() From bbbb9c22ad99ac887d1a4d624c594818e81a7189 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:04:09 +0200 Subject: [PATCH 049/102] better graph node names and descriptions --- .../skyhanni/data/model/GraphNodeTag.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt index e70786137bf6..2cdc9e7e0d1d 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt @@ -13,21 +13,21 @@ enum class GraphNodeTag( DEV("dev", LorenzColor.WHITE, "Dev", "Intentionally marked as dev."), // E.g. Spawn points, todos, etc // Everywhere - NPC("npc", LorenzColor.YELLOW, "NPC", "A NPC entity."), // also take from neu repo + NPC("npc", LorenzColor.YELLOW, "NPC", "A NPC to talk to."), // also take from neu repo AREA("area", LorenzColor.DARK_GREEN, "Area", "A big SkyBlock area."), SMALL_AREA("small_area", LorenzColor.GREEN, "Small Area", "A small SkyBlock area, e.g. a house."), - POI("poi", LorenzColor.WHITE, "PoI", "Point of interest."), + POI("poi", LorenzColor.WHITE, "Point of Interest", "A relevant spot or a landmark on the map."), // LAUNCH_PAD("launch", LorenzColor.WHITE, "Launch Pad", "Slime blocks sending you to another server."), TELEPORT("teleport", LorenzColor.BLUE, "Teleport", "A spot from/to teleport."), // on multiple islands - ROMEO("romeo", LorenzColor.WHITE, "Romeo & Juliette Quest", "Blocks related to the Romeo and Juliette/Ring of Love quest line."), + ROMEO("romeo", LorenzColor.WHITE, "Romeo & Juliette Quest", "Spots related to the Romeo and Juliette/Ring of Love quest line."), RACE("race", LorenzColor.WHITE, "Race Start/Stop", "A race start or stop point."), - SLAYER("slayer", LorenzColor.RED, "Slayer", "A Slayer area"), + SLAYER("slayer", LorenzColor.RED, "Slayer", "A Slayer area."), HOPPITY("hoppity", LorenzColor.AQUA, "Hoppity Egg", "An egg location in Hoppity's Hunt."), - GRIND_MOBS("grind_mobs", LorenzColor.RED, "Grind Mobs", "A spot where combat mobs spawn that can be killed."), - GRIND_ORES("grind_ores", LorenzColor.DARK_AQUA, "Grind Ores", "A spot where mining ores spawn that can be mines."), - GRIND_CROPS("grind_crops", LorenzColor.DARK_GREEN, "Grind Crops", "A spot where farming crops spawn that can be farmed."), + GRIND_MOBS("grind_mobs", LorenzColor.RED, "Mob Spawn Area", "An area where mobs spawn that can be killed."), + GRIND_ORES("grind_ores", LorenzColor.DARK_AQUA, "Ore Vein", "A regenerating ore vein that can be mined."), + GRIND_CROPS("grind_crops", LorenzColor.DARK_GREEN, "Crop Area", "An area where crops grow that can be farmed."), // hoppity // Hub @@ -41,8 +41,8 @@ enum class GraphNodeTag( // FARMING_CROP("farming_crop", LorenzColor.WHITE, "Farming Crop", "A spot where you can break crops on farming islands."), // Rift - RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the rift.", onlyIsland = IslandType.THE_RIFT), - RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Rift Eye", "An Eye in the rift to teleport to.", onlyIsland = IslandType.THE_RIFT), + RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the Rift.", onlyIsland = IslandType.THE_RIFT), + RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Rift Eye", "An Eye in the Rift to teleport to.", onlyIsland = IslandType.THE_RIFT), RIFT_MONTEZUMA("rift_montezuma", LorenzColor.GRAY, "Montezuma Soul Piece", "A piece of the Montezuma Soul.", onlyIsland = IslandType.THE_RIFT), RIFT_EFFIGY("rift_effigy", LorenzColor.RED, "Blood Effigies", "Locations of the Blood Effigies.", onlyIsland = IslandType.THE_RIFT), @@ -50,14 +50,14 @@ enum class GraphNodeTag( SPIDER_RELIC("SPIDER_RELIC", LorenzColor.DARK_PURPLE, "Spider's Relic", "An relic in the Spider's Den.", onlyIsland = IslandType.SPIDER_DEN), // Dwarven Mines - MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Mines Emissary", "A Emissary from the king.", onlyIsland = IslandType.DWARVEN_MINES), + MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Mines Emissary", "An Emissary to the king.", onlyIsland = IslandType.DWARVEN_MINES), // commission areas // Crimson Isles - CRIMSON_MINIBOSS("crimson_miniboss", LorenzColor.RED, "Crimson Miniboss", "Mini bosses in Crimson Isle.", onlyIsland = IslandType.CRIMSON_ISLE), + CRIMSON_MINIBOSS("crimson_miniboss", LorenzColor.RED, "Crimson Miniboss", "A Miniboss in the Crimson Isle.", onlyIsland = IslandType.CRIMSON_ISLE), // The End - END_GOLEM("end_golem", LorenzColor.RED, "Golem Spawn", "A spot where the golem in the end spawns", onlyIsland = IslandType.THE_END), + END_GOLEM("end_golem", LorenzColor.RED, "Golem Spawn", "A spot where the golem can spawn in the End.", onlyIsland = IslandType.THE_END), ; From 8a0a2fb1e27ee2a08a0bcb14634ab8ef134ba61b Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 21 Sep 2024 11:06:46 +0200 Subject: [PATCH 050/102] Fix: Totem of Corruption expiry warning sometimes not working (#2554) --- .../skyhanni/features/fishing/TotemOfCorruption.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt index 0410a04a91d0..c8ff17a9e935 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt @@ -22,6 +22,7 @@ import at.hannibal2.skyhanni.utils.RenderUtils.drawSphereInWorld import at.hannibal2.skyhanni.utils.RenderUtils.drawSphereWireframeInWorld import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings import at.hannibal2.skyhanni.utils.SoundUtils.playPlingSound +import at.hannibal2.skyhanni.utils.TimeLimitedSet import at.hannibal2.skyhanni.utils.TimeUnit import at.hannibal2.skyhanni.utils.TimeUtils.format import at.hannibal2.skyhanni.utils.getLorenzVec @@ -29,7 +30,9 @@ import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.entity.item.EntityArmorStand import net.minecraft.util.EnumParticleTypes import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.UUID import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.seconds @SkyHanniModule @@ -39,6 +42,7 @@ object TotemOfCorruption { private var display = emptyList() private var totems: List = emptyList() + private val warnedTotems = TimeLimitedSet(2.minutes) private val patternGroup = RepoPattern.group("fishing.totemofcorruption") private val totemNamePattern by patternGroup.pattern( @@ -154,9 +158,10 @@ object TotemOfCorruption { val owner = getOwner(totem) ?: return@mapNotNull null val timeToWarn = config.warnWhenAboutToExpire.seconds - if (timeToWarn > 0.seconds && timeRemaining == timeToWarn) { + if (timeToWarn > 0.seconds && timeRemaining <= timeToWarn && !warnedTotems.contains(totem.uniqueID)) { playPlingSound() sendTitle("§c§lTotem of Corruption §eabout to expire!", 5.seconds) + warnedTotems.add(totem.uniqueID) } Totem(totem.getLorenzVec(), timeRemaining, owner) } From 8e71a5e2a2d89ada948ab752f055cb7611b2a219 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:07:15 +0200 Subject: [PATCH 051/102] code cleanup --- .../at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt index c8ff17a9e935..9896580c6b2b 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/TotemOfCorruption.kt @@ -158,7 +158,7 @@ object TotemOfCorruption { val owner = getOwner(totem) ?: return@mapNotNull null val timeToWarn = config.warnWhenAboutToExpire.seconds - if (timeToWarn > 0.seconds && timeRemaining <= timeToWarn && !warnedTotems.contains(totem.uniqueID)) { + if (timeToWarn > 0.seconds && timeRemaining <= timeToWarn && totem.uniqueID !in warnedTotems) { playPlingSound() sendTitle("§c§lTotem of Corruption §eabout to expire!", 5.seconds) warnedTotems.add(totem.uniqueID) From c9bb5e1a14f558eb5eb8acc7c46d117a2b62ac0d Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:07:54 +0200 Subject: [PATCH 052/102] Fix: Mining Stats Reforge (#2555) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt index 994ff524e869..182309634f72 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt @@ -43,7 +43,14 @@ enum class SkyblockStat(val icon: String) { PRISTINE("§5✧"), FORAGING_FORTUNE("§☘"), FARMING_FORTUNE("§6☘"), + + MINING_SPREAD("§e▚"), MINING_FORTUNE("§6☘"), + ORE_FORTUNE("§6☘"), + DWARVEN_METAL_FORTUNE("§6☘"), + BLOCK_FORTUNE("§6☘"), + GEMSTONE_FORTUNE("§6☘"), + FEAR("§a☠"), HEAT_RESISTANCE("§c♨"), ; From f298d65f6b3029474f6695ccf3cb6ac5f0400e42 Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Sat, 21 Sep 2024 05:08:42 -0400 Subject: [PATCH 053/102] Fix: Glacite Powder being counted as an item in CorpseTracker (#2557) --- .../features/mining/glacitemineshaft/CorpseTracker.kt | 4 +++- .../mining/glacitemineshaft/ProfitPerMineshaftCorpse.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt index f1d23185f3e0..3e81a01a9588 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/CorpseTracker.kt @@ -17,6 +17,7 @@ import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.renderables.Searchable import at.hannibal2.skyhanni.utils.renderables.toSearchable @@ -65,9 +66,10 @@ object CorpseTracker { private fun addLootedCorpse(type: CorpseType) = tracker.modify { it.corpsesLooted.addOrPut(type, 1) } @SubscribeEvent - fun onCorpseLoot(event: CorpseLootedEvent) { + fun onCorpseLooted(event: CorpseLootedEvent) { addLootedCorpse(event.corpseType) for ((itemName, amount) in event.loot) { + if (itemName.removeColor().trim() == "Glacite Powder") continue NEUInternalName.fromItemNameOrNull(itemName)?.let { item -> tracker.modify { it.addItem(event.corpseType, item, amount) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/ProfitPerMineshaftCorpse.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/ProfitPerMineshaftCorpse.kt index 7eebeeaab890..78f4b07eda74 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/ProfitPerMineshaftCorpse.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/ProfitPerMineshaftCorpse.kt @@ -18,7 +18,7 @@ object ProfitPerMineshaftCorpse { private val config get() = SkyHanniMod.feature.mining.mineshaft @SubscribeEvent - fun onFossilExcavation(event: CorpseLootedEvent) { + fun onCorpseLooted(event: CorpseLootedEvent) { if (!config.profitPerCorpseLoot) return val loot = event.loot From 208fc04e7db767abda24fdbae141d60898371d61 Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Sat, 21 Sep 2024 11:18:41 +0200 Subject: [PATCH 054/102] Backend: AreaChangeEvent and Islands in HandleEvent (#2535) Co-authored-by: ItsEmpa Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/api/event/EventHandler.kt | 11 ++++++-- .../skyhanni/api/event/HandleEvent.kt | 9 ++++++- .../at/hannibal2/skyhanni/data/HypixelData.kt | 8 +++++- .../skyhanni/data/LocationFixData.kt | 19 +++++++++----- .../at/hannibal2/skyhanni/data/MiningAPI.kt | 25 +++++++++++-------- .../events/skyblock/AreaChangeEvents.kt | 6 +++++ .../features/combat/mobs/ArachneSpawnTimer.kt | 2 +- .../DungeonShadowAssassinNotification.kt | 2 +- .../diana/GriffinBurrowParticleFinder.kt | 2 +- .../event/diana/InquisitorWaypointShare.kt | 2 +- .../skyhanni/features/garden/GardenAPI.kt | 2 +- .../skyhanni/features/garden/pests/PestAPI.kt | 2 +- .../garden/pests/PestParticleWaypoint.kt | 2 +- .../garden/visitor/VisitorListener.kt | 4 +-- 14 files changed, 66 insertions(+), 30 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/events/skyblock/AreaChangeEvents.kt diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/EventHandler.kt b/src/main/java/at/hannibal2/skyhanni/api/event/EventHandler.kt index 78769060accf..4b4c008523e2 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/event/EventHandler.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/event/EventHandler.kt @@ -130,6 +130,13 @@ class EventHandler private constructor(val name: String, priv val invoker: Consumer, val options: HandleEvent, val generic: Class<*>?, - val onlyOnIslandTypes: Set = options.onlyOnIslands.toSet(), - ) + ) { + val onlyOnIslandTypes: Set = getIslands(options) + + companion object { + private fun getIslands(options: HandleEvent): Set = + if (options.onlyOnIslands.isEmpty()) setOf(options.onlyOnIsland) + else options.onlyOnIslands.toSet() + } + } } diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt b/src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt index c07239dc2ae9..a88f7f33724c 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/event/HandleEvent.kt @@ -12,8 +12,15 @@ annotation class HandleEvent( /** * If the event should only be received while on a specific skyblock island. + * To specify multiple islands, use [onlyOnIslands] instead. */ - vararg val onlyOnIslands: IslandType = [IslandType.ANY], + val onlyOnIsland: IslandType = IslandType.ANY, + + /** + * If the event should only be received while being on specific skyblock islands. + * To specify only one island, use [onlyOnIsland] instead. + */ + vararg val onlyOnIslands: IslandType = [], /** * The priority of when the event will be called, lower priority will be called first, see the companion object. diff --git a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt index 0cd806f577c7..b48805ef5bf5 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt @@ -13,6 +13,7 @@ import at.hannibal2.skyhanni.events.ProfileJoinEvent import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent import at.hannibal2.skyhanni.events.WidgetUpdateEvent import at.hannibal2.skyhanni.events.minecraft.ClientDisconnectEvent +import at.hannibal2.skyhanni.events.skyblock.ScoreboardAreaChangeEvent import at.hannibal2.skyhanni.features.bingo.BingoAPI import at.hannibal2.skyhanni.features.dungeon.DungeonAPI import at.hannibal2.skyhanni.features.rift.RiftAPI @@ -319,8 +320,13 @@ object HypixelData { loop@ for (line in ScoreboardData.sidebarLinesFormatted) { skyblockAreaPattern.matchMatcher(line) { val originalLocation = group("area").removeColor() - skyBlockArea = LocationFixData.fixLocation(skyBlockIsland) ?: originalLocation + val area = LocationFixData.fixLocation(skyBlockIsland) ?: originalLocation skyBlockAreaWithSymbol = line.trim() + if (area != skyBlockArea) { + val previousArea = skyBlockArea + skyBlockArea = area + ScoreboardAreaChangeEvent(area, previousArea).post() + } break@loop } } diff --git a/src/main/java/at/hannibal2/skyhanni/data/LocationFixData.kt b/src/main/java/at/hannibal2/skyhanni/data/LocationFixData.kt index 129e1cd956ec..20dfbb615722 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/LocationFixData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/LocationFixData.kt @@ -11,9 +11,9 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @SkyHanniModule object LocationFixData { - private var locationFixes = mutableListOf() + private var locationFixes = mutableMapOf>() - class LocationFix(val island: IslandType, val area: AxisAlignedBB, val realLocation: String) + private data class LocationFix(val area: AxisAlignedBB, val realLocation: String) // priority set to low so that IslandType can load their island names from repo earlier @SubscribeEvent(priority = EventPriority.LOW) @@ -26,11 +26,18 @@ object LocationFixData { val area = fix.a.axisAlignedTo(fix.b) val realLocation = fix.realLocation - locationFixes.add(LocationFix(island, area, realLocation)) + val list = locationFixes[island] + + val locationFix = LocationFix(area, realLocation) + + if (list == null) locationFixes[island] = listOf(locationFix) + else locationFixes[island] = list + locationFix } } - fun fixLocation(skyBlockIsland: IslandType) = locationFixes - .firstOrNull { skyBlockIsland == it.island && it.area.isPlayerInside() } - ?.realLocation + fun fixLocation(skyBlockIsland: IslandType): String? = + locationFixes[skyBlockIsland] + ?.find { it.area.isPlayerInside() } + ?.realLocation + } diff --git a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt index 758294ffc580..35855fc612d5 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt @@ -1,8 +1,10 @@ package at.hannibal2.skyhanni.data +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.events.BlockClickEvent import at.hannibal2.skyhanni.events.ColdUpdateEvent import at.hannibal2.skyhanni.events.DebugDataCollectEvent +import at.hannibal2.skyhanni.events.IslandChangeEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent @@ -11,6 +13,7 @@ import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent import at.hannibal2.skyhanni.events.ServerBlockChangeEvent import at.hannibal2.skyhanni.events.mining.OreMinedEvent import at.hannibal2.skyhanni.events.player.PlayerDeathEvent +import at.hannibal2.skyhanni.events.skyblock.ScoreboardAreaChangeEvent import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern import at.hannibal2.skyhanni.features.mining.OreBlock import at.hannibal2.skyhanni.features.mining.isTitanium @@ -69,8 +72,6 @@ object MiningAPI { var currentAreaOreBlocks = setOf() private set - private var lastSkyblockArea: String? = null - private val recentClickedBlocks = ConcurrentSet>() private val surroundingMinedBlocks = ConcurrentLinkedQueue>() private val allowedSoundNames = setOf("dig.glass", "dig.stone", "dig.gravel", "dig.cloth", "random.orb") @@ -203,10 +204,6 @@ object MiningAPI { @SubscribeEvent fun onTick(event: LorenzTickEvent) { if (!inCustomMiningIsland()) return - - if (LorenzUtils.lastWorldSwitch.passedSince() < 4.seconds) return - updateLocation() - if (currentAreaOreBlocks.isEmpty()) return // if somehow you take more than 20 seconds to mine a single block, congrats @@ -219,6 +216,17 @@ object MiningAPI { resetOreEvent() } + @HandleEvent + fun onAreaChange(event: ScoreboardAreaChangeEvent) { + if (!inCustomMiningIsland()) return + updateLocation() + } + + @SubscribeEvent + fun onIslandChange(event: IslandChangeEvent) { + updateLocation() + } + private fun runEvent() { resetOreEvent() @@ -287,11 +295,6 @@ object MiningAPI { } private fun updateLocation() { - val currentArea = LorenzUtils.skyBlockArea - // TODO add area change event with HypixelData.skyBlockArea instead - if (currentArea == lastSkyblockArea) return - lastSkyblockArea = currentArea - inGlacite = inGlaciteArea() inTunnels = inGlacialTunnels() inMineshaft = inMineshaft() diff --git a/src/main/java/at/hannibal2/skyhanni/events/skyblock/AreaChangeEvents.kt b/src/main/java/at/hannibal2/skyhanni/events/skyblock/AreaChangeEvents.kt new file mode 100644 index 000000000000..b262ea9fbe44 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/skyblock/AreaChangeEvents.kt @@ -0,0 +1,6 @@ +package at.hannibal2.skyhanni.events.skyblock + +import at.hannibal2.skyhanni.api.event.SkyHanniEvent + +// Detect area changes by looking at the scoreboard. +class ScoreboardAreaChangeEvent(val area: String, val previousArea: String?) : SkyHanniEvent() diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt index b3c9be188752..2fee0577e8f6 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt @@ -80,7 +80,7 @@ object ArachneSpawnTimer { } } - @HandleEvent(onlyOnIslands = [IslandType.SPIDER_DEN], priority = HandleEvent.LOW, receiveCancelled = true) + @HandleEvent(onlyOnIsland = IslandType.SPIDER_DEN, priority = HandleEvent.LOW, receiveCancelled = true) fun onPacketReceive(event: PacketReceivedEvent) { if (!saveNextTickParticles) return if (searchTime.passedSince() < 3.seconds) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonShadowAssassinNotification.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonShadowAssassinNotification.kt index abe83df8cbd4..ca888392f35c 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonShadowAssassinNotification.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonShadowAssassinNotification.kt @@ -16,7 +16,7 @@ object DungeonShadowAssassinNotification { private val config get() = SkyHanniMod.feature.dungeon - @HandleEvent(onlyOnIslands = [IslandType.CATACOMBS]) + @HandleEvent(onlyOnIsland = IslandType.CATACOMBS) fun onWorldBorderChange(event: PacketReceivedEvent) { if (!isEnabled()) return if (DungeonAPI.dungeonFloor?.contains("3") == true && DungeonAPI.inBossRoom) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/GriffinBurrowParticleFinder.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/GriffinBurrowParticleFinder.kt index 0f0220a76208..80d6ea0e0df9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/GriffinBurrowParticleFinder.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/GriffinBurrowParticleFinder.kt @@ -58,7 +58,7 @@ object GriffinBurrowParticleFinder { } } - @HandleEvent(onlyOnIslands = [IslandType.HUB], priority = HandleEvent.LOW, receiveCancelled = true) + @HandleEvent(onlyOnIsland = IslandType.HUB, priority = HandleEvent.LOW, receiveCancelled = true) fun onPacketReceive(event: PacketReceivedEvent) { if (!isEnabled()) return if (!config.burrowsSoopyGuess) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/InquisitorWaypointShare.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/InquisitorWaypointShare.kt index fbb599d68f90..2d0e2a809e81 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/InquisitorWaypointShare.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/InquisitorWaypointShare.kt @@ -222,7 +222,7 @@ object InquisitorWaypointShare { HypixelCommands.partyChat("x: $x, y: $y, z: $z ") } - @HandleEvent(onlyOnIslands = [IslandType.HUB], priority = HandleEvent.LOW, receiveCancelled = true) + @HandleEvent(onlyOnIsland = IslandType.HUB, priority = HandleEvent.LOW, receiveCancelled = true) fun onFirstChatEvent(event: PacketReceivedEvent) { if (!isEnabled()) return val packet = event.packet diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt index e188dcfd0d56..4e80bdbea678 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt @@ -90,7 +90,7 @@ object GardenAPI { "BINGHOE", ) - @HandleEvent(onlyOnIslands = [IslandType.GARDEN]) + @HandleEvent(onlyOnIsland = IslandType.GARDEN) fun onSendPacket(event: PacketSentEvent) { if (event.packet !is C09PacketHeldItemChange) return checkItemInHand() diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestAPI.kt index 7aefb74cd37c..d49de324c851 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestAPI.kt @@ -158,7 +158,7 @@ object PestAPI { PestUpdateEvent().post() } - @HandleEvent(onlyOnIslands = [IslandType.GARDEN]) + @HandleEvent(onlyOnIsland = IslandType.GARDEN) fun onPestSpawn(event: PestSpawnEvent) { PestSpawnTimer.lastSpawnTime = SimpleTimeMark.now() val plotNames = event.plotNames diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt index d3dd8a27ba10..23ead6f911d0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt @@ -121,7 +121,7 @@ object PestParticleWaypoint { ++particles } - @HandleEvent(onlyOnIslands = [IslandType.GARDEN]) + @HandleEvent(onlyOnIsland = IslandType.GARDEN) fun onFireWorkSpawn(event: PacketReceivedEvent) { if (event.packet !is S0EPacketSpawnObject) return if (!config.hideParticles) return diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt index e709fcda7633..66d03745f372 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt @@ -56,7 +56,7 @@ object VisitorListener { } // TODO make event - @HandleEvent(onlyOnIslands = [IslandType.GARDEN]) + @HandleEvent(onlyOnIsland = IslandType.GARDEN) fun onSendEvent(event: PacketSentEvent) { val packet = event.packet if (packet !is C02PacketUseEntity) return @@ -136,7 +136,7 @@ object VisitorListener { inventory.handleMouseClick_skyhanni(slot, slot.slotIndex, 0, 0) } - @HandleEvent(onlyOnIslands = [IslandType.GARDEN]) + @HandleEvent(onlyOnIsland = IslandType.GARDEN) fun onTooltip(event: ItemHoverEvent) { if (!GardenAPI.onBarnPlot) return if (!VisitorAPI.inInventory) return From 1739a5f13194bc136e169a7870a14c2704174be8 Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Sat, 21 Sep 2024 05:59:58 -0400 Subject: [PATCH 055/102] Feature: Hoppity Duplicate Count (#2556) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../event/hoppity/HoppityEggsConfig.java | 5 +++++ .../features/event/hoppity/HoppityAPI.kt | 20 +++++++++++-------- .../event/hoppity/HoppityCollectionStats.kt | 9 +++++++-- .../event/hoppity/HoppityEggsCompactChat.kt | 8 +++++++- .../event/hoppity/HoppityEggsManager.kt | 4 ++-- .../ChocolateFactoryBarnManager.kt | 12 ++++++++++- 6 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEggsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEggsConfig.java index b438d83f5c3b..8bbaa962ca26 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEggsConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEggsConfig.java @@ -197,6 +197,11 @@ public String toString() { } } + @Expose + @ConfigOption(name = "Show Duplicate Count", desc = "Show the number of previous finds of a duplicate Hoppity rabbit in chat messages.") + @ConfigEditorBoolean + public boolean showDuplicateNumber = false; + @Expose @ConfigOption( name = "Rabbit Pet Warning", diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt index 6c42f8a17002..93c9efc04021 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt @@ -6,11 +6,11 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.events.hoppity.EggFoundEvent import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_FACTORY_MILESTONE +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_SHOP_MILESTONE +import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.SIDE_DISH import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.eggFoundPattern import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.SIDE_DISH -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_SHOP_MILESTONE -import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_FACTORY_MILESTONE import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule @@ -35,6 +35,7 @@ object HoppityAPI { private var duplicate = false private var lastRarity = "" private var lastName = "" + private var lastNameCache = "" private var newRabbit = false private var lastMeal: HoppityEggType? = null private var lastDuplicateAmount: Long? = null @@ -51,6 +52,7 @@ object HoppityAPI { this.lastDuplicateAmount = null } + fun getLastRabbit(): String = this.lastNameCache fun isHoppityEvent() = (SkyblockSeason.currentSeason == SkyblockSeason.SPRING || SkyHanniMod.feature.dev.debug.alwaysHoppitys) fun rarityByRabbit(rabbit: String): LorenzRarity? = hoppityRarities.firstOrNull { it.chatColorCode == rabbit.substring(0, 2) } @@ -69,7 +71,7 @@ object HoppityAPI { */ private val sideDishNamePattern by ChocolateFactoryAPI.patternGroup.pattern( "rabbit.sidedish", - "(?:§.)*?Golden Rabbit (?:§.)?- (?:§.)?Side Dish" + "(?:§.)*?Golden Rabbit (?:§.)?- (?:§.)?Side Dish", ) /** @@ -95,12 +97,12 @@ object HoppityAPI { if (!ChocolateFactoryAPI.inChocolateFactory) return InventoryUtils.getItemsInOpenChest().filter { it.stack.hasDisplayName() && - it.stack.getMinecraftId().toString() == "minecraft:skull" && - it.stack.getLore().isNotEmpty() + it.stack.getMinecraftId().toString() == "minecraft:skull" && + it.stack.getLore().isNotEmpty() }.forEach { ChocolateFactoryStrayTracker.strayCaughtPattern.matchMatcher(it.stack.displayName) { ChocolateFactoryStrayTracker.handleStrayClicked(it) - when(groupOrNull("name") ?: return@matchMatcher) { + when (groupOrNull("name") ?: return@matchMatcher) { "Fish the Rabbit" -> EggFoundEvent(HoppityEggType.STRAY, it.slotNumber, null).post() "El Dorado" -> EggFoundEvent(HoppityEggType.STRAY, it.slotNumber, null).post() else -> return@matchMatcher @@ -140,9 +142,10 @@ object HoppityAPI { } } - @SubscribeEvent + @SubscribeEvent(priority = EventPriority.HIGH) fun onChat(event: LorenzChatEvent) { if (!LorenzUtils.inSkyBlock) return + eggFoundPattern.matchMatcher(event.message) { resetRabbitData() lastMeal = getEggType(event) @@ -160,6 +163,7 @@ object HoppityAPI { HoppityEggsManager.rabbitFoundPattern.matchMatcher(event.message) { lastName = group("name") + lastNameCache = lastName lastRarity = group("rarity") attemptFireRabbitFound() } diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt index 3782919666dd..7be0913e9fb2 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt @@ -10,6 +10,7 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.CollectionUtils.addString import at.hannibal2.skyhanni.utils.CollectionUtils.collectWhile import at.hannibal2.skyhanni.utils.CollectionUtils.consumeWhile @@ -449,10 +450,14 @@ object HoppityCollectionStats { return table } - fun incrementRabbit(name: String) { + fun getRabbitCount(name: String): Int = name.removeColor().run { + loggedRabbits[this]?.takeIf { HoppityCollectionData.isKnownRabbit(this) } ?: 0 + } + + fun incrementRabbitCount(name: String) { val rabbit = name.removeColor() if (!HoppityCollectionData.isKnownRabbit(rabbit)) return - loggedRabbits[rabbit] = (loggedRabbits[rabbit] ?: 0) + 1 + loggedRabbits.addOrPut(rabbit, 1) } // Gets the found rabbits according to the Hypixel progress bar diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt index abbdff1f023b..4a33820d8d55 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt @@ -94,9 +94,15 @@ object HoppityEggsCompactChat { ChocolateFactoryAPI.timeUntilNeed(it).format(maxUnits = 2) } ?: "?" + val dupeNumberFormat = if (eventConfig.showDuplicateNumber) { + (HoppityCollectionStats.getRabbitCount(this.lastName) - 1).takeIf { it > 1}?.let { + " §7(§b#$it§7)" + } ?: "" + } else "" + val showDupeRarity = rarityConfig.let { it == RarityType.BOTH || it == RarityType.DUPE } val timeStr = if (config.showDuplicateTime) ", §a+§b$timeFormatted§7" else "" - "$mealNameFormat! §7Duplicate ${if (showDupeRarity) "$lastRarity " else ""}$lastName §7(§6+$format Chocolate§7$timeStr)" + "$mealNameFormat! §7Duplicate ${if (showDupeRarity) "$lastRarity " else ""}$lastName$dupeNumberFormat §7(§6+$format Chocolate§7$timeStr)" } else if (newRabbit) { val showNewRarity = rarityConfig.let { it == RarityType.BOTH || it == RarityType.NEW } "$mealNameFormat! §d§lNEW ${if (showNewRarity) "$lastRarity " else ""}$lastName §7(${lastProfit}§7)" diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt index 75b65fdc48ac..b8143d311cf2 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt @@ -122,7 +122,7 @@ object HoppityEggsManager { @HandleEvent fun onRabbitFound(event: RabbitFoundEvent) { - HoppityCollectionStats.incrementRabbit(event.rabbitName) + HoppityCollectionStats.incrementRabbitCount(event.rabbitName) } @SubscribeEvent @@ -233,7 +233,7 @@ object HoppityEggsManager { val message = "All $amount Hoppity Eggs are ready to be found!" if (config.warpUnclaimedEggs) { val (action, actionName) = if (LorenzUtils.inSkyBlock) { - { HypixelCommands.warp(config.warpDestination) } to "${"warp to ${config.warpDestination}".trim()}" + { HypixelCommands.warp(config.warpDestination) } to "warp to ${config.warpDestination}".trim() } else { { HypixelCommands.skyblock() } to "join /skyblock!" } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt index ccb6712c0538..a2b264b358f5 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryBarnManager.kt @@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.features.event.hoppity.HoppityAPI import at.hannibal2.skyhanni.features.event.hoppity.HoppityCollectionData +import at.hannibal2.skyhanni.features.event.hoppity.HoppityCollectionStats import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsCompactChat import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule @@ -15,6 +16,7 @@ import at.hannibal2.skyhanni.utils.NumberUtil.formatLong import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.SoundUtils import at.hannibal2.skyhanni.utils.TimeUtils.format +import net.minecraft.util.ChatComponentText import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @SkyHanniModule @@ -40,7 +42,7 @@ object ChocolateFactoryBarnManager { if (!LorenzUtils.inSkyBlock) return HoppityEggsManager.newRabbitFound.matchMatcher(event.message) { - val profileStorage = profileStorage ?: return + val profileStorage = profileStorage ?: return@matchMatcher profileStorage.currentRabbits += 1 trySendBarnFullMessage(inventory = false) HoppityEggsManager.shareWaypointPrompt() @@ -58,6 +60,14 @@ object ChocolateFactoryBarnManager { ChocolateAmount.addToAll(amount) HoppityEggsCompactChat.compactChat(event, lastDuplicateAmount = amount) HoppityAPI.attemptFireRabbitFound(lastDuplicateAmount = amount) + + if (hoppityConfig.showDuplicateNumber && !hoppityConfig.compactChat) { + (HoppityCollectionStats.getRabbitCount(HoppityAPI.getLastRabbit()) - 1).takeIf { it > 1 }?.let { + event.chatComponent = ChatComponentText( + event.message.replace("§7§lDUPLICATE RABBIT!", "§7§lDUPLICATE RABBIT! §7(Duplicate §b#$it§7)§r"), + ) + } + } } rabbitCrashedPattern.matchMatcher(event.message) { From 1052203d26f8ded5d7a1b1eb25a32e0ce2e6c784 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:03:18 +0200 Subject: [PATCH 056/102] Feature: Carry Tracker (#2185) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/config/ConfigManager.kt | 1 + .../skyhanni/config/commands/Commands.kt | 23 +- .../config/features/misc/MiscConfig.java | 3 + .../skyhanni/data/OtherPlayersSlayerAPI.kt | 36 ++ .../data/jsonobjects/repo/CarryTrackerJson.kt | 8 + .../events/entity/slayer/SlayerDeathEvent.kt | 6 + .../skyhanni/features/misc/CarryTracker.kt | 326 ++++++++++++++++++ 7 files changed, 391 insertions(+), 12 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/data/OtherPlayersSlayerAPI.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/CarryTrackerJson.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/events/entity/slayer/SlayerDeathEvent.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt index 4ac3f7925ce5..bf0f2ba0a4a4 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt @@ -104,6 +104,7 @@ class ConfigManager { // commands "features.garden.GardenConfig.cropSpeedMeterPos", "features.misc.MiscConfig.collectionCounterPos", + "features.misc.MiscConfig.carryPosition", "features.misc.MiscConfig.lockedMouseDisplay", // debug features 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 9b85c2cf9590..cf4679e396d6 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -64,6 +64,7 @@ import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTrac import at.hannibal2.skyhanni.features.mining.glacitemineshaft.CorpseTracker import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker import at.hannibal2.skyhanni.features.minion.MinionFeatures +import at.hannibal2.skyhanni.features.misc.CarryTracker import at.hannibal2.skyhanni.features.misc.CollectionTracker import at.hannibal2.skyhanni.features.misc.LockMouseLook import at.hannibal2.skyhanni.features.misc.MarkedPlayerManager @@ -183,7 +184,6 @@ object Commands { registerCommand("shwords", "Opens the config list for modifying visual words") { openVisualWords() } } - private fun usersNormal() { registerCommand( "shmarkplayer", @@ -192,8 +192,7 @@ object Commands { registerCommand("shtrackcollection", "Tracks your collection gain over time") { CollectionTracker.command(it) } registerCommand( "shcroptime", - "Calculates with your current crop per second speed " + - "how long you need to farm a crop to collect this amount of items", + "Calculates with your current crop per second speed how long you need to farm a crop to collect this amount of items", ) { GardenCropTimeCommand.onCommand(it) } registerCommand( "shcropsin", @@ -250,8 +249,7 @@ object Commands { ) { FarmingWeightDisplay.lookUpCommand(it) } registerCommand( "shcopytranslation", - "Copy the English translation of a message in another language to the clipboard.\n" + - "Uses a 2 letter language code that can be found at the end of a translation message.", + "Copy the English translation of a message in another language to the clipboard.\n" + "Uses a 2 letter language code that can be found at the end of a translation message.", ) { Translator.fromEnglish(it) } registerCommand( "shtranslate", @@ -367,8 +365,12 @@ object Commands { ) { ColorFormattingHelper.printColorCodeList() } registerCommand( "shtps", - "Informs in chat about the server ticks per second (TPS)." + "Informs in chat about the server ticks per second (TPS).", ) { TpsCounter.tpsCommand() } + registerCommand( + "shcarry", + "Keep track of carries you do.", + ) { CarryTracker.onCommand(it) } } private fun usersBugFix() { @@ -490,7 +492,7 @@ object Commands { ) { SkyBlockIslandTest.onCommand(it) } registerCommand( "shdebugprice", - "Debug different price sources for an item." + "Debug different price sources for an item.", ) { ItemPriceUtils.debugItemPrice(it) } registerCommand( "shdebugscoreboard", @@ -585,9 +587,7 @@ object Commands { ) { TitleManager.command(it) } registerCommand( "shresetconfig", - "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).", + "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.resetConfigCommand() } registerCommand( "shreadcropmilestonefromclipboard", @@ -664,8 +664,7 @@ object Commands { else -> currentStream } - val switchingToBeta = updateStream == UpdateStream.BETA && - (currentStream != UpdateStream.BETA || !UpdateManager.isCurrentlyBeta()) + val switchingToBeta = updateStream == UpdateStream.BETA && (currentStream != UpdateStream.BETA || !UpdateManager.isCurrentlyBeta()) if (switchingToBeta) { ChatUtils.clickableChat( "Are you sure you want to switch to beta? These versions may be less stable.", diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java index 03b0bf779f61..9d503c3986be 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java @@ -144,6 +144,9 @@ public class MiscConfig { @Expose public Position collectionCounterPos = new Position(10, 10, false, true); + @Expose + public Position carryPosition = new Position(10, 10, false, true); + @Expose @ConfigOption(name = "Brewing Stand Overlay", desc = "Display the item names directly inside the Brewing Stand.") @ConfigEditorBoolean diff --git a/src/main/java/at/hannibal2/skyhanni/data/OtherPlayersSlayerAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/OtherPlayersSlayerAPI.kt new file mode 100644 index 000000000000..0401746c4edb --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/OtherPlayersSlayerAPI.kt @@ -0,0 +1,36 @@ +package at.hannibal2.skyhanni.data + +import at.hannibal2.skyhanni.data.mob.Mob +import at.hannibal2.skyhanni.events.MobEvent +import at.hannibal2.skyhanni.events.entity.slayer.SlayerDeathEvent +import at.hannibal2.skyhanni.features.slayer.SlayerType +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.test.command.ErrorManager +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object OtherPlayersSlayerAPI { + + @SubscribeEvent + fun onMobDespawn(event: MobEvent.DeSpawn.SkyblockMob) { + val mob = event.mob + + // no death, rather despawn because too far away + if (mob.baseEntity.health != 0f) return + + if (mob.mobType != Mob.Type.SLAYER) return + + val owner = mob.owner?.ownerName + val tier = mob.levelOrTier + val name = mob.name + val slayerType = SlayerType.getByName(name) ?: run { + ErrorManager.logErrorStateWithData( + "Unknown slayer type found", "unknown slayer", + "name" to name, + ) + return + } + + SlayerDeathEvent(slayerType, tier, owner).post() + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/CarryTrackerJson.kt b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/CarryTrackerJson.kt new file mode 100644 index 000000000000..1819b36b9517 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/repo/CarryTrackerJson.kt @@ -0,0 +1,8 @@ +package at.hannibal2.skyhanni.data.jsonobjects.repo + +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName + +data class CarryTrackerJson( + @Expose @SerializedName("slayer_names") val slayerNames: Map>, +) diff --git a/src/main/java/at/hannibal2/skyhanni/events/entity/slayer/SlayerDeathEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/entity/slayer/SlayerDeathEvent.kt new file mode 100644 index 000000000000..620e4fb3e703 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/entity/slayer/SlayerDeathEvent.kt @@ -0,0 +1,6 @@ +package at.hannibal2.skyhanni.events.entity.slayer + +import at.hannibal2.skyhanni.api.event.SkyHanniEvent +import at.hannibal2.skyhanni.features.slayer.SlayerType + +class SlayerDeathEvent(val slayerType: SlayerType, val tier: Int, val owner: String?) : SkyHanniEvent() diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt new file mode 100644 index 000000000000..f70fa58b8517 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt @@ -0,0 +1,326 @@ +package at.hannibal2.skyhanni.features.misc + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.data.jsonobjects.repo.CarryTrackerJson +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.events.entity.slayer.SlayerDeathEvent +import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboardUtils.formatNum +import at.hannibal2.skyhanni.features.slayer.SlayerType +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.CollectionUtils.addString +import at.hannibal2.skyhanni.utils.HypixelCommands +import at.hannibal2.skyhanni.utils.KeyboardManager +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble +import at.hannibal2.skyhanni.utils.NumberUtil.formatDoubleOrUserError +import at.hannibal2.skyhanni.utils.NumberUtil.formatIntOrUserError +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher +import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables +import at.hannibal2.skyhanni.utils.StringUtils.cleanPlayerName +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.time.Duration.Companion.seconds + +/** + * TODO more carry features + * save on restart + * support for Dungeon, Kuudra, crimson minibosses + * average spawn time per slayer customer + * change customer name color if offline, onlilne, on your island + * show time since last boss died next to slayer customer name + * highlight slayer bosses for slayer customers + * automatically mark customers with /shmarkplaayers + * show a line behind them + */ + +@SkyHanniModule +object CarryTracker { + private val config get() = SkyHanniMod.feature.misc + + private val customers = mutableListOf() + private val carryTypes = mutableMapOf() + private var slayerNames = emptyMap>() + + private var display = listOf() + + private val patternGroup = RepoPattern.group("carry") + + /** + * REGEX-TEST: + * §6Trade completed with §r§b[MVP§r§c+§r§b] ClachersHD§r§f§r§6! + */ + private val tradeCompletedPattern by patternGroup.pattern( + "trade.completed", + "§6Trade completed with (?.*)§r§6!", + ) + + /** + * REGEX-TEST: + * §r§a§l+ §r§6500k coins + */ + private val rawNamePattern by patternGroup.pattern( + "trade.coins.gained", + " §r§a§l\\+ §r§6(?.*) coins", + ) + + @HandleEvent + fun onSlayerDeath(event: SlayerDeathEvent) { + val slayerType = event.slayerType + val tier = event.tier + val owner = event.owner + for (customer in customers) { + if (!customer.name.equals(owner, ignoreCase = true)) continue + for (carry in customer.carries) { + val type = carry.type as? SlayerCarryType ?: return + if (type.slayerType != slayerType) continue + if (type.tier != tier) continue + carry.done++ + if (carry.done == carry.requested) { + ChatUtils.chat("Carry done for ${customer.name}!") + LorenzUtils.sendTitle("§eCarry done!", 3.seconds) + } + update() + } + } + } + + // TODO create trade event with player name, coins and items + var lastTradedPlayer = "" + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + tradeCompletedPattern.matchMatcher(event.message) { + lastTradedPlayer = group("name").cleanPlayerName() + } + + rawNamePattern.matchMatcher(event.message) { + val coinsGained = group("coins").formatDouble() + getCustomer(lastTradedPlayer).alreadyPaid += coinsGained + update() + } + } + + @SubscribeEvent + fun onRepoReload(event: RepositoryReloadEvent) { + val data = event.getConstant("CarryTracker") + slayerNames = data.slayerNames.mapKeys { SlayerType.valueOf(it.key) } + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent) { + if (!LorenzUtils.inSkyBlock) return + + config.carryPosition.renderRenderables(display, posLabel = "Carry Tracker") + } + + fun onCommand(args: Array) { + if (args.size < 2 || args.size > 3) { + ChatUtils.userError("Usage:\n§c/shcarry \n§c/shcarry ") + return + } + if (args.size == 2) { + setPrice(args[0], args[1]) + return + } + + val customerName = args[0] + + val rawType = args[1] + val carryType = getCarryType(rawType) ?: return + + val amountRequested = args[2].formatIntOrUserError() ?: return + + val newCarry = Carry(carryType, amountRequested) + + for (customer in customers) { + if (!customer.name.equals(customerName, ignoreCase = true)) continue + val carries = customer.carries + for (carry in carries.toList()) { + if (!newCarry.type.sameType(carry.type)) continue + val newAmountRequested = carry.requested + amountRequested + if (newAmountRequested < 1) { + ChatUtils.userError("New carry amount requested must be positive!") + return + } + carries.remove(carry) + val updatedCarry = Carry(carryType, newAmountRequested) + updatedCarry.done = carry.done + carries.add(updatedCarry) + update() + ChatUtils.chat("Updated carry: §b$customerName §8x$newAmountRequested ${newCarry.type}") + return + } + } + if (amountRequested < 1) { + ChatUtils.userError("Carry amount requested must be positive!") + return + } + + val customer = getCustomer(customerName) + customer.carries.add(newCarry) + update() + ChatUtils.chat("Started carry: §b$customerName §8x$amountRequested ${newCarry.type}") + } + + private fun getCarryType(rawType: String): CarryType? = carryTypes.getOrPut(rawType) { + createCarryType(rawType) ?: run { + ChatUtils.userError("Unknown carry type: '$rawType'! Use e.g. rev5, sven4, eman3, blaze2..") + return null + } + } + + private fun setPrice(rawType: String, rawPrice: String) { + val carryType = getCarryType(rawType) ?: return + + val price = rawPrice.formatDoubleOrUserError() ?: return + carryType.pricePer = price + update() + ChatUtils.chat("Set carry price for $carryType §eto §6${price.formatNum()} coins.") + } + + private fun getCustomer(customerName: String): Customer { + for (customer in customers) { + if (customer.name.equals(customerName, ignoreCase = true)) { + return customer + } + } + val customer = Customer(customerName) + customers.add(customer) + return customer + } + + private fun CarryType.sameType(other: CarryType): Boolean = name == other.name && tier == other.tier + + private fun update() { + val list = mutableListOf() + if (customers.none { it.carries.isNotEmpty() }) { + display = emptyList() + return + } + list.addString("§c§lCarries") + for (customer in customers) { + if (customer.carries.isEmpty()) continue + addCustomerName(customer, list) + + val carries = customer.carries + for (carry in carries) { + val requested = carry.requested + val done = carry.done + val missing = requested - done + + val color = if (done > requested) "§c" else if (done == requested) "§a" else "§e" + val cost = formatCost(carry.type.pricePer?.let { it * requested }) + val text = "$color$done§8/$color$requested $cost" + list.add( + Renderable.clickAndHover( + Renderable.string(" ${carry.type} $text"), + tips = buildList { + add("§b${customer.name}' ${carry.type} §cCarry") + add("") + add("§7Requested: §e$requested") + add("§7Done: §e$done") + add("§7Missing: §e$missing") + add("") + if (cost != "") { + add("§7Total cost: §e${cost}") + add("§7Cost per carry: §e${formatCost(carry.type.pricePer)}") + } else { + add("§cNo price set for this carry!") + add("§7Set a price with §e/shcarry ") + } + add("") + add("§eClick to send current progress in the party chat!") + add("§eControl-click to remove this carry!") + }, + onClick = { + if (KeyboardManager.isModifierKeyDown()) { + carries.remove(carry) + update() + } else { + HypixelCommands.partyChat( + "${customer.name} ${carry.type.toString().removeColor()} carry: $done/$requested", + ) + } + }, + ), + ) + } + } + display = list + } + + private fun addCustomerName(customer: Customer, list: MutableList) { + val customerName = customer.name + val totalCost = customer.carries.sumOf { it.getCost() ?: 0.0 } + val totalCostFormat = formatCost(totalCost) + if (totalCostFormat != "") { + val paidFormat = "§6${customer.alreadyPaid.formatNum()}" + val missingFormat = formatCost(totalCost - customer.alreadyPaid) + list.add( + Renderable.clickAndHover( + Renderable.string("§b$customerName $paidFormat§8/${totalCostFormat}"), + tips = listOf( + "§7Carries for §b$customerName", + "", + "§7Total cost: $totalCostFormat", + "§7Already paid: $paidFormat", + "§7Still missing: $missingFormat", + "", + "§eClick to send missing coins in party chat!", + ), + onClick = { + HypixelCommands.partyChat( + "$customerName Carry: already paid: ${paidFormat.removeColor()}, " + "still missing: ${missingFormat.removeColor()}", + ) + }, + ), + ) + + } else { + list.addString("§b$customerName$totalCostFormat") + } + } + + private fun Carry.getCost(): Double? { + return type.pricePer?.let { + requested * it + }?.takeIf { it != 0.0 } + } + + private fun formatCost(totalCost: Double?): String = if (totalCost == 0.0 || totalCost == null) "" else "§6${totalCost.formatNum()}" + + private fun createCarryType(input: String): CarryType? { + if (input.length == 1) return null + val rawName = input.dropLast(1).lowercase() + val tier = input.last().digitToIntOrNull() ?: return null + + getSlayerType(rawName)?.let { + return SlayerCarryType(it, tier) + } + + return null + } + + private fun getSlayerType(name: String): SlayerType? = slayerNames.entries.find { name in it.value }?.key + + class Customer( + val name: String, + var alreadyPaid: Double = 0.0, + val carries: MutableList = mutableListOf(), + ) + + class Carry(val type: CarryType, val requested: Int, var done: Int = 0) + + abstract class CarryType(val name: String, val tier: Int, var pricePer: Double? = null) { + override fun toString(): String = "§d$name $tier" + } + + class SlayerCarryType(val slayerType: SlayerType, tier: Int) : CarryType(slayerType.displayName, tier) +// class DungeonCarryType(val floor: DungeonFloor, masterMode: Boolean) : CarryType(floor.name, tier) +} From 0aa7a605a23f3b1740a481c39ca58835c53d0650 Mon Sep 17 00:00:00 2001 From: Clicks <58398364+CuzImClicks@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:20:10 +0200 Subject: [PATCH 057/102] Feature: Highlight Active Beacon Effect (#2546) Co-authored-by: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../features/inventory/InventoryConfig.java | 6 ++ .../features/inventory/ActiveBeaconEffect.kt | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/ActiveBeaconEffect.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java index 062f29b30e33..510f953ca2f9 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java @@ -292,4 +292,10 @@ public String toString() { @ConfigEditorBoolean @FeatureToggle public boolean hexAsColorInLore = true; + + @Expose + @ConfigOption(name = "Highlight Active Beacon Effect", desc = "Highlights the currently selected beacon effect in the beacon inventory.") + @ConfigEditorBoolean + @FeatureToggle + public boolean highlightActiveBeaconEffect = true; } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/ActiveBeaconEffect.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/ActiveBeaconEffect.kt new file mode 100644 index 000000000000..d0bc67fde543 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/ActiveBeaconEffect.kt @@ -0,0 +1,69 @@ +package at.hannibal2.skyhanni.features.inventory + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.RenderUtils.highlight +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object ActiveBeaconEffect { + + val config get() = SkyHanniMod.feature.inventory + + private val patternGroup = RepoPattern.group("inventory.activebeaconeffect") + + /** + * REGEX-TEST: Profile Stat Upgrades + */ + private val inventoryPattern by patternGroup.pattern( + "inventory", + "Profile Stat Upgrades", + ) + + /** + * REGEX-TEST: §aActive stat boost! + */ + private val slotPattern by patternGroup.pattern( + "slot.active", + "§aActive stat boost!", + ) + + private var slot: Int? = null + + @SubscribeEvent + fun onInventoryFullyOpened(event: InventoryFullyOpenedEvent) { + if (!isEnabled()) return + if (!inventoryPattern.matches(event.inventoryName)) { + slot = null + return + } + + slot = event.inventoryItems.filter { (_, stack) -> + stack.getLore().any { slotPattern.matches(it) } + }.firstNotNullOfOrNull { it.key } + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + slot = null + } + + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (!isEnabled()) return + val slot = slot ?: return + + event.gui.inventorySlots.getSlot(slot) highlight LorenzColor.GREEN + } + + fun isEnabled() = IslandType.PRIVATE_ISLAND.isInIsland() && config.highlightActiveBeaconEffect +} From aca98055728de99808e5d07c4ca1aea72773e703 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:24:57 +0200 Subject: [PATCH 058/102] added docs to InventoryFullyOpenedEvent --- .../skyhanni/events/InventoryFullyOpenedEvent.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/at/hannibal2/skyhanni/events/InventoryFullyOpenedEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/InventoryFullyOpenedEvent.kt index d786445fbf5e..5374b95d3c2e 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/InventoryFullyOpenedEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/InventoryFullyOpenedEvent.kt @@ -26,6 +26,17 @@ open class InventoryOpenEvent(private val inventory: OtherInventoryData.Inventor val fullyOpenedOnce: Boolean get() = inventory.fullyOpenedOnce } +/** + * This event is getting fired after every slot in the newly opened inventory has item data. + * + * New inventory data gets first sent as an empty inventory from the server. + * Item stack slot information is sent afterwards, sometimes with a short delay. + * + * This approach is faster than to wait a fix duration after the inventory open packet is detected. + * + * Since this logic only works via packets, and the player inventory (pressing E) is client side, + * this event does not get fired when opening the invenotory via pressingE. + */ class InventoryFullyOpenedEvent(inventory: OtherInventoryData.Inventory) : InventoryOpenEvent(inventory) class InventoryUpdatedEvent(inventory: OtherInventoryData.Inventory) : InventoryOpenEvent(inventory) From 824716488f4c73d16458cad82bb13dd5b1008fdd Mon Sep 17 00:00:00 2001 From: Clicks <58398364+CuzImClicks@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:33:00 +0200 Subject: [PATCH 059/102] Feature: Excavator Tooltip Hider (#2539) --- .../mining/FossilExcavatorConfig.java | 8 +++++++ .../features/inventory/HarpFeatures.kt | 2 +- .../fossilexcavator/ExcavatorTooltipHider.kt | 22 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java index 176c8f3bda35..8dfb12854232 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java @@ -36,4 +36,12 @@ public class FossilExcavatorConfig { @FeatureToggle public boolean glacitePowderStack = false; + @Expose + @ConfigOption( + name = "Hide Excavator Tooltips", + desc = "Hides tooltips of items inside of the Fossil Excavator." + ) + @ConfigEditorBoolean + @FeatureToggle + public boolean hideExcavatorTooltips = true; } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt index 75b1d6ad3cd3..a79a66717320 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt @@ -213,6 +213,6 @@ object HarpFeatures { if (!config.hideMelodyTooltip) return if (!isHarpGui(InventoryUtils.openInventoryName())) return if (event.slot.inventory !is ContainerLocalMenu) return - event.cancel() + event.cancel() } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt new file mode 100644 index 000000000000..8b58402cdd63 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt @@ -0,0 +1,22 @@ +package at.hannibal2.skyhanni.features.mining.fossilexcavator + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.LorenzToolTipEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import net.minecraft.client.player.inventory.ContainerLocalMenu +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object ExcavatorTooltipHider { + + private val config get() = SkyHanniMod.feature.mining.fossilExcavator + + @SubscribeEvent + fun onTooltip(event: LorenzToolTipEvent) { + if (!isEnabled()) return + if (event.slot.inventory !is ContainerLocalMenu) return + event.cancel() + } + + fun isEnabled() = FossilExcavatorAPI.inInventory && !FossilExcavatorAPI.inExcavatorMenu && config.hideExcavatorTooltips +} From 9db106b981b827ea254ca7dbe8cd924dc90375eb Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sat, 21 Sep 2024 13:34:28 +0200 Subject: [PATCH 060/102] formatting --- .../skyhanni/features/inventory/HarpFeatures.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt index a79a66717320..d6d79b31a710 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt @@ -44,15 +44,15 @@ object HarpFeatures { private val patternGroup = RepoPattern.group("harp") private val inventoryTitlePattern by patternGroup.pattern( "inventory", - "Harp.*" + "Harp.*", ) private val menuTitlePattern by patternGroup.pattern( "menu", - "Melody.*" + "Melody.*", ) private val songSelectedPattern by patternGroup.pattern( "song.selected", - "§aSong is selected!" + "§aSong is selected!", ) private fun isHarpGui(chestName: String) = inventoryTitlePattern.matches(chestName) @@ -77,7 +77,7 @@ object HarpFeatures { 37 + index, 2, 3, - Minecraft.getMinecraft().thePlayer + Minecraft.getMinecraft().thePlayer, ) // middle clicks > left clicks lastClick = SimpleTimeMark.now() break @@ -181,7 +181,7 @@ object HarpFeatures { it, event.clickedButton, event.clickType, - Minecraft.getMinecraft().thePlayer + Minecraft.getMinecraft().thePlayer, ) } } From d2072b83949b90bd7c2f5bfa6bd0c08941953879 Mon Sep 17 00:00:00 2001 From: martimavocado <39881008+martimavocado@users.noreply.github.com> Date: Sat, 21 Sep 2024 22:13:15 +0100 Subject: [PATCH 061/102] Feature: Force locale for Number Formatting (#2563) --- .../skyhanni/config/features/dev/DevConfig.java | 7 +++++++ src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java index b62c88efc61f..6daf37cfda74 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java @@ -108,6 +108,13 @@ public class DevConfig { @ConfigEditorBoolean public boolean fancySbaContributors = false; + @Expose + @ConfigOption( + name = "Number Format Override", + desc = "Forces the number format to use the en_US locale.") + @ConfigEditorBoolean + public boolean numberFormatOverride = false; + @Expose @Category(name = "Minecraft Console", desc = "Minecraft Console Settings") public MinecraftConsoleConfig minecraftConsoles = new MinecraftConsoleConfig(); diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt index b97298ce43dd..4d5fe0a1dd6f 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt @@ -1,14 +1,18 @@ package at.hannibal2.skyhanni.utils +import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.RegexUtils.matches import java.text.NumberFormat +import java.util.Locale import java.util.TreeMap import kotlin.math.pow import kotlin.math.roundToInt object NumberUtil { + private val config get() = SkyHanniMod.feature + private val suffixes = TreeMap().apply { this[1000L] = "k" this[1000000L] = "M" @@ -109,7 +113,10 @@ object NumberUtil { return this.toString() + this.ordinal() } - fun Number.addSeparators() = NumberFormat.getNumberInstance().format(this) + fun Number.addSeparators(): String { + return if (!config.dev.numberFormatOverride) NumberFormat.getNumberInstance().format(this) + else NumberFormat.getNumberInstance(Locale.US).format(this) + } fun String.romanToDecimalIfNecessary() = toIntOrNull() ?: romanToDecimal() From 1ae2d448cc34c20c34c0aee1a97bdd01801a6b66 Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Sat, 21 Sep 2024 23:14:34 +0200 Subject: [PATCH 062/102] Fix: ReforgeAPI fix hard crash for unkown stat (#2562) --- .../java/at/hannibal2/skyhanni/api/ReforgeAPI.kt | 14 +++++++++++++- .../hannibal2/skyhanni/data/model/SkyblockStat.kt | 6 ++++++ .../skyhanni/utils/json/SkyHanniTypeAdapters.kt | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt index 987a864af77e..534d38d47c82 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/ReforgeAPI.kt @@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.data.model.SkyblockStat import at.hannibal2.skyhanni.data.model.SkyblockStatList import at.hannibal2.skyhanni.events.NeuRepositoryReloadEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.ItemCategory import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull @@ -176,7 +177,18 @@ object ReforgeAPI { while (reader.hasNext()) { val name = reader.nextName() val value = reader.nextDouble() - list[SkyblockStat.valueOf(name.uppercase())] = value + + val stat = SkyblockStat.getValueOrNull(name.uppercase()) ?: run { + ErrorManager.logErrorStateWithData( + "Unknown stat: '${name.uppercase()}'", + "Stat list could not parse stat", + "failed" to name.uppercase(), + betaOnly = true, + ) + continue + } + + list[stat] = value } reader.endObject() return list diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt index 182309634f72..f31c95958659 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/SkyblockStat.kt @@ -53,6 +53,8 @@ enum class SkyblockStat(val icon: String) { FEAR("§a☠"), HEAT_RESISTANCE("§c♨"), + + UNKNOWN("§c?") ; val capitalizedName = name.lowercase().allLettersFirstUppercase() @@ -65,6 +67,10 @@ enum class SkyblockStat(val icon: String) { val fontSizeOfLargestIcon by lazy { entries.maxOf { Minecraft.getMinecraft().fontRendererObj.getStringWidth(it.icon) } + 1 } + + fun getValueOrNull(string: String): SkyblockStat? = entries.firstOrNull { it.name == string } + + fun getValue(string: String): SkyblockStat = getValueOrNull(string) ?: UNKNOWN } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt index 2048be522061..13aec1d92b6a 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/SkyHanniTypeAdapters.kt @@ -67,7 +67,7 @@ object SkyHanniTypeAdapters { val SKYBLOCK_STAT: TypeAdapter = SimpleStringTypeAdapter( { name.lowercase() }, - { SkyblockStat.valueOf(this.uppercase()) }, + { SkyblockStat.getValue(this.uppercase()) }, ) val TRACKER_DISPLAY_MODE = SimpleStringTypeAdapter.forEnum() From fa8096a38e7364f7912361f81df4127ad7a231dd Mon Sep 17 00:00:00 2001 From: ILike2WatchMemes Date: Sat, 21 Sep 2024 23:37:15 +0200 Subject: [PATCH 063/102] Fix: Experiments Profit Tracker & Superpairs Data (#2560) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> --- .../ExperimentsProfitTracker.kt | 54 ++++++++++--------- .../SuperpairExperimentInformationDisplay.kt | 19 ++++--- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt index 16c0305cbedd..7b6fa9bc5f1e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt @@ -18,6 +18,7 @@ import at.hannibal2.skyhanni.features.inventory.experimentationtable.Experimenta import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString +import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemPriceUtils.getNpcPriceOrNull import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice @@ -42,6 +43,7 @@ import com.google.gson.annotations.Expose import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.math.absoluteValue +import kotlin.time.Duration.Companion.milliseconds @SkyHanniModule object ExperimentsProfitTracker { @@ -98,29 +100,12 @@ object ExperimentsProfitTracker { if (!isEnabled()) return val message = event.message.removeColor() - if (claimMessagePattern.matches(message) && ExperimentMessages.DONE.isSelected()) + if (claimMessagePattern.matches(message) && ExperimentMessages.DONE.isSelected()) { event.blockedReason = "CLAIM_MESSAGE" + } experimentsDropPattern.matchMatcher(message) { - val reward = group("reward") - - event.blockedReason = when { - enchantingExpPattern.matches(reward) && ExperimentMessages.EXPERIENCE.isSelected() -> "EXPERIENCE_DROP" - experienceBottlePattern.matches(reward) && ExperimentMessages.BOTTLES.isSelected() -> "BOTTLE_DROP" - listOf("Metaphysical Serum", "Experiment The Fish").contains(reward) && ExperimentMessages.MISC.isSelected() -> "MISC_DROP" - ExperimentMessages.ENCHANTMENTS.isSelected() -> "ENCHANT_DROP" - else -> "" - } - - enchantingExpPattern.matchMatcher(reward) { - tracker.modify { - it.xpGained += group("amount").substringBefore(",").toInt() * 1000 - } - return - } - - val internalName = NEUInternalName.fromItemNameOrNull(reward) ?: return - if (!experienceBottlePattern.matches(group("reward"))) tracker.addItem(internalName, 1, false) + event.handleDrop(group("reward")) return } @@ -132,6 +117,27 @@ object ExperimentsProfitTracker { } } + private fun LorenzChatEvent.handleDrop(reward: String) { + blockedReason = when { + enchantingExpPattern.matches(reward) && ExperimentMessages.EXPERIENCE.isSelected() -> "EXPERIENCE_DROP" + experienceBottlePattern.matches(reward) && ExperimentMessages.BOTTLES.isSelected() -> "BOTTLE_DROP" + listOf("Metaphysical Serum", "Experiment The Fish").contains(reward) && ExperimentMessages.MISC.isSelected() -> "MISC_DROP" + ExperimentMessages.ENCHANTMENTS.isSelected() -> "ENCHANT_DROP" + else -> "" + } + + enchantingExpPattern.matchMatcher(reward) { + tracker.modify { + it.xpGained += group("amount").substringBefore(",").toInt() * 1000 + } + return + } + + val internalName = NEUInternalName.fromItemNameOrNull(reward) ?: return + if (!experienceBottlePattern.matches(reward)) tracker.addItem(internalName, 1, false) + else DelayedRun.runDelayed(100.milliseconds) { handleExpBottles(true) } + } + @SubscribeEvent fun onItemClick(event: ItemClickEvent) { if (event.clickType == ClickType.RIGHT_CLICK) { @@ -177,9 +183,6 @@ object ExperimentsProfitTracker { it.experimentsDone++ } } - if (ExperimentationTableAPI.inTable && InventoryUtils.openInventoryName() == "Superpairs Rewards") { - handleExpBottles(true) - } } private fun drawDisplay(data: Data): List = buildList { @@ -230,6 +233,7 @@ object ExperimentsProfitTracker { if (internalName.asString() !in listOf("EXP_BOTTLE", "GRAND_EXP_BOTTLE", "TITANIC_EXP_BOTTLE")) continue currentBottlesInInventory.addOrPut(internalName, item.stackSize) } + for ((internalName, amount) in currentBottlesInInventory) { val lastInInv = lastBottlesInInventory.getOrDefault(internalName, 0) if (lastInInv >= amount) { @@ -237,6 +241,7 @@ object ExperimentsProfitTracker { lastBottlesInInventory[internalName] = amount continue } + if (lastInInv == 0) { currentBottlesInInventory[internalName] = 0 lastBottlesInInventory[internalName] = amount @@ -253,6 +258,5 @@ object ExperimentsProfitTracker { private fun ExperimentMessages.isSelected() = config.hideMessages.contains(this) private fun isEnabled() = - LorenzUtils.inSkyBlock && config.enabled - && ExperimentationTableAPI.inDistanceToTable(LorenzVec.getBlockBelowPlayer(), 5.0) + LorenzUtils.inSkyBlock && config.enabled && ExperimentationTableAPI.inDistanceToTable(LorenzVec.getBlockBelowPlayer(), 5.0) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt index 21bc6052e099..86d7fca50ad9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/SuperpairExperimentInformationDisplay.kt @@ -17,6 +17,7 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration.Companion.milliseconds @SkyHanniModule +// TODO important: all use cases of listOf in combination with string needs to be gone. no caching, constant new list creation, and bad design. object SuperpairExperimentInformationDisplay { private val config get() = SkyHanniMod.feature.inventory.experimentationTable @@ -30,6 +31,7 @@ object SuperpairExperimentInformationDisplay { data class Item(val index: Int, val name: String) data class ItemPair(val first: Item, val second: Item) + // TODO remove string. use enum instead! maybe even create new data type instaed of map of pairs private var found = mutableMapOf, String>() private var toCheck = mutableListOf>() @@ -60,7 +62,7 @@ object SuperpairExperimentInformationDisplay { @SubscribeEvent fun onChestGuiOverlayRendered(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { if (!isEnabled()) return - config.superpairDisplayPosition.renderStrings(display, posLabel = "Sperpair Experiment Information") + config.superpairDisplayPosition.renderStrings(display, posLabel = "Superpair Experiment Information") display = checkItems(toCheck) } @@ -133,6 +135,8 @@ object SuperpairExperimentInformationDisplay { ?: return else lastClicked.find { it.second == uncovered } ?: return val lastItem = InventoryUtils.getItemAtSlotIndex(lastSlotClicked.first) ?: return + val itemClicked = InventoryUtils.getItemAtSlotIndex(slot) ?: return + val lastItemName = convertToReward(lastItem) if (isWaiting(lastItemName)) return @@ -145,7 +149,7 @@ object SuperpairExperimentInformationDisplay { uncoveredAt += 1 } - hasFoundPair(slot, lastSlotClicked.first, reward, lastItemName) -> handleFoundPair( + hasFoundPair(slot, lastSlotClicked.first, reward, lastItemName) && lastItem.itemDamage == itemClicked.itemDamage -> handleFoundPair( slot, reward, lastSlotClicked.first, @@ -166,7 +170,7 @@ object SuperpairExperimentInformationDisplay { found[pair] = "Pair" found.entries.removeIf { - it.value == "Match" && right(it.key).first.name == reward + it.value == "Match" && right(it.key).first.index == slot } found.entries.removeIf { it.value == "Normal" && (left(it.key).index == slot || left(it.key).index == lastSlotClicked) @@ -177,10 +181,10 @@ object SuperpairExperimentInformationDisplay { val match = uncoveredItems.find { it.second == reward }?.first ?: return val pair = toEither(ItemPair(Item(slot, reward), Item(match, reward))) - found[pair] = "Match" - found.entries.removeIf { - it.value == "Normal" && (left(it.key).index == slot || left(it.key).index == match) - } + if (found.none { + listOf("Pair", "Match").contains(it.value) && (right(it.key).first.index == slot) + }) found[pair] = "Match" + found.entries.removeIf { it.value == "Normal" && (left(it.key).index == slot || left(it.key).index == match) } } private fun handleNormalReward(slot: Int, reward: String) { @@ -262,6 +266,7 @@ object SuperpairExperimentInformationDisplay { private fun isOutOfBounds(slot: Int, experiment: Experiment): Boolean = slot <= experiment.startSlot || slot >= experiment.endSlot || (if (experiment.sideSpace == 1) slot in sideSpaces1 else slot in sideSpaces2) + // TODO remove left and right, use custom data type instead private fun left(it: Pair): Item = it.first ?: Item(-1, "") private fun right(it: Pair): ItemPair = it.second ?: ItemPair(Item(-1, ""), Item(-1, "")) From d3c4106dbd1d22b952992eb0c5705ff036a56c96 Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Sun, 22 Sep 2024 00:30:36 +0200 Subject: [PATCH 064/102] Backend: Renderable Wrapped String Internal Alignement (#2372) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/utils/renderables/Renderable.kt | 33 ++++++++++++------- .../utils/renderables/RenderableUtils.kt | 6 ++++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt index 9a9cc2adc9d2..d161cf2b0eeb 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt @@ -438,23 +438,27 @@ interface Renderable { color: Color = Color.WHITE, horizontalAlign: HorizontalAlignment = HorizontalAlignment.LEFT, verticalAlign: VerticalAlignment = VerticalAlignment.CENTER, + internalAlign: HorizontalAlignment = HorizontalAlignment.LEFT, ) = object : Renderable { - val list by lazy { - Minecraft.getMinecraft().fontRendererObj.listFormattedStringToWidth( + val fontRenderer by lazy { Minecraft.getMinecraft().fontRendererObj } + + val map by lazy { + fontRenderer.listFormattedStringToWidth( text, (width / scale).toInt(), - ) + ).associateWith { fontRenderer.getStringWidth(it) } } - override val width by lazy { - if (list.size == 1) { - (Minecraft.getMinecraft().fontRendererObj.getStringWidth(text) * scale).toInt() + 1 - } else { - width - } + override val width by lazy { (rawWidth * scale).toInt() + 1 } + + val rawWidth by lazy { + if (map.size == 1) + map.entries.first().value + else + map.maxOf { it.value } } - override val height by lazy { list.size * ((9 * scale).toInt() + 1) } + override val height by lazy { map.size * ((9 * scale).toInt() + 1) } override val horizontalAlign = horizontalAlign override val verticalAlign = verticalAlign @@ -464,8 +468,13 @@ interface Renderable { val fontRenderer = Minecraft.getMinecraft().fontRendererObj GlStateManager.translate(1.0, 1.0, 0.0) GlStateManager.scale(scale, scale, 1.0) - list.forEachIndexed { index, text -> - fontRenderer.drawStringWithShadow(text, 0f, index * 10.0f, color.rgb) + map.entries.forEachIndexed { index, (text, size) -> + fontRenderer.drawStringWithShadow( + text, + RenderableUtils.calculateAlignmentXOffset(size, rawWidth, internalAlign).toFloat(), + index * 10.0f, + color.rgb, + ) } GlStateManager.scale(inverseScale, inverseScale, 1.0) GlStateManager.translate(-1.0, -1.0, 0.0) diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt index 9200b467a20e..64c1d8f86b6a 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableUtils.kt @@ -39,6 +39,12 @@ internal object RenderableUtils { } ?: listOf(yPadding)) } + fun calculateAlignmentXOffset(width: Int, xSpace: Int, alignment: HorizontalAlignment) = when (alignment) { + HorizontalAlignment.CENTER -> (xSpace - width) / 2 + HorizontalAlignment.RIGHT -> xSpace - width + else -> 0 + } + private fun calculateAlignmentXOffset(renderable: Renderable, xSpace: Int) = when (renderable.horizontalAlign) { HorizontalAlignment.LEFT -> 0 HorizontalAlignment.CENTER -> (xSpace - renderable.width) / 2 From 857edca75b49a36d73628bbe9b11850ac6233f94 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Sun, 22 Sep 2024 00:49:53 +0200 Subject: [PATCH 065/102] Removed: Forge GFS (#2564) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../config/features/mining/MiningConfig.java | 6 -- .../skyhanni/features/mining/ForgeGfs.kt | 94 ------------------- 2 files changed, 100 deletions(-) delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/ForgeGfs.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java index 5f242ecd5f47..08c8ec894a71 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java @@ -96,10 +96,4 @@ public class MiningConfig { @ConfigEditorBoolean @FeatureToggle public boolean highlightYourGoldenGoblin = true; - - @Expose - @ConfigOption(name = "Forge GfS", desc = "Get Forge ingredients of a recipe.") - @ConfigEditorBoolean - @FeatureToggle - public boolean forgeGfs = false; } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/ForgeGfs.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/ForgeGfs.kt deleted file mode 100644 index 995198674334..000000000000 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/ForgeGfs.kt +++ /dev/null @@ -1,94 +0,0 @@ -package at.hannibal2.skyhanni.features.mining - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.api.GetFromSackAPI -import at.hannibal2.skyhanni.data.SackAPI -import at.hannibal2.skyhanni.events.GuiContainerEvent -import at.hannibal2.skyhanni.events.InventoryCloseEvent -import at.hannibal2.skyhanni.events.InventoryUpdatedEvent -import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent -import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.CollectionUtils -import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut -import at.hannibal2.skyhanni.utils.ItemUtils -import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull -import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.NEUInternalName -import at.hannibal2.skyhanni.utils.PrimitiveItemStack.Companion.makePrimitiveStack -import at.hannibal2.skyhanni.utils.RegexUtils.matches -import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern -import net.minecraft.client.player.inventory.ContainerLocalMenu -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent - -@SkyHanniModule -object ForgeGfs { - - private val patternGroup = RepoPattern.group("mining.forge") - - private val confirmScreenPattern by patternGroup.pattern( - "recipe.confirm", - "Confirm Process", - ) - - private val config get() = SkyHanniMod.feature.mining - - private val gfsFakeItem by lazy { - ItemUtils.createSkull( - displayName = "§aGet items from sacks", - uuid = "75ea8094-5152-4457-8c23-1ad9b3c176c0", - value = "ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsC" + - "iAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogI" + - "CJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3" + - "RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=", - "§8(from SkyHanni)", - "§7Click here to try to get all of this", - "§7recipe's ingredients from sacks.", - ) - } - - private var showFakeItem = false - - @SubscribeEvent - fun onInventoryOpen(event: InventoryUpdatedEvent) { - if (!isEnabled()) return - if (!confirmScreenPattern.matches(event.inventoryName)) return - - showFakeItem = true - } - - @SubscribeEvent - fun onInventoryClose(event: InventoryCloseEvent) { - showFakeItem = false - } - - @SubscribeEvent - fun replaceItem(event: ReplaceItemEvent) { - if (event.inventory is ContainerLocalMenu && showFakeItem && event.slot == 53) { - event.replace(gfsFakeItem) - } - } - - @SubscribeEvent - fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { - if (!isEnabled()) return - if (!showFakeItem || event.slotId != 53) return - - event.cancel() - - val itemMap: MutableMap = LinkedHashMap() - // Search for the first 4 columns only - // Normally would be 3, but the gemstone mixture is the only one that overflows to 4 - - for (i in CollectionUtils.takeColumn(0, 53, 0, 4)) { - val currentItem = event.container.getSlot(i).stack - val currItemInternalName = currentItem.getInternalNameOrNull() ?: continue - if (SackAPI.sackListInternalNames.contains(currItemInternalName.asString())) { - itemMap.addOrPut(currItemInternalName, currentItem.stackSize) - } - } - - GetFromSackAPI.getFromSack(itemMap.map { it.key.makePrimitiveStack(it.value) }) - } - - fun isEnabled() = LorenzUtils.inSkyBlock && config.forgeGfs -} From ad247a94a9d7186b670c834ebf88c5426c7b1c88 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Sun, 22 Sep 2024 01:03:58 +0200 Subject: [PATCH 066/102] Version 0.27 Beta 11 --- docs/CHANGELOG.md | 19 +++++++++++++++++++ docs/FEATURES.md | 6 ++++++ root.gradle.kts | 2 +- .../java/at/hannibal2/skyhanni/SkyHanniMod.kt | 2 +- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d351445de428..5f65761d0f01 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,6 +16,7 @@ + Added a "Get from Sack" button in the forge recipe menu to retrieve ingredients. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2106) + Added Tracker for Glacite Corpses. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2306) + Tracks overall loot and loot per type. ++ Hides tooltips of items inside the Fossil Excavator in Glacite Tunnels. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/2539) #### Rift Features @@ -122,6 +123,10 @@ + Added Broodmother spawn alert, countdown and stage change messages. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2325) + Countdown will not show until a spawning stage change is observed, and may be off by a few seconds. + Added Broodmother support to the Damage Indicator. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2325) ++ Added Carry Tracker. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2185) + + Use `/shcarry` to add carries to a customer, and set a price for a slayer boss. + + Automatically counts slayer bosses you carry. + + Automatically tracks coins received from customers via `/trade`. #### Fishing Features @@ -145,6 +150,7 @@ + Added option to hide pet nametag text. - Empa + j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/1880) + Added Transfer Cooldown Prevention. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/1751) + Wait for the transfer cooldown to complete before sending commands like warp/is/hub. ++ Added highlighting for the active Beacon Effect. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/2546) ### Improvements @@ -205,6 +211,7 @@ + Chocolate Factory: Shows "time to next milestone" instead of "max milestone" when you are at Prestige 6. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2503) + Added the required chocolate amount to the tooltip of milestone rabbits in Hoppity's Collection. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2523) + Improved the responsiveness of Hoppity Chat Compacting. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2488) ++ Added the ability to see how many duplicates you have previously found in Hoppity messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2556) #### Combat Improvements @@ -246,6 +253,7 @@ + Search works in all SkyHanni Trackers and the Sack Display. + In your inventory, hover over the GUI with your mouse, then start typing to search. + Improved the performance of pathfinding logic in Area Navigation. - nea (https://github.com/hannibal002/SkyHanni/pull/2537) ++ Added a toggle to force the `en_US` locale for number formatting. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2563) ### Fixes @@ -276,6 +284,7 @@ + Fixed "click to show tunnel map" from the Glacite Mines commission menu appearing in the inventory. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2485) + Fixed Mouse Keys not working as Wardrobe Slot Hotkeys. - j10a1n15, ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2506) + Fixed Estimated Item Value incorrectly showing all Kuudra armor as starred. - Luna (https://github.com/hannibal002/SkyHanni/pull/2550) ++ Fixed Experiments Profit Tracker & Superpair Data. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2560) #### Mining Fixes @@ -295,6 +304,8 @@ + Fixed Sky Mall perks not being read correctly after the Hypixel update. - Luna (https://github.com/hannibal002/SkyHanni/pull/2551) + Fixed Mining Commission Blocks Color not working properly with Connected Textures. - Empa (https://github.com/hannibal002/SkyHanni/pull/2553) + Fixed Gemstone Gauntlet item category. - Luna (https://github.com/hannibal002/SkyHanni/pull/2534) ++ Fixed Glacite powder being tracked as Glacite in Corpse Tracker. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2557) ++ Fixed "Mining Commissions Block Color" disabling OptiFine connected textures. - nopo (https://github.com/hannibal002/SkyHanni/pull/2559) #### Scoreboard Fixes @@ -380,6 +391,8 @@ + Fixed lag spikes in Path Finder. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2525) + Fixed old mayor perks not being marked as inactive when the mayor changes. - Luna (https://github.com/hannibal002/SkyHanni/pull/2524) + Fixed direct GitHub downloads triggering the source checker. - ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2548) ++ Fixed reforge display not working with new mining stats. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2555) ++ Fixed Totem of Corruption expiry warning not working in some cases. - Luna (https://github.com/hannibal002/SkyHanni/pull/2554) ### Technical Details @@ -430,6 +443,10 @@ + Added options to the graph node list to hide nodes with certain tags. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2537) + Removed the use of `LorenzChatEvent()` for 'manually' adding data to Hoppity Chat Compact. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2488) + Now uses `EggFoundEvent()`. ++ The Renderable Wrapped String now has an option to align text inside the wrapped string box. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2372) ++ Improved handling of unknown stats for ReforgeAPI. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2562) ++ Added `AreaChangeEvent`. - Empa (https://github.com/hannibal002/SkyHanni/pull/2535) ++ Updated `HandleEvent` to allow `onlyOnIsland` usage for a single island without brackets. - Empa (https://github.com/hannibal002/SkyHanni/pull/2535) ### Removed Features @@ -437,6 +454,8 @@ + Artist's Abode now sends a lobby-wide message for dye drops, making the notification obsolete. + Removed `Only Requirement Not Met` and `Highlight Requirement Rabbits` config options. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438) + Replaced then with a draggable list of different Hoppity collection highlight options. ++ Removed "Forge GfS". - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2564) + + Hypixel now pulls directly from sacks when using the forge. ## Version 0.26 diff --git a/docs/FEATURES.md b/docs/FEATURES.md index e3c68e43d602..22223bf303e4 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -864,6 +864,7 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game. + Option to show the walls also when inside the Nucleus. + Fossil Excavator Solver. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/1427) + Shows where to next click for optimal chance of solving the fossil. If there is a fossil this will find it within 18 moves. ++ Hides tooltips of items inside the Fossil Excavator in Glacite Tunnels. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/2539) + Excavation Profit Tracker. - hannibal2 + Empa (https://github.com/hannibal002/SkyHanni/pull/1432) + Count all drops you gain while excavating in the Fossil Research Center. + Track Glacite Powder gained as well (no profit, but progress). @@ -1337,6 +1338,7 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game. + In-Water Display. - Stella (https://github.com/hannibal002/SkyHanni/pull/1892) + Useful when using a Prismarine Blade in Stranded Mode. + Added Beacon Power Display. - Empa (https://github.com/hannibal002/SkyHanni/pull/1901) ++ Added highlighting for the active Beacon Effect. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/2546) + Added Hide Useless Armor Stands. - Empa (https://github.com/hannibal002/SkyHanni/pull/1962) + Hides armor stands that briefly appear on Hypixel. + **Custom Wardrobe**, a new look for the wardrobe. - j10an15, Empa (https://github.com/hannibal002/SkyHanni/pull/2039) @@ -1379,6 +1381,10 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game. + Added option to hide pet nametag text. - Empa + j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/1880) + Added Transfer Cooldown Prevention. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/1751) + Wait for the transfer cooldown to complete before sending commands like warp/is/hub. ++ Added Carry Tracker. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2185) + + Use `/shcarry` to add carries to a customer, and set a price for a slayer boss. + + Automatically counts slayer bosses you carry. + + Automatically tracks coins received from customers via `/trade`.
diff --git a/root.gradle.kts b/root.gradle.kts index 4c8333a202c6..84a371098d90 100644 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -12,7 +12,7 @@ plugins { allprojects { group = "at.hannibal2.skyhanni" - version = "0.27.Beta.10" + version = "0.27.Beta.11" } preprocess { diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 41552ba66982..35f083580184 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -42,7 +42,7 @@ import org.apache.logging.log4j.Logger clientSideOnly = true, useMetadata = true, guiFactory = "at.hannibal2.skyhanni.config.ConfigGuiForgeInterop", - version = "0.27.Beta.10", + version = "0.27.Beta.11", ) class SkyHanniMod { From e1b9ba327a4569e31728cbcbee217cbf72ec2e8d Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:40:07 +0200 Subject: [PATCH 067/102] Fix: Stonks of stonks auction on auction (#2572) --- .../skyhanni/features/inventory/StockOfStonkFeature.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/StockOfStonkFeature.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/StockOfStonkFeature.kt index 4bc8af1e3e56..f905eb5a532c 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/StockOfStonkFeature.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/StockOfStonkFeature.kt @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.InventoryOpenEvent import at.hannibal2.skyhanni.events.LorenzToolTipEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.transformAt +import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher @@ -20,7 +21,6 @@ object StockOfStonkFeature { private val repoGroup = RepoPattern.group("inventory.stockofstonks") - /** * REGEX-TEST: Stonks Auction */ @@ -42,7 +42,7 @@ object StockOfStonkFeature { */ private val topPattern by repoGroup.pattern( "top", - "§5§o§7§7▶ §c§lTOP (?[\\d,]+)§7 - §5Stock of Stonks §8x(?\\d+)", + "§5§o§7§.▶ §c§lTOP (?[\\d,]+)§7 - §5Stock of Stonks §8x(?\\d+)", ) /** @@ -85,7 +85,7 @@ object StockOfStonkFeature { } bidPattern.matchMatcher(line) { val cost = group("amount").replace(",", "").toLong() - val ratio = cost / stonksReward + val ratio = cost / stonksReward.transformIf({ this == 0 }, { 1 }) event.toolTip[index - 1] = line + " §7(§6§6${ratio.addSeparators()} §7per)" // double §6 for the replacement at the end if (ratio < bestRatio) { bestValueIndex = index - 1 From e60a7adf98a37ba13ce1caf372f3a6f9895c5da6 Mon Sep 17 00:00:00 2001 From: martimavocado <39881008+martimavocado@users.noreply.github.com> Date: Mon, 23 Sep 2024 05:15:58 +0100 Subject: [PATCH 068/102] Fix: Great Glacite Lake detection (#2565) --- src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt | 2 +- .../skyhanni/features/misc/discordrpc/DiscordLocationKey.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt index 35855fc612d5..91b7e4b79350 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt @@ -41,7 +41,7 @@ import kotlin.time.Duration.Companion.seconds object MiningAPI { private val group = RepoPattern.group("data.miningapi") - private val glaciteAreaPattern by group.pattern("area.glacite", "Glacite Tunnels|Glacite Lake") + private val glaciteAreaPattern by group.pattern("area.glacite", "Glacite Tunnels|Great Glacite Lake") private val dwarvenBaseCampPattern by group.pattern("area.basecamp", "Dwarven Base Camp") // TODO rename to include suffix "pattern", add regex test diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordLocationKey.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordLocationKey.kt index 96759bc7d2e5..895a04688f80 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordLocationKey.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordLocationKey.kt @@ -159,7 +159,7 @@ object DiscordLocationKey { "Dwarven Base Camp" to "glacite-tunnels", "Fossil Research Center" to "glacite-tunnels", - "Glacite Lake" to "glacite-tunnels", + "Great Glacite Lake" to "glacite-tunnels", "Glacite Mineshafts" to "glacite-tunnels", ) // maps sublocations to their broader image From 4b34113c86f824000bbe9eebb5cc3fcdfe56b9ba Mon Sep 17 00:00:00 2001 From: David Cole <40234707+DavidArthurCole@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:17:59 -0400 Subject: [PATCH 069/102] Backend: Add Helper Text to `/shtestmessage` (#2568) --- .../hannibal2/skyhanni/test/command/TestChatCommand.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt index e9d8d61453cd..fbb28b2724b1 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt @@ -12,7 +12,14 @@ object TestChatCommand { fun command(args: Array) { if (args.isEmpty()) { - ChatUtils.userError("Specify a chat message to test!") + val syntaxStrings = listOf( + "§7Syntax: §e/shtestmessage §7<§e-chat message§7> §7[-lines] [-complex] [-clipboard] [-s]", + " §7[-lines]§e: §7Split the message into multiple by newlines", + " §7[-complex]§e: §7Parse the message as a JSON chat component", + " §7[-clipboard]§e: §7Read the message from the clipboard", + " §7[-s]§e: §7Hide the output message", + ) + ChatUtils.userError("Specify a chat message to test!\n${syntaxStrings.joinToString("\n")}") return } From 8f7b79a0b4953bb64f342ccc21d962c8aacbc169 Mon Sep 17 00:00:00 2001 From: ThatGravyBoat Date: Mon, 23 Sep 2024 02:51:37 -0230 Subject: [PATCH 070/102] Backend: Add blossom and make source checker show version again (#2558) --- build.gradle.kts | 7 +++++++ root.gradle.kts | 1 + settings.gradle.kts | 1 + src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt | 2 +- .../hannibal2/skyhanni/tweaker/DownloadSourceChecker.java | 5 ++--- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e8464ada27e8..447387f86a72 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,7 @@ plugins { kotlin("plugin.power-assert") `maven-publish` id("moe.nea.shot") version "1.0.0" + id("net.kyori.blossom") } val target = ProjectTarget.values().find { it.projectPath == project.path }!! @@ -312,12 +313,18 @@ if (!MultiVersionStage.activeState.shouldCompile(target)) { onlyIf { false } } } + preprocess { vars.put("MC", target.minecraftVersion.versionNumber) vars.put("FORGE", if (target.forgeDep != null) 1 else 0) vars.put("JAVA", target.minecraftVersion.javaVersion) patternAnnotation.set("at.hannibal2.skyhanni.utils.compat.Pattern") } + +blossom { + replaceToken("@MOD_VERSION@", version) +} + val sourcesJar by tasks.creating(Jar::class) { destinationDirectory.set(layout.buildDirectory.dir("badjars")) archiveClassifier.set("src") diff --git a/root.gradle.kts b/root.gradle.kts index 84a371098d90..627d155ee866 100644 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -3,6 +3,7 @@ import com.replaymod.gradle.preprocess.Node plugins { id("dev.deftu.gradle.preprocess") version "0.6.1" + id("net.kyori.blossom") version "1.3.2" apply false id("gg.essential.loom") version "1.6.+" apply false kotlin("jvm") version "2.0.0" apply false kotlin("plugin.power-assert") version "2.0.0" apply false diff --git a/settings.gradle.kts b/settings.gradle.kts index aaf0f02cf6d6..adee2acba161 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ pluginManagement { maven("https://repo.nea.moe/releases") maven("https://repo.sk1er.club/repository/maven-releases/") maven("https://maven.deftu.dev/releases") + maven("https://maven.teamresourceful.com/repository/maven-private/") // Blossom maven("https://jitpack.io") { content { includeGroupByRegex("(com|io)\\.github\\..*") diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 35f083580184..45d02f5445f6 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -42,7 +42,7 @@ import org.apache.logging.log4j.Logger clientSideOnly = true, useMetadata = true, guiFactory = "at.hannibal2.skyhanni.config.ConfigGuiForgeInterop", - version = "0.27.Beta.11", + version = "@MOD_VERSION@", ) class SkyHanniMod { diff --git a/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java b/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java index 5672ee87cd7e..63a92849bc62 100644 --- a/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java +++ b/src/main/java/at/hannibal2/skyhanni/tweaker/DownloadSourceChecker.java @@ -14,6 +14,7 @@ public class DownloadSourceChecker { + private static final String MOD_VERSION = "@MOD_VERSION@"; private static final String GITHUB_REPO = "511310721"; private static final String GITHUB_REPO_TEXT = "repo_id=" + GITHUB_REPO; private static final String MODRINTH_URL = "/data/byNkmv5G/"; @@ -95,12 +96,10 @@ private static void openMenu(URI host) { } )); - // TODO FIX THIS CAUSING CLASS LOADING AND CRASHING - //String version = SkyHanniMod.Companion.getVersion(); JOptionPane.showOptionDialog( frame, String.format(String.join("\n", SECURITY_POPUP), uriToSimpleString(host)), - "SkyHanni Security Error", + "SkyHanni " + MOD_VERSION + " Security Error", JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, From b0735ffc3ed655838de6d0d0eb6376cde92483d1 Mon Sep 17 00:00:00 2001 From: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:48:19 +1000 Subject: [PATCH 071/102] Backend: Create and use primitive recipe and ingredient (#2460) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../features/bingo/FirstMinionTier.kt | 13 +++-- .../features/bingo/MinionCraftHelper.kt | 18 +++---- .../garden/visitor/GardenVisitorFeatures.kt | 8 +-- .../garden/visitor/GardenVisitorSupercraft.kt | 10 ++-- .../inventory/craft/CraftableItemList.kt | 6 +-- .../items/EstimatedItemValueCalculator.kt | 9 ++-- .../skyhanni/test/SkyHanniDebugsAndTests.kt | 2 +- .../skyhanni/utils/ItemPriceUtils.kt | 7 ++- .../at/hannibal2/skyhanni/utils/ItemUtils.kt | 10 ++-- .../at/hannibal2/skyhanni/utils/NEUItems.kt | 25 ++++----- .../skyhanni/utils/PrimitiveIngredient.kt | 26 +++++++++ .../skyhanni/utils/PrimitiveRecipe.kt | 53 +++++++++++++++++++ 12 files changed, 126 insertions(+), 61 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/PrimitiveRecipe.kt diff --git a/src/main/java/at/hannibal2/skyhanni/features/bingo/FirstMinionTier.kt b/src/main/java/at/hannibal2/skyhanni/features/bingo/FirstMinionTier.kt index e4bae4f05f8d..e94fb8d840d0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bingo/FirstMinionTier.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bingo/FirstMinionTier.kt @@ -1,13 +1,12 @@ package at.hannibal2.skyhanni.features.bingo import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.NEUInternalName -import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.NEUItems.getCachedIngredients +import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull +import at.hannibal2.skyhanni.utils.PrimitiveRecipe import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe object FirstMinionTier { @@ -30,20 +29,20 @@ object FirstMinionTier { ) { for (minionId in tierOneMinionsFiltered) { for (recipe in NEUItems.getRecipes(minionId)) { - if (recipe !is CraftingRecipe) continue + if (!recipe.isCraftingRecipe()) continue checkOne(recipe, help, minions, minionId) } } } private fun checkOne( - recipe: CraftingRecipe, + recipe: PrimitiveRecipe, help: Map, minions: MutableMap, minionId: NEUInternalName, ) { - if (recipe.getCachedIngredients().any { help.contains(it.internalItemId.asInternalName()) }) { - val name = recipe.output.itemStack.name.removeColor() + if (recipe.getCachedIngredients().any { help.contains(it.internalName) }) { + val name = recipe.output?.internalName?.getItemStackOrNull()?.displayName?.removeColor() ?: return val abc = name.replace(" I", " 0") minions[abc] = minionId.replace("_1", "_0") } diff --git a/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt index c12bafc39ea3..dbec4ea5788c 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt @@ -24,7 +24,6 @@ import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import com.google.gson.JsonArray import com.google.gson.JsonObject -import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe import net.minecraft.client.Minecraft import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -139,7 +138,7 @@ object MinionCraftHelper { for (recipe in recipes) { for (ingredient in recipe.getCachedIngredients()) { - val ingredientInternalName = ingredient.internalItemId.asInternalName() + val ingredientInternalName = ingredient.internalName if (ingredientInternalName == internalName) return true val ingredientPrimitive = NEUItems.getPrimitiveMultiplier(ingredientInternalName) @@ -169,10 +168,10 @@ object MinionCraftHelper { if (internalName.contains("_GENERATOR_")) { for (recipe in NEUItems.getRecipes(internalName)) { - if (recipe !is CraftingRecipe) continue + if (!recipe.isCraftingRecipe()) continue for (ingredient in recipe.getCachedIngredients()) { - val id = ingredient.internalItemId.asInternalName() + val id = ingredient.internalName if (!id.contains("_GENERATOR_") && !allIngredients.contains(id)) { allIngredients.add(id) } @@ -194,13 +193,12 @@ object MinionCraftHelper { newDisplay.add(minionName) val nextMinionId = minionId.addOneToId() for (recipe in NEUItems.getRecipes(nextMinionId)) { - if (recipe !is CraftingRecipe) continue - val output = recipe.output - val internalItemId = output.internalItemId.asInternalName() - if (!internalItemId.contains("_GENERATOR_")) continue + if (!recipe.isCraftingRecipe()) continue + val output = recipe.output ?: continue + if (!output.internalName.contains("_GENERATOR_")) continue val map = mutableMapOf() - for (input in recipe.inputs) { - val itemId = input.internalItemId.asInternalName() + for (input in recipe.ingredients) { + val itemId = input.internalName if (minionId != itemId) { val count = input.count.toInt() val old = map.getOrDefault(itemId, 0) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt index aed4f7e7cffc..2e8d789660ea 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt @@ -46,7 +46,6 @@ import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems -import at.hannibal2.skyhanni.utils.NEUItems.allIngredients import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.NEUItems.getPrice import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators @@ -250,13 +249,14 @@ object GardenVisitorFeatures { val ingredients = NEUItems.getRecipes(internalName) // TODO describe what this line does - .firstOrNull() { !it.allIngredients().first().internalItemId.contains("PEST") } - ?.allIngredients() ?: emptySet() + .firstOrNull { !it.ingredients.first().internalName.contains("PEST") } + ?.ingredients ?: emptySet() if (ingredients.isEmpty()) return + // TODO change key to NEUInternalName val requiredIngredients = mutableMapOf() for (ingredient in ingredients) { - val key = ingredient.internalItemId + val key = ingredient.internalName.asString() requiredIngredients[key] = requiredIngredients.getOrDefault(key, 0) + ingredient.count.toInt() } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt index f441d17ab6a1..26bfa7f26409 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt @@ -8,14 +8,11 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.HypixelCommands -import at.hannibal2.skyhanni.utils.ItemUtils.itemName import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems -import at.hannibal2.skyhanni.utils.NEUItems.allIngredients import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.SimpleTimeMark -import at.hannibal2.skyhanni.utils.StringUtils.removeColor import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.entity.player.InventoryPlayer import net.minecraftforge.fml.common.eventhandler.EventPriority @@ -74,11 +71,12 @@ object GardenVisitorSupercraft { private fun getSupercraftForSacks(internalName: NEUInternalName, amount: Int) { val ingredients = NEUItems.getRecipes(internalName) // TODO describe what this line does - .firstOrNull { !it.allIngredients().first().internalItemId.contains("PEST") } - ?.allIngredients() ?: return + .firstOrNull { !it.ingredients.first().internalName.contains("PEST") } + ?.ingredients ?: return + // TODO change key to NEUInternalName val ingredientReqs = mutableMapOf() for (ingredient in ingredients) { - val key = ingredient.internalItemId + val key = ingredient.internalName.asString() ingredientReqs[key] = ingredientReqs.getOrDefault(key, 0) + ingredient.count.toInt() } hasIngredients = true diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt index 6b841f5a40d0..8612c9795cfc 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/craft/CraftableItemList.kt @@ -20,12 +20,12 @@ import at.hannibal2.skyhanni.utils.NEUItems.isVanillaItem import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.PrimitiveItemStack.Companion.toPrimitiveStackOrNull +import at.hannibal2.skyhanni.utils.PrimitiveRecipe import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.StringUtils import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern -import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.math.floor @@ -71,7 +71,7 @@ object CraftableItemList { val recipes = NEUItems.getRecipes(internalName) for (recipe in recipes) { - if (recipe !is CraftingRecipe) continue + if (!recipe.isCraftingRecipe()) continue val renderable = createItemRenderable(recipe, availableMaterial, pricePer, internalName) ?: continue lines[internalName] = renderable } @@ -79,7 +79,7 @@ object CraftableItemList { } private fun createItemRenderable( - recipe: CraftingRecipe, + recipe: PrimitiveRecipe, availableMaterial: Map, pricePer: MutableMap, internalName: NEUInternalName, diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt index 953809b963d9..81e12f91349a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt @@ -20,13 +20,13 @@ import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzRarity import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName -import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull import at.hannibal2.skyhanni.utils.NEUItems.getRawCraftCostOrNull import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.PrimitiveIngredient import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getAbilityScrolls import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getArmorDye @@ -58,7 +58,6 @@ import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.hasJalapenoBook import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.hasWoodSingularity import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.isRecombobulated import at.hannibal2.skyhanni.utils.StringUtils.allLettersFirstUppercase -import io.github.moulberry.notenoughupdates.recipes.Ingredient import io.github.notenoughupdates.moulconfig.observer.Property import net.minecraft.item.ItemStack import java.util.Locale @@ -889,12 +888,12 @@ object EstimatedItemValueCalculator { val previousTotal = totalPrice for (ingredients in slot.value) { - val ingredient = Ingredient(NEUItems.manager, ingredients) + val ingredient = PrimitiveIngredient(ingredients) - totalPrice += if (ingredient.isCoins) { + totalPrice += if (ingredient.isCoin()) { ingredient.count } else { - ingredient.internalItemId.asInternalName().getPrice() * ingredient.count + ingredient.internalName.getPrice() * ingredient.count } } diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index 045ce5bce1c2..32b287cb96a2 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -449,7 +449,7 @@ object SkyHanniDebugsAndTests { } @SubscribeEvent - fun onSHowCraftPrice(event: LorenzToolTipEvent) { + fun onShowCraftPrice(event: LorenzToolTipEvent) { if (!LorenzUtils.inSkyBlock) return if (!debugConfig.showCraftPrice) return val price = event.itemStack.getInternalNameOrNull()?.getRawCraftCostOrNull() ?: return diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemPriceUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemPriceUtils.kt index b12f3640e07f..66775b2d63fb 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemPriceUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemPriceUtils.kt @@ -9,18 +9,17 @@ import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull import at.hannibal2.skyhanni.utils.NEUItems.getRecipes import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators -import io.github.moulberry.notenoughupdates.recipes.NeuRecipe object ItemPriceUtils { fun NEUInternalName.getPrice( priceSource: ItemPriceSource = ItemPriceSource.BAZAAR_INSTANT_BUY, - pastRecipes: List = emptyList(), + pastRecipes: List = emptyList(), ) = getPriceOrNull(priceSource, pastRecipes) ?: 0.0 fun NEUInternalName.getPriceOrNull( priceSource: ItemPriceSource = ItemPriceSource.BAZAAR_INSTANT_BUY, - pastRecipes: List = emptyList(), + pastRecipes: List = emptyList(), ): Double? { when (this) { NEUInternalName.JASPER_CRYSTAL -> return 0.0 @@ -60,7 +59,7 @@ object ItemPriceUtils { // NEUItems.manager.auctionManager.getCraftCost(asString())?.craftCost fun NEUInternalName.getRawCraftCostOrNull( priceSource: ItemPriceSource = ItemPriceSource.BAZAAR_INSTANT_BUY, - pastRecipes: List = emptyList(), + pastRecipes: List = emptyList(), ): Double? = getRecipes(this).filter { it !in pastRecipes } .map { it.getRecipePrice(priceSource, pastRecipes + it) } .filter { it >= 0 } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt index cc1a4362b070..ac35c07418ba 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt @@ -7,7 +7,6 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice -import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull import at.hannibal2.skyhanni.utils.NumberUtil.formatInt import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher @@ -20,7 +19,6 @@ import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.StringUtils.removeResets import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import com.google.common.collect.Lists -import io.github.moulberry.notenoughupdates.recipes.NeuRecipe import io.github.moulberry.notenoughupdates.util.NotificationHandler import net.minecraft.client.Minecraft import net.minecraft.init.Items @@ -463,19 +461,19 @@ object ItemUtils { return list } - fun neededItems(recipe: NeuRecipe): Map { + fun neededItems(recipe: PrimitiveRecipe): Map { val neededItems = mutableMapOf() for (ingredient in recipe.ingredients) { - val material = ingredient.internalItemId.asInternalName() + val material = ingredient.internalName val amount = ingredient.count.toInt() neededItems.addOrPut(material, amount) } return neededItems } - fun NeuRecipe.getRecipePrice( + fun PrimitiveRecipe.getRecipePrice( priceSource: ItemPriceSource = ItemPriceSource.BAZAAR_INSTANT_BUY, - pastRecipes: List = emptyList(), + pastRecipes: List = emptyList(), ): Double = neededItems(this).map { it.key.getPrice(priceSource, pastRecipes) * it.value }.sum() diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt index 81eb2968cce3..34d491ceea81 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt @@ -30,9 +30,6 @@ import io.github.moulberry.notenoughupdates.NotEnoughUpdates import io.github.moulberry.notenoughupdates.events.ProfileDataLoadedEvent import io.github.moulberry.notenoughupdates.overlays.AuctionSearchOverlay import io.github.moulberry.notenoughupdates.overlays.BazaarSearchOverlay -import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe -import io.github.moulberry.notenoughupdates.recipes.Ingredient -import io.github.moulberry.notenoughupdates.recipes.NeuRecipe import io.github.moulberry.notenoughupdates.util.ItemResolutionQuery import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.Minecraft @@ -57,8 +54,8 @@ object NEUItems { val manager: NEUManager get() = NotEnoughUpdates.INSTANCE.manager private val multiplierCache = mutableMapOf() - private val recipesCache = mutableMapOf>() - private val ingredientsCache = mutableMapOf>() + private val recipesCache = mutableMapOf>() + private val ingredientsCache = mutableMapOf>() private val itemIdCache = mutableMapOf>() private val hypixelApiGson by lazy { @@ -183,7 +180,7 @@ object NEUItems { @Deprecated("Moved to ItemPriceUtils", ReplaceWith("")) fun NEUInternalName.getPrice( priceSource: ItemPriceSource = ItemPriceSource.BAZAAR_INSTANT_BUY, - pastRecipes: List = emptyList(), + pastRecipes: List = emptyList(), ): Double = getPriceNew(priceSource, pastRecipes) @Deprecated("Moved to ItemPriceUtils", ReplaceWith("")) @@ -198,11 +195,11 @@ object NEUItems { @Deprecated("Moved to ItemPriceUtils", ReplaceWith("")) fun NEUInternalName.getPriceOrNull( priceSource: ItemPriceSource = ItemPriceSource.BAZAAR_INSTANT_BUY, - pastRecipes: List = emptyList(), + pastRecipes: List = emptyList(), ): Double? = this.getPriceOrNullNew(priceSource, pastRecipes) @Deprecated("Moved to ItemPriceUtils", ReplaceWith("")) - fun NEUInternalName.getRawCraftCostOrNull(pastRecipes: List = emptyList()): Double? = + fun NEUInternalName.getRawCraftCostOrNull(pastRecipes: List = emptyList()): Double? = getRawCraftCostOrNullNew(ItemPriceSource.BAZAAR_INSTANT_BUY, pastRecipes) fun NEUInternalName.getItemStackOrNull(): ItemStack? = ItemResolutionQuery(manager) @@ -309,12 +306,12 @@ object NEUItems { return internalName.makePrimitiveStack() } for (recipe in getRecipes(internalName)) { - if (recipe !is CraftingRecipe) continue + if (!recipe.isCraftingRecipe()) continue val map = mutableMapOf() for (ingredient in recipe.getCachedIngredients()) { val count = ingredient.count.toInt() - var internalItemId = ingredient.internalItemId.asInternalName() + var internalItemId = ingredient.internalName // ignore cactus green if (internalName == "ENCHANTED_CACTUS_GREEN".asInternalName() && internalItemId == "INK_SACK-2".asInternalName()) { internalItemId = "CACTUS".asInternalName() @@ -356,13 +353,13 @@ object NEUItems { return result } - fun getRecipes(internalName: NEUInternalName): Set { + fun getRecipes(internalName: NEUInternalName): Set { return recipesCache.getOrPut(internalName) { - manager.getRecipesFor(internalName.asString()) + PrimitiveRecipe.convertMultiple(manager.getRecipesFor(internalName.asString())).toSet() } } - fun NeuRecipe.getCachedIngredients() = ingredientsCache.getOrPut(this) { allIngredients() } + fun PrimitiveRecipe.getCachedIngredients() = ingredientsCache.getOrPut(this) { ingredients } fun neuHasFocus(): Boolean { if (AuctionSearchOverlay.shouldReplace()) return true @@ -391,6 +388,4 @@ object NEUItems { val jsonObject = ConfigManager.gson.fromJson(jsonString, JsonObject::class.java) return manager.jsonToStack(jsonObject, false) } - - fun NeuRecipe.allIngredients(): Set = ingredients } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt new file mode 100644 index 000000000000..e8d1edefcc44 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt @@ -0,0 +1,26 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.SKYBLOCK_COIN +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import io.github.moulberry.notenoughupdates.recipes.Ingredient + +class PrimitiveIngredient(val internalName: NEUInternalName, val count: Double = 1.0) { + + constructor(internalName: NEUInternalName, count: Int) : this(internalName, count.toDouble()) + + constructor(ingredientIdentifier: String) : this( + ingredientIdentifier.substringBefore(':').asInternalName(), + ingredientIdentifier.substringAfter(':').toDoubleOrNull() ?: 1.0, + ) + + companion object { + fun coinIngredient(count: Double = 1.0) = PrimitiveIngredient(SKYBLOCK_COIN, count) + + fun fromNeuIngredient(neuIngredient: Ingredient) = PrimitiveIngredient(neuIngredient.internalItemId.asInternalName(), neuIngredient.count) + } + + fun isCoin() = internalName == SKYBLOCK_COIN + + + override fun toString() = "$internalName x$count" +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveRecipe.kt b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveRecipe.kt new file mode 100644 index 000000000000..f064183ba372 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveRecipe.kt @@ -0,0 +1,53 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.utils.RecipeType.CRAFTING +import io.github.moulberry.notenoughupdates.recipes.EssenceUpgrades +import io.github.moulberry.notenoughupdates.recipes.ForgeRecipe +import io.github.moulberry.notenoughupdates.recipes.ItemShopRecipe +import io.github.moulberry.notenoughupdates.recipes.KatRecipe +import io.github.moulberry.notenoughupdates.recipes.MobLootRecipe +import io.github.moulberry.notenoughupdates.recipes.NeuRecipe +import io.github.moulberry.notenoughupdates.recipes.VillagerTradeRecipe + +data class PrimitiveRecipe( + val ingredients: Set, + val outputs: Set, + val recipeType: RecipeType, +) { + + val output by lazy { outputs.firstOrNull() } + + companion object { + fun fromNeuRecipe(neuRecipe: NeuRecipe): PrimitiveRecipe { + val ingredients = neuRecipe.ingredients.map { PrimitiveIngredient.fromNeuIngredient(it) }.toSet() + val outputs = neuRecipe.outputs.map { PrimitiveIngredient.fromNeuIngredient(it) }.toSet() + + val recipeType = when (neuRecipe::class.java) { + ForgeRecipe::class.java -> RecipeType.FORGE + VillagerTradeRecipe::class.java -> RecipeType.TRADE + EssenceUpgrades::class.java -> RecipeType.ESSENCE + MobLootRecipe::class.java -> RecipeType.MOB_DROP + ItemShopRecipe::class.java -> RecipeType.NPC_SHOP + KatRecipe::class.java -> RecipeType.KAT_UPGRADE + else -> CRAFTING + } + + return PrimitiveRecipe(ingredients, outputs, recipeType) + } + + fun convertMultiple(neuRecipes: Collection) = neuRecipes.map { fromNeuRecipe(it) } + } + + fun isCraftingRecipe() = this.recipeType == CRAFTING +} + +enum class RecipeType { + FORGE, + TRADE, + MOB_DROP, + NPC_SHOP, + KAT_UPGRADE, + ESSENCE, + CRAFTING, + ; +} From 143d1521bc3a5ff02e071e0c771aeb5f74e49db8 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:26:10 +0200 Subject: [PATCH 072/102] added todo --- .../hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt index 508296d4974c..a710e4fd0e94 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt @@ -275,6 +275,7 @@ object DungeonFinderFeatures { if (!partyFinderTitlePattern.matches(inventoryName)) return map inInventory = true for ((slot, stack) in event.inventoryItems) { + // TODO use enum val classNames = mutableListOf("Healer", "Mage", "Berserk", "Archer", "Tank") val toolTip = stack.getLore().toMutableList() for ((index, line) in stack.getLore().withIndex()) { From 91dee900cc7bc40db5e936d2e9f2f18dcbbe870f Mon Sep 17 00:00:00 2001 From: Thunderblade73 <85900443+Thunderblade73@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:26:32 +0200 Subject: [PATCH 073/102] Internal: Shared Live Templates for common structures (#2463) --- .gitignore | 1 + .idea/liveTemplates/SkyHanni.xml | 101 +++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .idea/liveTemplates/SkyHanni.xml diff --git a/.gitignore b/.gitignore index d77f4bd8b6f0..7f4081e02e52 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ !.idea/icon.svg !.idea/dictionaries/default_user.xml !.idea/scopes/Mixins.xml +!.idea/liveTemplates/SkyHanni.xml .vscode/ run/ build/ diff --git a/.idea/liveTemplates/SkyHanni.xml b/.idea/liveTemplates/SkyHanni.xml new file mode 100644 index 000000000000..08c6737b5ce2 --- /dev/null +++ b/.idea/liveTemplates/SkyHanni.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + \ No newline at end of file From 478e5c6ac5f587dbf5f6f45f6489cbe37b05fd2f Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:10:19 +0200 Subject: [PATCH 074/102] Improvement: Glacite Tunnels Area Navigation (#2544) Co-authored-by: nea Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/api/event/SkyHanniEvent.kt | 3 + .../hannibal2/skyhanni/data/IslandGraphs.kt | 77 +++++++++++++++++-- .../hannibal2/skyhanni/events/LorenzEvent.kt | 5 ++ .../skyhanni/features/mining/TunnelsMaps.kt | 10 ++- .../skyhanni/features/misc/IslandAreas.kt | 4 +- 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvent.kt b/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvent.kt index 4f3685c8bdff..c05b438cde20 100644 --- a/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/api/event/SkyHanniEvent.kt @@ -1,5 +1,8 @@ package at.hannibal2.skyhanni.api.event +/** + * Use @[HandleEvent] + */ abstract class SkyHanniEvent protected constructor() { var isCancelled: Boolean = false diff --git a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt index ca3143b952ab..9e4639454361 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/IslandGraphs.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.data import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.data.model.Graph import at.hannibal2.skyhanni.data.model.GraphNode import at.hannibal2.skyhanni.data.model.findShortestPathAsGraphWithDistance @@ -10,23 +11,29 @@ import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.events.skyblock.ScoreboardAreaChangeEvent import at.hannibal2.skyhanni.features.misc.IslandAreas import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.SkyHanniDebugsAndTests +import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.LocationUtils import at.hannibal2.skyhanni.utils.LocationUtils.canBeSeen import at.hannibal2.skyhanni.utils.LocationUtils.distanceSqToPlayer import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine import at.hannibal2.skyhanni.utils.RenderUtils.draw3DPathWithWaypoint import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import java.awt.Color import java.io.File import kotlin.math.abs +import kotlin.time.Duration.Companion.milliseconds /** * TODO @@ -101,26 +108,73 @@ object IslandGraphs { private var fastestPath: Graph? = null private var condition: () -> Boolean = { true } + private var inGlaciteTunnels: Boolean? = null + + private val patternGroup = RepoPattern.group("data.island.navigation") + + /** + * REGEX-TEST: Dwarven Base Camp + * REGEX-TEST: Forge + * REGEX-TEST: Fossil Research Center + */ + private val glaciteTunnelsPattern by patternGroup.pattern( + "glacitetunnels", + "(Glacite Tunnels|Dwarven Base Camp|Great Glacite Lake|Fossil Research Center)", + ) @SubscribeEvent fun onRepoReload(event: RepositoryReloadEvent) { if (!LorenzUtils.inSkyBlock) return - reloadFromJson(LorenzUtils.skyBlockIsland) + loadIsland(LorenzUtils.skyBlockIsland) } @SubscribeEvent fun onIslandChange(event: IslandChangeEvent) { - reloadFromJson(event.newIsland) + if (currentIslandGraph != null) return + if (event.newIsland == IslandType.NONE) return + loadIsland(event.newIsland) } @SubscribeEvent fun onWorldChange(event: LorenzWorldChangeEvent) { + currentIslandGraph = null reset() } - private fun reloadFromJson(newIsland: IslandType) { - val islandName = newIsland.name + fun isGlaciteTunnelsArea(area: String?): Boolean = glaciteTunnelsPattern.matches(area) + + @HandleEvent + fun onAreaChange(event: ScoreboardAreaChangeEvent) { + if (!IslandType.DWARVEN_MINES.isInIsland()) { + inGlaciteTunnels = null + return + } + + val now = isGlaciteTunnelsArea(LorenzUtils.skyBlockArea) + if (inGlaciteTunnels != now) { + inGlaciteTunnels = now + loadDwarvenMines() + } + } + + private fun loadDwarvenMines() { + if (isGlaciteTunnelsArea(LorenzUtils.skyBlockArea)) { + reloadFromJson("GLACITE_TUNNELS") + } else { + reloadFromJson("DWARVEN_MINES") + } + } + + private fun loadIsland(newIsland: IslandType) { + if (newIsland == IslandType.DWARVEN_MINES) { + loadDwarvenMines() + } else { + reloadFromJson(newIsland.name) + } + } + + private fun reloadFromJson(islandName: String) { val constant = "island_graphs/$islandName" val name = "constants/$constant.json" val jsonFile = File(SkyHanniMod.repo.repoLocation, name) @@ -136,6 +190,13 @@ object IslandGraphs { fun setNewGraph(graph: Graph) { reset() currentIslandGraph = graph + + // calling various update functions to make swtiching between deep caverns and glacite tunnels bareable + handleTick() + IslandAreas.noteMoved() + DelayedRun.runDelayed(150.milliseconds) { + IslandAreas.updatePosition() + } } private fun reset() { @@ -143,11 +204,16 @@ object IslandGraphs { currentTarget = null goal = null fastestPath = null + IslandAreas.display = null } @SubscribeEvent fun onTick(event: LorenzTickEvent) { if (!LorenzUtils.inSkyBlock) return + handleTick() + } + + private fun handleTick() { val prevClosed = closedNote val graph = currentIslandGraph ?: return @@ -294,8 +360,7 @@ object IslandGraphs { val nodes = graph.nodes val potentialSkip = - nodes.lastOrNull { it.position.canBeSeen(maxSkipDistance, -1.0) && abs(it.position.y - playerY) <= 2 } - ?: return null + nodes.lastOrNull { it.position.canBeSeen(maxSkipDistance, -1.0) && abs(it.position.y - playerY) <= 2 } ?: return null val angleSkip = if (potentialSkip == nodes.first()) { false diff --git a/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt index c895b1058d7c..4a294a6779ca 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt @@ -12,6 +12,11 @@ import at.hannibal2.skyhanni.utils.system.PlatformUtils import net.minecraftforge.common.MinecraftForge import net.minecraftforge.fml.common.eventhandler.Event import net.minecraftforge.fml.common.eventhandler.IEventListener +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +/** + * Use @[SubscribeEvent] + */ @Deprecated("Use SkyHanniEvent instead") abstract class LorenzEvent : Event() { diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt index 847a3673f5ad..5214ff162eb5 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/TunnelsMaps.kt @@ -81,6 +81,7 @@ object TunnelsMaps { private var active: String = "" private lateinit var fairySouls: Map + // TODO what is this? why is there a difference? can this be replaced with GraphNodeTag.GRIND_ORES? private lateinit var newGemstones: Map> private lateinit var oldGemstones: Map> private lateinit var normalLocations: Map> @@ -221,7 +222,7 @@ object TunnelsMaps { @SubscribeEvent fun onRepoReload(event: RepositoryReloadEvent) { - graph = event.getConstant("TunnelsGraph", gson = Graph.gson) + graph = event.getConstant("island_graphs/GLACITE_TUNNELS", gson = Graph.gson) possibleLocations = graph.groupBy { it.name }.filterNotNullKeys().mapValues { (_, value) -> value } @@ -235,7 +236,12 @@ object TunnelsMaps { key.contains("Fairy") -> fairy[key] = value.first() newGemstonePattern.matches(key) -> newGemstone[key] = value oldGemstonePattern.matches(key) -> oldGemstone[key] = value - else -> other[key] = value + else -> { + // ignore node names without color codes + if (key.removeColor() != key) { + other[key] = value + } + } } } fairySouls = fairy diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt index 311af056eab0..d5e57d513380 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt @@ -41,7 +41,7 @@ object IslandAreas { private var nodes = mapOf() private var paths = mapOf() - private var display: Renderable? = null + var display: Renderable? = null private var targetNode: GraphNode? = null private var currentAreaName = "" private val textInput = TextInput() @@ -103,7 +103,7 @@ object IslandAreas { } } - private fun updatePosition() { + fun updatePosition() { display = buildDisplay().buildSearchBox(textInput) } From 4fe133f51a84d49ebc3d7444e010c7de474fecef Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:10:48 +0200 Subject: [PATCH 075/102] removed unnecessary debug --- .../features/misc/items/EstimatedItemValueCalculator.kt | 5 ----- .../java/at/hannibal2/skyhanni/utils/EssenceItemUtils.kt | 6 ------ 2 files changed, 11 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt index 81e12f91349a..b0fbc2f31f83 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/items/EstimatedItemValueCalculator.kt @@ -498,11 +498,6 @@ object EstimatedItemValueCalculator { if (remainingStars <= 0) continue val price = getPriceFor(prices, remainingStars) ?: return null - println(" ") - println("price for $id ($remainingStars)") - println("price.itemPrice: ${price.itemPrice}") - println("essencePrice: ${price.essencePrice}") - println("itemPrice: ${price.itemPrice}") finalPrice = finalPrice?.let { it + price } ?: price remainingStars -= prices.size } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/EssenceItemUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/EssenceItemUtils.kt index c63fb7177a14..b4b9f30b1f2f 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/EssenceItemUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/EssenceItemUtils.kt @@ -1,6 +1,5 @@ package at.hannibal2.skyhanni.utils -import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.data.jsonobjects.repo.neu.NeuEssenceCostJson import at.hannibal2.skyhanni.events.NeuRepositoryReloadEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule @@ -16,11 +15,6 @@ object EssenceItemUtils { @SubscribeEvent fun onNeuRepoReload(event: NeuRepositoryReloadEvent) { val unformattedData = event.getConstant>("essencecosts", NeuEssenceCostJson.TYPE) - - ConfigManager.gson.toJson(unformattedData).let { - ChatUtils.debug("saved to clipboard!") - OSUtils.copyToClipboard(it) - } this.itemPrices = reformatData(unformattedData) } From c24b433a0cfeb662d5ccaf63ff7e4f6a06f9be4a Mon Sep 17 00:00:00 2001 From: NopoTheGamer <40329022+NopoTheGamer@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:30:53 +1000 Subject: [PATCH 076/102] Fix: Mining Commissions Blocks Color connected textures v2 (#2577) --- .../mixins/transformers/MixinOptifineConnectedTextures.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java index 9f3087e8e0ed..47ebe4ea63cf 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinOptifineConnectedTextures.java @@ -16,8 +16,8 @@ private static IBlockState modifyGetConnectedTextureMultiPass(IBlockState state) return OptifineConnectedTexturesHookKt.modifyConnectedTexturesBlockState(state); } - @ModifyArg(method = "getConnectedTexture", at = @At(value = "INVOKE", target = "skipConnectedTexture")) - private static IBlockState modifySkipConnectedTexture(IBlockState state) { + @ModifyArg(method = "isNeighbour", at = @At(value = "INVOKE", target = "isNeighbour"), index = 4) + private static IBlockState modifyIsNeighbour(IBlockState state) { return OptifineConnectedTexturesHookKt.modifyConnectedTexturesBlockState(state); } } From 8edd0070a55d422e6470aaed09ab72d8bc952578 Mon Sep 17 00:00:00 2001 From: Empa <42304516+ItsEmpa@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:45:21 +0200 Subject: [PATCH 077/102] Improvement: Add Pickobulus support to OreMineEvent (#2540) Co-authored-by: ItsEmpa Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../at/hannibal2/skyhanni/data/MiningAPI.kt | 124 ++++++++++++++++-- .../skyhanni/events/mining/OreMinedEvent.kt | 2 +- .../features/mining/MineshaftPityDisplay.kt | 7 +- .../skyhanni/test/SkyHanniDebugsAndTests.kt | 4 +- 4 files changed, 118 insertions(+), 19 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt index 91b7e4b79350..9d635b1b9a22 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt @@ -24,7 +24,9 @@ import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.inAnyIsland import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.formatInt import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher +import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.TimeUtils.format @@ -44,13 +46,44 @@ object MiningAPI { private val glaciteAreaPattern by group.pattern("area.glacite", "Glacite Tunnels|Great Glacite Lake") private val dwarvenBaseCampPattern by group.pattern("area.basecamp", "Dwarven Base Camp") - // TODO rename to include suffix "pattern", add regex test - private val coldReset by group.pattern( + // TODO add regex test + private val coldResetPattern by group.pattern( "cold.reset", - "§6The warmth of the campfire reduced your §r§b❄ Cold §r§6to §r§a0§r§6!|§c ☠ §r§7You froze to death§r§7.", + "§6The warmth of the campfire reduced your §r§b❄ Cold §r§6to §r§a0§r§6!|§c ☠ §r§7You froze to death§r§7\\.", ) - private data class MinedBlock(val ore: OreBlock, var confirmed: Boolean, val time: SimpleTimeMark = SimpleTimeMark.now()) + private val pickbobulusGroup = group.group("pickobulus") + + /** + * REGEX-TEST: §aYou used your §r§6Pickobulus §r§aPickaxe Ability! + */ + + private val pickobulusUsePattern by pickbobulusGroup.pattern( + "use", + "§aYou used your §r§6Pickobulus §r§aPickaxe Ability!", + ) + + // TODO add regex test + private val pickobulusEndPattern by pickbobulusGroup.pattern( + "end", + "§7Your §r§aPickobulus §r§7destroyed §r§e(?[\\d,.]+) §r§7blocks!", + ) + + /** + * REGEX-TEST: §7Your §r§aPickobulus §r§7didn't destroy any blocks! + */ + private val pickobulusFailPattern by pickbobulusGroup.pattern( + "fail", + "§7Your §r§aPickobulus §r§7didn't destroy any blocks!" + ) + + private data class MinedBlock(val ore: OreBlock, var confirmed: Boolean) { + val time: SimpleTimeMark = SimpleTimeMark.now() + } + + // normal mining + private val recentClickedBlocks = ConcurrentSet>() + private val surroundingMinedBlocks = ConcurrentLinkedQueue>() private var lastInitSound = SimpleTimeMark.farPast() @@ -60,6 +93,18 @@ object MiningAPI { private var waitingForEffMinerSound = false private var waitingForEffMinerBlock = false + // pickobulus + private var lastPickobulusUse = SimpleTimeMark.farPast() + private var lastPickobulusExplosion = SimpleTimeMark.farPast() + private var pickobulusExplosionPos: LorenzVec? = null + private val pickobulusMinedBlocks = ConcurrentLinkedQueue>() + + private val pickobulusActive get() = lastPickobulusUse.passedSince() < 2.seconds + + private var pickobulusWaitingForSound = false + private var pickobulusWaitingForBlock = false + + // oreblock data var inGlacite = false var inTunnels = false var inMineshaft = false @@ -72,15 +117,15 @@ object MiningAPI { var currentAreaOreBlocks = setOf() private set - private val recentClickedBlocks = ConcurrentSet>() - private val surroundingMinedBlocks = ConcurrentLinkedQueue>() private val allowedSoundNames = setOf("dig.glass", "dig.stone", "dig.gravel", "dig.cloth", "random.orb") var cold: Int = 0 private set var lastColdUpdate = SimpleTimeMark.farPast() + private set var lastColdReset = SimpleTimeMark.farPast() + private set fun inGlaciteArea() = inGlacialTunnels() || IslandType.MINESHAFT.isInIsland() @@ -128,10 +173,29 @@ object MiningAPI { @SubscribeEvent fun onChat(event: LorenzChatEvent) { if (!inColdIsland()) return - if (coldReset.matches(event.message)) { + if (coldResetPattern.matches(event.message)) { updateCold(0) lastColdReset = SimpleTimeMark.now() + return } + if (pickobulusUsePattern.matches(event.message)) { + lastPickobulusUse = SimpleTimeMark.now() + return + } + if (pickobulusFailPattern.matches(event.message)) { + resetPickobulusEvent() + pickobulusMinedBlocks.clear() + return + } + pickobulusEndPattern.matchMatcher(event.message) { + val amount = group("amount").formatInt() + resetPickobulusEvent() + val blocks = pickobulusMinedBlocks.take(amount).countBy { it.second } + if (blocks.isNotEmpty()) OreMinedEvent(null, blocks).post() + pickobulusMinedBlocks.clear() + return + } + } @SubscribeEvent @@ -145,8 +209,18 @@ object MiningAPI { @SubscribeEvent fun onPlaySound(event: PlaySoundEvent) { if (!inCustomMiningIsland()) return + if (event.soundName == "random.explode" && lastPickobulusUse.passedSince() < 5.seconds) { + lastPickobulusExplosion = SimpleTimeMark.now() + pickobulusExplosionPos = event.location + pickobulusWaitingForSound = true + return + } if (event.soundName !in allowedSoundNames) return - //println("Sound: ${event.soundName} ${event.pitch} ${event.volume} ${event.location.toCleanString()}") + if (pickobulusActive && pickobulusWaitingForSound) { + pickobulusWaitingForSound = false + pickobulusWaitingForBlock = true + return + } if (waitingForInitSound) { if (event.soundName != "random.orb" && event.pitch == 0.7936508f) { val pos = event.location.roundLocationToBlock() @@ -180,10 +254,19 @@ object MiningAPI { if (newBlock != Blocks.air && newBlock != Blocks.bedrock && !isTitanium(newState)) return val pos = event.location - if (pos.distanceToPlayer() > 7) return - //println("Block change: $oldState -> $newState ${pos.toCleanString()}") + if (pickobulusActive && pickobulusWaitingForBlock) { + val explosionPos = pickobulusExplosionPos ?: return + if (explosionPos.distance(pos) > 15) return + val ore = OreBlock.getByStateOrNull(oldState) ?: return + if (pickobulusMinedBlocks.any { it.first == pos }) return + pickobulusMinedBlocks += pos to ore + pickobulusWaitingForBlock = false + pickobulusWaitingForSound = true + return + } if (lastInitSound.passedSince() > 100.milliseconds) return + if (pos.distanceToPlayer() > 7) return val ore = OreBlock.getByStateOrNull(oldState) ?: return @@ -210,10 +293,13 @@ object MiningAPI { recentClickedBlocks.removeIf { it.second.passedSince() >= 20.seconds } surroundingMinedBlocks.removeIf { it.first.time.passedSince() >= 20.seconds } - if (waitingForInitSound) return - if (lastInitSound.passedSince() < 200.milliseconds) return - // in case the init block is not found - resetOreEvent() + if (!waitingForInitSound && lastInitSound.passedSince() > 200.milliseconds) { + resetOreEvent() + } + if (!lastPickobulusUse.isFarPast() && lastPickobulusUse.passedSince() > 5.seconds) { + resetPickobulusEvent() + pickobulusMinedBlocks.clear() + } } @HandleEvent @@ -252,8 +338,10 @@ object MiningAPI { lastColdReset = SimpleTimeMark.now() recentClickedBlocks.clear() surroundingMinedBlocks.clear() + pickobulusMinedBlocks.clear() currentAreaOreBlocks = setOf() resetOreEvent() + resetPickobulusEvent() } private fun resetOreEvent() { @@ -264,6 +352,14 @@ object MiningAPI { waitingForEffMinerBlock = false } + private fun resetPickobulusEvent() { + lastPickobulusUse = SimpleTimeMark.farPast() + lastPickobulusExplosion = SimpleTimeMark.farPast() + pickobulusExplosionPos = null + pickobulusWaitingForSound = false + pickobulusWaitingForBlock = false + } + @SubscribeEvent fun onDebugDataCollect(event: DebugDataCollectEvent) { event.title("Mining API") diff --git a/src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt index a97402a4179a..d74e65eced2a 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/mining/OreMinedEvent.kt @@ -3,4 +3,4 @@ package at.hannibal2.skyhanni.events.mining import at.hannibal2.skyhanni.api.event.SkyHanniEvent import at.hannibal2.skyhanni.features.mining.OreBlock -class OreMinedEvent(val originalOre: OreBlock, val extraBlocks: Map) : SkyHanniEvent() +class OreMinedEvent(val originalOre: OreBlock?, val extraBlocks: Map) : SkyHanniEvent() diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt index 8497951febb1..ec9a390edff0 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt @@ -85,9 +85,12 @@ object MineshaftPityDisplay { fun onOreMined(event: OreMinedEvent) { if (!MiningAPI.inGlacialTunnels()) return - event.originalOre.getPityBlock()?.let { it.blocksBroken++ } + val originalOre = event.originalOre + originalOre?.getPityBlock()?.let { it.blocksBroken++ } event.extraBlocks.toMutableMap() - .apply { addOrPut(event.originalOre, -1) } + .apply { + if (originalOre != null) addOrPut(originalOre, -1) + } .map { (block, amount) -> block.getPityBlock()?.let { it.efficientMiner += amount } } diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index 32b287cb96a2..c0c03b6771ab 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -597,9 +597,9 @@ object SkyHanniDebugsAndTests { @HandleEvent(onlyOnSkyblock = true) fun onOreMined(event: OreMinedEvent) { if (!debugConfig.oreEventMessages) return - val originalOre = event.originalOre + val originalOre = event.originalOre?.let { "$it " } ?: "" val extraBlocks = event.extraBlocks.map { "${it.key.name}: ${it.value}" } - ChatUtils.debug("Mined: $originalOre (${extraBlocks.joinToString()})") + ChatUtils.debug("Mined: $originalOre(${extraBlocks.joinToString()})") } @SubscribeEvent From 74490dcdb619e6eadf1be7665871cdaba4d58ee8 Mon Sep 17 00:00:00 2001 From: J10a1n15 <45315647+j10a1n15@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:45:56 +0200 Subject: [PATCH 078/102] Backend: Carry Tracker Number format depending on Custom Scoreboard (#2571) --- .../at/hannibal2/skyhanni/features/misc/CarryTracker.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt index f70fa58b8517..bfecbc48f57f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CarryTracker.kt @@ -7,7 +7,6 @@ import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.RepositoryReloadEvent import at.hannibal2.skyhanni.events.entity.slayer.SlayerDeathEvent -import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboardUtils.formatNum import at.hannibal2.skyhanni.features.slayer.SlayerType import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils @@ -15,6 +14,7 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.addString import at.hannibal2.skyhanni.utils.HypixelCommands import at.hannibal2.skyhanni.utils.KeyboardManager import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble import at.hannibal2.skyhanni.utils.NumberUtil.formatDoubleOrUserError import at.hannibal2.skyhanni.utils.NumberUtil.formatIntOrUserError @@ -181,7 +181,7 @@ object CarryTracker { val price = rawPrice.formatDoubleOrUserError() ?: return carryType.pricePer = price update() - ChatUtils.chat("Set carry price for $carryType §eto §6${price.formatNum()} coins.") + ChatUtils.chat("Set carry price for $carryType §eto §6${price.shortFormat()} coins.") } private fun getCustomer(customerName: String): Customer { @@ -260,7 +260,7 @@ object CarryTracker { val totalCost = customer.carries.sumOf { it.getCost() ?: 0.0 } val totalCostFormat = formatCost(totalCost) if (totalCostFormat != "") { - val paidFormat = "§6${customer.alreadyPaid.formatNum()}" + val paidFormat = "§6${customer.alreadyPaid.shortFormat()}" val missingFormat = formatCost(totalCost - customer.alreadyPaid) list.add( Renderable.clickAndHover( @@ -293,7 +293,7 @@ object CarryTracker { }?.takeIf { it != 0.0 } } - private fun formatCost(totalCost: Double?): String = if (totalCost == 0.0 || totalCost == null) "" else "§6${totalCost.formatNum()}" + private fun formatCost(totalCost: Double?): String = if (totalCost == 0.0 || totalCost == null) "" else "§6${totalCost.shortFormat()}" private fun createCarryType(input: String): CarryType? { if (input.length == 1) return null From d39f595b150aaa0eee32c7ebacb961ef48095924 Mon Sep 17 00:00:00 2001 From: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Date: Mon, 23 Sep 2024 23:54:16 +1000 Subject: [PATCH 079/102] Backend: Remove more neu code from SkyHanni (#2419) --- .github/workflows/illegal-imports.txt | 3 +- .idea/dictionaries/default_user.xml | 3 +- build.gradle.kts | 6 +- .../events/NeuRepositoryReloadEvent.kt | 3 +- .../skyhanni/features/chat/ChatFilterGui.kt | 15 +- .../combat/ghostcounter/GhostCounter.kt | 3 +- .../features/combat/ghostcounter/GhostUtil.kt | 56 +++----- .../features/garden/GardenNextJacobContest.kt | 2 +- .../garden/inventory/plots/GardenPlotIcon.kt | 4 +- .../garden/visitor/GardenVisitorSupercraft.kt | 6 +- .../HighlightVisitorsOutsideOfGarden.kt | 5 +- .../AuctionHouseOpenPriceWebsite.kt | 4 +- .../bazaar/BazaarOpenPriceWebsite.kt | 6 +- .../skyhanni/features/mining/ColdOverlay.kt | 4 +- .../features/mining/DeepCavernsGuide.kt | 4 +- .../features/misc/UserLuckBreakdown.kt | 12 +- .../features/misc/limbo/LimboPlaytime.kt | 4 +- .../DefaultConfigOptionGui.kt | 21 +-- .../rift/everywhere/EnigmaSoulWaypoints.kt | 4 +- .../features/skillprogress/SkillType.kt | 4 +- .../test/SkyHanniConfigSearchResetCommand.kt | 2 +- .../skyhanni/utils/GuiRenderUtils.kt | 132 +++++++++++++----- .../at/hannibal2/skyhanni/utils/ItemUtils.kt | 56 +++++++- .../at/hannibal2/skyhanni/utils/NEUItems.kt | 3 +- .../hannibal2/skyhanni/utils/RenderUtils.kt | 28 ++-- .../utils/SkyBlockItemModifierUtils.kt | 3 +- .../skyhanni/utils/json/JsonUtils.kt | 18 +++ .../hannibal2/skyhanni/utils/json/Shimmy.kt | 64 +++++++++ .../skyhanni/utils/renderables/Renderable.kt | 27 +--- .../utils/renderables/RenderableTooltips.kt | 5 +- 30 files changed, 338 insertions(+), 169 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/utils/json/Shimmy.kt diff --git a/.github/workflows/illegal-imports.txt b/.github/workflows/illegal-imports.txt index f53a5da78e25..83354f622960 100644 --- a/.github/workflows/illegal-imports.txt +++ b/.github/workflows/illegal-imports.txt @@ -5,7 +5,8 @@ at/hannibal2/skyhanni/ scala. at/hannibal2/skyhanni/ jline. -at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.util.Constants at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.events.SlotClickEvent at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.events.ReplaceItemEvent +at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.util.Constants +at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.util.Utils at/hannibal2/skyhanni/ java.util.function.Supplier diff --git a/.idea/dictionaries/default_user.xml b/.idea/dictionaries/default_user.xml index cb91301718f0..a0017ca5e3e0 100644 --- a/.idea/dictionaries/default_user.xml +++ b/.idea/dictionaries/default_user.xml @@ -254,6 +254,7 @@ superpairs tablist terracottas + tessellator thaumaturgist thaumaturgy townsquare @@ -277,4 +278,4 @@ yolkar - + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 447387f86a72..add2df4e77e2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -160,9 +160,9 @@ dependencies { exclude(module = "unspecified") isTransitive = false } - // June 3, 2024, 9:30 PM AEST - // https://github.com/NotEnoughUpdates/NotEnoughUpdates/tree/2.3.0 - devenvMod("com.github.NotEnoughUpdates:NotEnoughUpdates:2.3.0:all") { + // August 27, 2024, 4:30 PM AEST + // https://github.com/NotEnoughUpdates/NotEnoughUpdates/tree/2.3.3 + devenvMod("com.github.NotEnoughUpdates:NotEnoughUpdates:2.3.3:all") { exclude(module = "unspecified") isTransitive = false } diff --git a/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt index c2bd261ca0a1..97a3f580c555 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt @@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.data.repo.RepoUtils import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.NEUItems.manager import at.hannibal2.skyhanni.utils.json.fromJson +import at.hannibal2.skyhanni.utils.json.getJson import com.google.gson.Gson import com.google.gson.JsonObject import com.google.gson.JsonSyntaxException @@ -15,7 +16,7 @@ import java.lang.reflect.Type class NeuRepositoryReloadEvent : LorenzEvent() { fun getConstant(file: String): JsonObject? { - return manager.getJsonFromFile(File(manager.repoLocation, "constants/$file.json")) + return File(manager.repoLocation, "constants/$file.json").getJson() } inline fun readConstant(file: String, gson: Gson = ConfigManager.gson): T { diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilterGui.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilterGui.kt index 0fee71849b4f..6a5f22b6635a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilterGui.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilterGui.kt @@ -5,7 +5,8 @@ import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.KeyboardManager import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.StringUtils.stripHypixelMessage -import io.github.moulberry.notenoughupdates.util.Utils +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.renderables.RenderableTooltips import io.github.notenoughupdates.moulconfig.internal.GlScissorStack import io.github.notenoughupdates.moulconfig.internal.RenderUtils import net.minecraft.client.Minecraft @@ -42,7 +43,7 @@ class ChatFilterGui(private val history: List + RenderableTooltips.setTooltipForRender(tooltip.map { Renderable.string(it) }) } GlStateManager.color(1f, 1f, 1f, 1f) } @@ -98,7 +99,7 @@ class ChatFilterGui(private val history: List${if (SkyHanniMod.feature.combat.ghostCounter.showMax) "25" else nextLevel}" - ) - ) + this.replace("%value%", t) + .replace( + "%display%", + "$level->${if (SkyHanniMod.feature.combat.ghostCounter.showMax) "25" else nextLevel}", + ) } - fun String.formatText(value: Double, session: Double) = Utils.chromaStringByColourCode( - this.replace("%value%", value.roundToPrecision(2).addSeparators()) - .replace("%session%", session.roundToPrecision(2).addSeparators()) - .replace("&", "§") - ) + fun String.formatText(value: Double, session: Double) = this.replace("%value%", value.roundToPrecision(2).addSeparators()) + .replace("%session%", session.roundToPrecision(2).addSeparators()) + .replace("&", "§") fun String.formatBestiary(currentKill: Int, killNeeded: Int): String { val bestiaryNextLevel = GhostCounter.storage?.bestiaryNextLevel @@ -127,17 +117,15 @@ object GhostUtil { val nextLevel = bestiaryNextLevel?.let { if (GhostCounter.config.showMax) "25" else "${it.toInt()}" } ?: "§cNo Bestiary Level data!" - return Utils.chromaStringByColourCode( - this.replace( - "%currentKill%", - if (GhostCounter.config.showMax) GhostCounter.bestiaryCurrentKill.addSeparators() else currentKill.addSeparators() - ) - .replace("%percentNumber%", percent(GhostCounter.bestiaryCurrentKill.toDouble())) - .replace("%killNeeded%", killNeeded.shortFormat()) - .replace("%currentLevel%", currentLevel) - .replace("%nextLevel%", nextLevel) - .replace("&", "§") + return this.replace( + "%currentKill%", + if (GhostCounter.config.showMax) GhostCounter.bestiaryCurrentKill.addSeparators() else currentKill.addSeparators(), ) + .replace("%percentNumber%", percent(GhostCounter.bestiaryCurrentKill.toDouble())) + .replace("%killNeeded%", killNeeded.shortFormat()) + .replace("%currentLevel%", currentLevel) + .replace("%nextLevel%", nextLevel) + .replace("&", "§") } private fun percent(number: Double) = diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt index 8c2dc9a95e08..ab8820b40fd6 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt @@ -32,10 +32,10 @@ import at.hannibal2.skyhanni.utils.SoundUtils import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.TabListData import at.hannibal2.skyhanni.utils.TimeUtils.format +import at.hannibal2.skyhanni.utils.json.toJsonArray import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import com.google.gson.Gson import com.google.gson.JsonPrimitive -import io.github.moulberry.notenoughupdates.util.toJsonArray import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/plots/GardenPlotIcon.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/plots/GardenPlotIcon.kt index 1fc378b7e890..01e9f87fb4df 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/plots/GardenPlotIcon.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/plots/GardenPlotIcon.kt @@ -8,10 +8,10 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.ItemUtils.editItemInfo import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.NEUItems.getItemStack -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.player.inventory.ContainerLocalMenu import net.minecraft.init.Items import net.minecraft.item.ItemStack @@ -51,7 +51,7 @@ object GardenPlotIcon { for ((index, internalName) in plotList) { val old = originalStack[index]!! val new = internalName.getItemStack() - cachedStack[index] = Utils.editItemStackInfo(new, old.displayName, true, *old.getLore().toTypedArray()) + cachedStack[index] = new.editItemInfo(old.displayName, true, old.getLore()) } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt index 26bfa7f26409..f2e4f80fb8f1 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt @@ -8,12 +8,12 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.HypixelCommands +import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.SimpleTimeMark -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.entity.player.InventoryPlayer import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -30,11 +30,11 @@ object GardenVisitorSupercraft { private val superCraftItem by lazy { val neuItem = "GOLD_PICKAXE".asInternalName().getItemStack() - Utils.createItemStack( + ItemUtils.createItemStack( neuItem.item, "§bSuper Craft", "§7You have the items to craft", - "§7Click me to open the super crafter!" + "§7Click me to open the super crafter!", ) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/HighlightVisitorsOutsideOfGarden.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/HighlightVisitorsOutsideOfGarden.kt index 88ea75b572b9..8a8c11165564 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/HighlightVisitorsOutsideOfGarden.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/HighlightVisitorsOutsideOfGarden.kt @@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.features.garden.visitor import at.hannibal2.skyhanni.api.event.HandleEvent import at.hannibal2.skyhanni.config.features.garden.visitor.VisitorConfig.VisitorBlockBehaviour +import at.hannibal2.skyhanni.data.HypixelData import at.hannibal2.skyhanni.data.jsonobjects.repo.GardenJson import at.hannibal2.skyhanni.data.jsonobjects.repo.GardenVisitor import at.hannibal2.skyhanni.events.RepositoryReloadEvent @@ -19,7 +20,6 @@ import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.getLorenzVec import at.hannibal2.skyhanni.utils.toLorenzVec -import io.github.moulberry.notenoughupdates.util.SBInfo import net.minecraft.client.Minecraft import net.minecraft.entity.Entity import net.minecraft.entity.EntityLivingBase @@ -57,7 +57,8 @@ object HighlightVisitorsOutsideOfGarden { } private fun isVisitor(entity: Entity): Boolean { - val mode = SBInfo.getInstance().getLocation() + // todo migrate to Skyhanni IslandType + val mode = HypixelData.mode val possibleJsons = visitorJson[mode] ?: return false val skinOrType = getSkinOrTypeFor(entity) return possibleJsons.any { diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/auctionhouse/AuctionHouseOpenPriceWebsite.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/auctionhouse/AuctionHouseOpenPriceWebsite.kt index b253e01911f2..7229283ca9c9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/auctionhouse/AuctionHouseOpenPriceWebsite.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/auctionhouse/AuctionHouseOpenPriceWebsite.kt @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStack @@ -13,7 +14,6 @@ import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.entity.player.InventoryPlayer import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.EventPriority @@ -48,7 +48,7 @@ object AuctionHouseOpenPriceWebsite { } } - private fun createDisplayItem() = Utils.createItemStack( + private fun createDisplayItem() = ItemUtils.createItemStack( "PAPER".asInternalName().getItemStack().item, "§bPrice History", "§7Click here to open", diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarOpenPriceWebsite.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarOpenPriceWebsite.kt index 61606346ad9e..216d3e10872a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarOpenPriceWebsite.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarOpenPriceWebsite.kt @@ -4,12 +4,12 @@ import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.GuiContainerEvent import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.SimpleTimeMark -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.entity.player.InventoryPlayer import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -23,12 +23,12 @@ object BazaarOpenPriceWebsite { private val item by lazy { val neuItem = "PAPER".asInternalName().getItemStack() - Utils.createItemStack( + ItemUtils.createItemStack( neuItem.item, "§bPrice History", "§7Click here to open", "§7the price history", - "§7on §cskyblock.bz" + "§7on §cskyblock.bz", ) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/ColdOverlay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/ColdOverlay.kt index 8af757f6e074..58ef41ce7e8b 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/ColdOverlay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/ColdOverlay.kt @@ -6,8 +6,8 @@ import at.hannibal2.skyhanni.events.ColdUpdateEvent import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.DelayedRun +import at.hannibal2.skyhanni.utils.GuiRenderUtils import at.hannibal2.skyhanni.utils.NumberUtil -import at.hannibal2.skyhanni.utils.RenderUtils import at.hannibal2.skyhanni.utils.SimpleTimeMark import net.minecraft.client.Minecraft import net.minecraft.client.renderer.GlStateManager @@ -42,7 +42,7 @@ object ColdOverlay { GlStateManager.translate(0f, 0f, -500f) GlStateManager.color(1f, 1f, 1f, alpha) - RenderUtils.drawTexturedRect(0f, 0f) + GuiRenderUtils.drawTexturedRect(0f, 0f) GL11.glDepthMask(true) GlStateManager.popMatrix() diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/DeepCavernsGuide.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/DeepCavernsGuide.kt index 9600fbc20dd1..a1d8f43a8d5b 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/DeepCavernsGuide.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/DeepCavernsGuide.kt @@ -16,12 +16,12 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor import at.hannibal2.skyhanni.utils.ConditionalUtils +import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.ParkourHelper -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.player.inventory.ContainerLocalMenu import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -37,7 +37,7 @@ object DeepCavernsGuide { private val startIcon by lazy { val neuItem = "MAP".asInternalName().getItemStack() - Utils.createItemStack( + ItemUtils.createItemStack( neuItem.item, "§bDeep Caverns Guide", "§8(From SkyHanni)", diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt index d4532a77527a..7f041bc27f9a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt @@ -10,6 +10,7 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.features.skillprogress.SkillType import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut +import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils @@ -20,7 +21,6 @@ import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.player.inventory.ContainerLocalMenu import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -237,9 +237,11 @@ object UserLuckBreakdown { } private fun createItems() { - fillerItem = Utils.createItemStack( + fillerItem = ItemUtils.createItemStack( fillerID.getItemStack().item, fillerName, + listOf(), + 1, 15, ) @@ -247,17 +249,17 @@ object UserLuckBreakdown { val skillLuck = skillOverflowLuck.values.sum() val totalLuck = skillLuck + limboLuck - mainLuckItem = Utils.createItemStack( + mainLuckItem = ItemUtils.createItemStack( mainLuckID.getItemStack().item, "$mainLuckName §f${tryTruncateFloat(totalLuck)}", *createItemLore("mainMenu", totalLuck), ) - limboItem = Utils.createItemStack( + limboItem = ItemUtils.createItemStack( limboID.getItemStack().item, limboName, *createItemLore("limbo", limboLuck), ) - skillsItem = Utils.createItemStack( + skillsItem = ItemUtils.createItemStack( skillsID.getItemStack().item, skillsName, *createItemLore("skills"), diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt index 0996701f23ca..666671e9ca3f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.InventoryOpenEvent import at.hannibal2.skyhanni.events.LorenzToolTipEvent import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName @@ -14,7 +15,6 @@ import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.player.inventory.ContainerLocalMenu import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -58,7 +58,7 @@ object LimboPlaytime { if (lastCreateCooldown.passedSince() > 3.seconds) { lastCreateCooldown = SimpleTimeMark.now() - limboItem = Utils.createItemStack( + limboItem = ItemUtils.createItemStack( itemID.getItemStack().item, itemName, *createItemLore() diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt index a68978caec21..bbe40db5cd4a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/massconfiguration/DefaultConfigOptionGui.kt @@ -1,6 +1,7 @@ package at.hannibal2.skyhanni.features.misc.massconfiguration -import io.github.moulberry.notenoughupdates.util.Utils +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.renderables.RenderableTooltips import io.github.notenoughupdates.moulconfig.internal.GlScissorStack import io.github.notenoughupdates.moulconfig.internal.RenderUtils import io.github.notenoughupdates.moulconfig.internal.TextRenderUtils @@ -66,7 +67,7 @@ class DefaultConfigOptionGui( mc.fontRendererObj.FONT_HEIGHT.toFloat(), false, xSize / 2 - padding, - -1 + -1, ) GlStateManager.popMatrix() @@ -74,7 +75,7 @@ class DefaultConfigOptionGui( GlStateManager.translate( (width - xSize) / 2F + padding, (height + ySize) / 2F - mc.fontRendererObj.FONT_HEIGHT * 2, - 0F + 0F, ) var i = 0 fun button(title: String, tooltip: List, func: () -> Unit) { @@ -95,7 +96,7 @@ class DefaultConfigOptionGui( 2 + i.toFloat(), 0F, if (overMouse) 0xFF00FF00.toInt() else -1, - overMouse + overMouse, ) i += width + 12 } @@ -132,12 +133,12 @@ class DefaultConfigOptionGui( (height - ySize) / 2 + barSize, (width + xSize) / 2, (height + ySize) / 2 - barSize, - scaledResolution + scaledResolution, ) GlStateManager.translate( (width - xSize) / 2F + padding, (height - ySize) / 2F + barSize - currentScrollOffset, - 0F + 0F, ) for ((cat) in orderedOptions.entries) { @@ -157,13 +158,13 @@ class DefaultConfigOptionGui( "§7${cat.description}", "§7Current plan: ${suggestionState.label}", "§aClick to toggle!", - "§7Hold shift to show all options" + "§7Hold shift to show all options", ) if (isShiftKeyDown()) { hoveringTextToDraw = listOf( "§e${cat.name}", - "§7${cat.description}" + "§7${cat.description}", ) + orderedOptions[cat]!!.map { "§7 - §a" + it.name } } @@ -179,8 +180,8 @@ class DefaultConfigOptionGui( GlStateManager.popMatrix() GlScissorStack.pop(scaledResolution) - if (hoveringTextToDraw != null) { - Utils.drawHoveringText(hoveringTextToDraw, mouseX, mouseY, width, height, 100, mc.fontRendererObj) + hoveringTextToDraw?.let { tooltip -> + RenderableTooltips.setTooltipForRender(tooltip.map { Renderable.string(it) }) } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt index d91b4008d912..2a4613f7a7bc 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/EnigmaSoulWaypoints.kt @@ -14,6 +14,7 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor import at.hannibal2.skyhanni.utils.InventoryUtils.getAllItems +import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzColor @@ -24,7 +25,6 @@ import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.client.player.inventory.ContainerLocalMenu import net.minecraft.inventory.ContainerChest @@ -43,7 +43,7 @@ object EnigmaSoulWaypoints { private val item by lazy { val neuItem = "SKYBLOCK_ENIGMA_SOUL".asInternalName().getItemStack() - Utils.createItemStack( + ItemUtils.createItemStack( neuItem.item, "§5Toggle Missing", "§7Click here to toggle", diff --git a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt index bd44a63f39b7..b10bc8cb6c08 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt @@ -1,6 +1,6 @@ package at.hannibal2.skyhanni.features.skillprogress -import io.github.moulberry.notenoughupdates.util.Utils +import at.hannibal2.skyhanni.utils.ItemUtils import net.minecraft.block.Block import net.minecraft.init.Blocks import net.minecraft.init.Items @@ -21,7 +21,7 @@ enum class SkillType(val displayName: String, icon: Item) { constructor(displayName: String, block: Block) : this(displayName, Item.getItemFromBlock(block)) - val item: ItemStack by lazy { Utils.createItemStack(icon, displayName) } + val item: ItemStack by lazy { ItemUtils.createItemStack(icon, displayName) } val lowercaseName = displayName.lowercase() val uppercaseName = displayName.uppercase() diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniConfigSearchResetCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniConfigSearchResetCommand.kt index 8a74510828eb..34bf36b5fbbf 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniConfigSearchResetCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniConfigSearchResetCommand.kt @@ -10,8 +10,8 @@ import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.ReflectionUtils.makeAccessible +import at.hannibal2.skyhanni.utils.json.Shimmy import com.google.gson.JsonElement -import io.github.moulberry.notenoughupdates.util.Shimmy import kotlinx.coroutines.launch import java.lang.reflect.Field import java.lang.reflect.Modifier diff --git a/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt index 75eb5087ec7f..ba1081a5f3dd 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt @@ -7,16 +7,17 @@ import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.fractionOf import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment import at.hannibal2.skyhanni.utils.renderables.Renderable -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.Minecraft import net.minecraft.client.gui.FontRenderer import net.minecraft.client.gui.GuiScreen +import net.minecraft.client.gui.ScaledResolution import net.minecraft.client.renderer.GlStateManager import net.minecraft.client.renderer.RenderHelper import net.minecraft.client.renderer.Tessellator import net.minecraft.client.renderer.vertex.DefaultVertexFormats import net.minecraft.item.ItemStack import org.lwjgl.opengl.GL11 +import org.lwjgl.opengl.GL14 import java.awt.Color import java.text.DecimalFormat import kotlin.math.ceil @@ -75,7 +76,7 @@ object GuiRenderUtils { fun drawStringCentered(str: String?, x: Int, y: Int) { drawStringCentered( - str, Minecraft.getMinecraft().fontRendererObj, x.toFloat(), y.toFloat(), true, 0xffffff + str, Minecraft.getMinecraft().fontRendererObj, x.toFloat(), y.toFloat(), true, 0xffffff, ) } @@ -125,25 +126,25 @@ object GuiRenderUtils { if (tooltipY + tooltipHeight + 6 > screenHeight) tooltipY = screenHeight - tooltipHeight - 6 // main background GuiScreen.drawRect( - tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, -0xfeffff0 + tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, -0xfeffff0, ) // borders GuiScreen.drawRect( - tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1, tooltipY + tooltipHeight + 3 - 1, borderColor + tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1, tooltipY + tooltipHeight + 3 - 1, borderColor, - ) + ) GuiScreen.drawRect( tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, - borderColor + borderColor, ) GuiScreen.drawRect( - tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY - 3 + 1, borderColor + tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY - 3 + 1, borderColor, ) GuiScreen.drawRect( @@ -151,7 +152,7 @@ object GuiRenderUtils { tooltipY + tooltipHeight + 2, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, - borderColor + borderColor, ) GlStateManager.translate(0f, 0f, -100f) GlStateManager.disableDepth() @@ -188,27 +189,33 @@ object GuiRenderUtils { val current = currentValue.toDouble().coerceAtLeast(0.0) val percent = current.fractionOf(maxValue) val scale = textScale.toDouble() - return Renderable.hoverTips(Renderable.verticalContainer( - listOf( - Renderable.string(label, scale = scale), - Renderable.fixedSizeLine( - listOf( - Renderable.string( - "§2${DecimalFormat("0.##").format(current)} / ${ - DecimalFormat( - "0.##" - ).format(maxValue) - }☘", scale = scale, horizontalAlign = HorizontalAlignment.LEFT + return Renderable.hoverTips( + Renderable.verticalContainer( + listOf( + Renderable.string(label, scale = scale), + Renderable.fixedSizeLine( + listOf( + Renderable.string( + "§2${DecimalFormat("0.##").format(current)} / ${ + DecimalFormat( + "0.##", + ).format(maxValue) + }☘", + scale = scale, horizontalAlign = HorizontalAlignment.LEFT, + ), + Renderable.string( + "§2${(percent * 100).round(1)}%", + scale = scale, + horizontalAlign = HorizontalAlignment.RIGHT, + ), ), - Renderable.string( - "§2${(percent * 100).round(1)}%", - scale = scale, - horizontalAlign = HorizontalAlignment.RIGHT - ), - ), width - ), Renderable.progressBar(percent, width = width) - ) - ), tooltip.split('\n').map { Renderable.string(it) }) + width, + ), + Renderable.progressBar(percent, width = width), + ), + ), + tooltip.split('\n').map { Renderable.string(it) }, + ) } private fun barColorGradient(double: Double): Int { @@ -265,16 +272,14 @@ object GuiRenderUtils { Color.LIGHT_GRAY.darker().red / 255f, Color.LIGHT_GRAY.darker().green / 255f, Color.LIGHT_GRAY.darker().blue / 255f, - 1f + 1f, ) } else { GlStateManager.color(color.darker().red / 255f, color.darker().green / 255f, color.darker().blue / 255f, 1f) } - Utils.drawTexturedRect(x, y, w_2.toFloat(), height, 0f, w_2 / xSize, vMinEmpty, vMaxEmpty, GL11.GL_NEAREST) - Utils.drawTexturedRect( - x + w_2, y, w_2.toFloat(), height, 1 - w_2 / xSize, 1f, vMinEmpty, vMaxEmpty, GL11.GL_NEAREST - ) + drawTexturedRect(x, y, w_2.toFloat(), height, 0f, w_2 / xSize, vMinEmpty, vMaxEmpty, GL11.GL_NEAREST) + drawTexturedRect(x + w_2, y, w_2.toFloat(), height, 1 - w_2 / xSize, 1f, vMinEmpty, vMaxEmpty, GL11.GL_NEAREST) if (useChroma) { GlStateManager.color(Color.WHITE.red / 255f, Color.WHITE.green / 255f, Color.WHITE.blue / 255f, 1f) @@ -285,9 +290,9 @@ object GuiRenderUtils { if (k > 0) { val uMax = w_2.toDouble().coerceAtMost(k.toDouble() / xSize).toFloat() val width = w_2.coerceAtMost(k).toFloat() - Utils.drawTexturedRect(x, y, width, height, 0f, uMax, vMinFilled, vMaxFilled, GL11.GL_NEAREST) + drawTexturedRect(x, y, width, height, 0f, uMax, vMinFilled, vMaxFilled, GL11.GL_NEAREST) if (completed > 0.5f) { - Utils.drawTexturedRect( + drawTexturedRect( x + w_2, y, (k - w_2).toFloat(), @@ -296,7 +301,7 @@ object GuiRenderUtils { 1 + (k - w) / xSize, vMinFilled, vMaxFilled, - GL11.GL_NEAREST + GL11.GL_NEAREST, ) } } @@ -342,4 +347,59 @@ object GuiRenderUtils { GlStateManager.enableAlpha() GlStateManager.enableTexture2D() } + + fun drawTexturedRect(x: Float, y: Float) { + with(ScaledResolution(Minecraft.getMinecraft())) { + drawTexturedRect(x, y, scaledWidth.toFloat(), scaledHeight.toFloat(), filter = GL11.GL_NEAREST) + } + } + + fun drawTexturedRect( + x: Int, + y: Int, + width: Int, + height: Int, + uMin: Float = 0f, + uMax: Float = 1f, + vMin: Float = 0f, + vMax: Float = 1f, + filter: Int = GL11.GL_NEAREST, + ) { + drawTexturedRect(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), uMin, uMax, vMin, vMax, filter) + } + + // Taken from NEU + fun drawTexturedRect( + x: Float, + y: Float, + width: Float, + height: Float, + uMin: Float = 0f, + uMax: Float = 1f, + vMin: Float = 0f, + vMax: Float = 1f, + filter: Int = GL11.GL_NEAREST, + ) { + GlStateManager.enableTexture2D() + GlStateManager.enableBlend() + GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA) + GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA) + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter) + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter) + + val tessellator = Tessellator.getInstance() + val worldRenderer = tessellator.worldRenderer + worldRenderer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX) + worldRenderer.pos(x.toDouble(), (y + height).toDouble(), 0.0).tex(uMin.toDouble(), vMax.toDouble()).endVertex() + worldRenderer.pos((x + width).toDouble(), (y + height).toDouble(), 0.0).tex(uMax.toDouble(), vMax.toDouble()).endVertex() + worldRenderer.pos((x + width).toDouble(), y.toDouble(), 0.0).tex(uMax.toDouble(), vMin.toDouble()).endVertex() + worldRenderer.pos(x.toDouble(), y.toDouble(), 0.0).tex(uMin.toDouble(), vMin.toDouble()).endVertex() + tessellator.draw() + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST) + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST) + + GlStateManager.disableBlend() + } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt index ac35c07418ba..add66cc0adcf 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt @@ -22,6 +22,7 @@ import com.google.common.collect.Lists import io.github.moulberry.notenoughupdates.util.NotificationHandler import net.minecraft.client.Minecraft import net.minecraft.init.Items +import net.minecraft.item.Item import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagList @@ -43,9 +44,11 @@ object ItemUtils { fun isSack(stack: ItemStack) = stack.getInternalName().endsWith("_SACK") && stack.cleanName().endsWith(" Sack") - fun ItemStack.getLore(): List { - val tagCompound = this.tagCompound ?: return emptyList() - val tagList = tagCompound.getCompoundTag("display").getTagList("Lore", 8) + fun ItemStack.getLore(): List = this.tagCompound.getLore() + + fun NBTTagCompound?.getLore(): List { + this ?: return emptyList() + val tagList = this.getCompoundTag("display").getTagList("Lore", 8) val list: MutableList = ArrayList() for (i in 0 until tagList.tagCount()) { list.add(tagList.getStringTagAt(i)) @@ -53,6 +56,13 @@ object ItemUtils { return list } + fun getDisplayName(compound: NBTTagCompound?): String? { + compound ?: return null + val name = compound.getCompoundTag("display").getString("Name") + if (name == null || name.isEmpty()) return null + return name + } + fun ItemStack.setLore(lore: List): ItemStack { val tagCompound = this.tagCompound ?: NBTTagCompound() val display = tagCompound.getCompoundTag("display") @@ -67,12 +77,14 @@ object ItemUtils { } var ItemStack.extraAttributes: NBTTagCompound - get() = this.tagCompound?.getCompoundTag("ExtraAttributes") ?: NBTTagCompound() + get() = this.tagCompound?.extraAttributes ?: NBTTagCompound() set(value) { val tag = this.tagCompound ?: NBTTagCompound().also { tagCompound = it } tag.setTag("ExtraAttributes", value) } + val NBTTagCompound.extraAttributes: NBTTagCompound get() = this.getCompoundTag("ExtraAttributes") + fun ItemStack.overrideId(id: String): ItemStack { extraAttributes = extraAttributes.apply { setString("id", id) } return this @@ -183,6 +195,20 @@ object ItemUtils { return render } + fun createItemStack(item: Item, displayName: String, vararg lore: String): ItemStack { + return createItemStack(item, displayName, lore.toList()) + } + + // Taken from NEU + fun createItemStack(item: Item, displayName: String, lore: List, amount: Int = 1, damage: Int = 0): ItemStack { + val stack = ItemStack(item, amount, damage) + val tag = NBTTagCompound() + addNameAndLore(tag, displayName, *lore.toTypedArray()) + tag.setInteger("HideFlags", 254) + stack.tagCompound = tag + return stack + } + // Taken from NEU private fun addNameAndLore(tag: NBTTagCompound, displayName: String, vararg lore: String) { val display = NBTTagCompound() @@ -315,6 +341,28 @@ object ItemUtils { setStackDisplayName(value) } + // Taken from NEU + fun ItemStack.editItemInfo(displayName: String, disableNeuTooltips: Boolean, lore: List): ItemStack { + val tag = this.tagCompound ?: NBTTagCompound() + val display = tag.getCompoundTag("display") + val loreList = NBTTagList() + for (line in lore) { + loreList.appendTag(NBTTagString(line)) + } + + display.setString("Name", displayName) + display.setTag("Lore", loreList) + + tag.setTag("display", display) + tag.setInteger("HideFlags", 254) + if (disableNeuTooltips) { + tag.setBoolean("disableNeuTooltip", true) + } + + this.tagCompound = tag + return this + } + fun isSkyBlockMenuItem(stack: ItemStack?): Boolean = stack?.getInternalName()?.equals("SKYBLOCK_MENU") ?: false private val itemAmountCache = mutableMapOf>() diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt index 34d491ceea81..198b43a856a6 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt @@ -31,7 +31,6 @@ import io.github.moulberry.notenoughupdates.events.ProfileDataLoadedEvent import io.github.moulberry.notenoughupdates.overlays.AuctionSearchOverlay import io.github.moulberry.notenoughupdates.overlays.BazaarSearchOverlay import io.github.moulberry.notenoughupdates.util.ItemResolutionQuery -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.Minecraft import net.minecraft.client.renderer.GLAllocation import net.minecraft.client.renderer.GlStateManager @@ -97,7 +96,7 @@ object NEUItems { val ignoreItemsFilter = MultiFilter() private val fallbackItem by lazy { - Utils.createItemStack( + ItemUtils.createItemStack( ItemStack(Blocks.barrier).item, "§cMissing Repo Item", "§cYour NEU repo seems to be out of date", diff --git a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt index 08532e0c4eec..609ccebae24b 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt @@ -19,11 +19,11 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.zipWithNext3 import at.hannibal2.skyhanni.utils.ColorUtils.getFirstColorCode import at.hannibal2.skyhanni.utils.LorenzColor.Companion.toLorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils.getCorners +import at.hannibal2.skyhanni.utils.compat.GuiScreenUtils import at.hannibal2.skyhanni.utils.renderables.Renderable import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXAligned import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderYAligned import at.hannibal2.skyhanni.utils.shader.ShaderManager -import io.github.moulberry.notenoughupdates.util.Utils import io.github.notenoughupdates.moulconfig.internal.TextRenderUtils import net.minecraft.client.Minecraft import net.minecraft.client.gui.FontRenderer @@ -41,6 +41,7 @@ import net.minecraft.item.ItemStack import net.minecraft.util.AxisAlignedBB import net.minecraft.util.MathHelper import net.minecraft.util.ResourceLocation +import org.lwjgl.input.Mouse import org.lwjgl.opengl.GL11 import java.awt.Color import java.nio.FloatBuffer @@ -95,6 +96,15 @@ object RenderUtils { highlight(color, x, y) } + fun getMouseX(): Int { + return Mouse.getX() * GuiScreenUtils.scaledWindowWidth / Minecraft.getMinecraft().displayWidth + } + + fun getMouseY(): Int { + val height = GuiScreenUtils.scaledWindowHeight + return height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1 + } + fun highlight(color: Color, x: Int, y: Int) { GlStateManager.disableLighting() GlStateManager.disableDepth() @@ -504,8 +514,8 @@ object RenderUtils { fun Position.transform(): Pair { GlStateManager.translate(getAbsX().toFloat(), getAbsY().toFloat(), 0F) GlStateManager.scale(effectiveScale, effectiveScale, 1F) - val x = ((Utils.getMouseX() - getAbsX()) / effectiveScale).toInt() - val y = ((Utils.getMouseY() - getAbsY()) / effectiveScale).toInt() + val x = ((getMouseX() - getAbsX()) / effectiveScale).toInt() + val y = ((getMouseY() - getAbsY()) / effectiveScale).toInt() return x to y } @@ -1712,7 +1722,7 @@ object RenderUtils { fun drawRoundTexturedRect(x: Int, y: Int, width: Int, height: Int, filter: Int, radius: Int = 10, smoothness: Int = 1) { // if radius is 0 then just draw a normal textured rect if (radius <= 0) { - Utils.drawTexturedRect(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), filter) + GuiRenderUtils.drawTexturedRect(x, y, width, height, filter = filter) return } @@ -1731,7 +1741,7 @@ object RenderUtils { GlStateManager.pushMatrix() ShaderManager.enableShader(ShaderManager.Shaders.ROUNDED_TEXTURE) - Utils.drawTexturedRect(x.toFloat(), y.toFloat(), width.toFloat(), height.toFloat(), filter) + GuiRenderUtils.drawTexturedRect(x, y, width, height, filter = filter) ShaderManager.disableShader() GlStateManager.popMatrix() @@ -1832,6 +1842,7 @@ object RenderUtils { GlStateManager.popMatrix() } + // todo merge with the one in GuiRenderUtils fun drawGradientRect( left: Int, top: Int, @@ -1871,13 +1882,6 @@ object RenderUtils { GlStateManager.enableTexture2D() } - // TODO move off of neu function - fun drawTexturedRect(x: Float, y: Float) { - with(ScaledResolution(Minecraft.getMinecraft())) { - Utils.drawTexturedRect(x, y, scaledWidth.toFloat(), scaledHeight.toFloat(), GL11.GL_NEAREST) - } - } - fun getAlpha(): Float { colorBuffer.clear() GlStateManager.getFloat(GL11.GL_CURRENT_COLOR, colorBuffer) diff --git a/src/main/java/at/hannibal2/skyhanni/utils/SkyBlockItemModifierUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/SkyBlockItemModifierUtils.kt index 4d0cf446856f..128b5d11e877 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/SkyBlockItemModifierUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/SkyBlockItemModifierUtils.kt @@ -3,6 +3,7 @@ package at.hannibal2.skyhanni.utils import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.data.PetAPI import at.hannibal2.skyhanni.mixins.hooks.ItemStackCachedData +import at.hannibal2.skyhanni.utils.ItemUtils.extraAttributes import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name @@ -272,7 +273,7 @@ object SkyBlockItemModifierUtils { private fun ItemStack.getAttributeByte(label: String) = getExtraAttributes()?.getByte(label) ?: 0 - fun ItemStack.getExtraAttributes() = tagCompound?.getCompoundTag("ExtraAttributes") + fun ItemStack.getExtraAttributes() = tagCompound?.extraAttributes class GemstoneSlot(val type: GemstoneType, val quality: GemstoneQuality) { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt index 750bd96aa662..ed4a5fbe6709 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/JsonUtils.kt @@ -1,9 +1,11 @@ package at.hannibal2.skyhanni.utils.json +import at.hannibal2.skyhanni.config.ConfigManager import com.google.gson.Gson import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject +import java.io.File import java.io.Reader import kotlin.reflect.jvm.javaType import kotlin.reflect.typeOf @@ -15,6 +17,16 @@ inline fun Gson.fromJson(jsonElement: JsonElement): T = inline fun Gson.fromJson(reader: Reader): T = this.fromJson(reader, typeOf().javaType) +fun File.getJson(): JsonObject? { + return try { + this.inputStream().use { + ConfigManager.gson.fromJson(it.reader(), JsonObject::class.java) + } + } catch (e: Exception) { + null + } +} + /** * Straight forward deep copy. This is included in gson as well, but different versions have it exposed privately instead of publicly, * so this reimplementation is here as an always public alternative. @@ -35,3 +47,9 @@ fun JsonElement.shDeepCopy(): JsonElement { else -> this } } + +fun Iterable.toJsonArray(): JsonArray = JsonArray().also { + for (jsonElement in this) { + it.add(jsonElement) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/json/Shimmy.kt b/src/main/java/at/hannibal2/skyhanni/utils/json/Shimmy.kt new file mode 100644 index 000000000000..31022da58d19 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/json/Shimmy.kt @@ -0,0 +1,64 @@ +package at.hannibal2.skyhanni.utils.json + +import at.hannibal2.skyhanni.config.ConfigManager +import com.google.gson.JsonElement +import java.lang.reflect.Field + +// Copied from NEU +class Shimmy private constructor( + val source: Any, + val field: Field, +) { + companion object { + private fun shimmy(source: Any?, fieldName: String): Any? { + if (source == null) return null + return try { + val declaredField = source.javaClass.getDeclaredField(fieldName) + declaredField.isAccessible = true + declaredField.get(source) + } catch (e: NoSuchFieldException) { + null + } + } + + @JvmStatic + fun makeShimmy(source: Any?, path: List): Shimmy? { + if (path.isEmpty()) + return null + var source = source + for (part in path.dropLast(1)) { + source = shimmy(source, part) + } + if (source == null) return null + val lastName = path.last() + return try { + val field = source.javaClass.getDeclaredField(lastName) + field.isAccessible = true + Shimmy( + source, + field, + ) + } catch (e: NoSuchFieldException) { + null + } + } + + } + + val clazz: Class<*> = field.type + fun get(): Any? { + return field.get(source) + } + + fun set(value: Any?) { + field.set(source, value) + } + + fun getJson(): JsonElement { + return ConfigManager.gson.toJsonTree(get()) + } + + fun setJson(element: JsonElement) { + set(ConfigManager.gson.fromJson(element, clazz)) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt index d161cf2b0eeb..f3f704f374a2 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt @@ -14,6 +14,7 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.contains import at.hannibal2.skyhanni.utils.ColorUtils import at.hannibal2.skyhanni.utils.ColorUtils.addAlpha import at.hannibal2.skyhanni.utils.ColorUtils.darker +import at.hannibal2.skyhanni.utils.GuiRenderUtils import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzLogger @@ -29,7 +30,6 @@ import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXAligned import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXYAligned import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderYAligned import at.hannibal2.skyhanni.utils.shader.ShaderManager -import io.github.moulberry.notenoughupdates.util.Utils import io.github.notenoughupdates.moulconfig.gui.GuiScreenElementWrapper import net.minecraft.client.Minecraft import net.minecraft.client.gui.Gui @@ -1272,18 +1272,9 @@ interface Renderable { override fun render(posX: Int, posY: Int) { Minecraft.getMinecraft().textureManager.bindTexture(texture) + GlStateManager.color(1f, 1f, 1f, alpha / 255f) - Utils.drawTexturedRect( - 0f, - 0f, - width.toFloat(), - height.toFloat(), - uMin, - uMax, - vMin, - vMax, - GL11.GL_NEAREST, - ) + GuiRenderUtils.drawTexturedRect(0, 0, width, height, uMin, uMax, vMin, vMax) GlStateManager.color(1f, 1f, 1f, 1f) GlStateManager.translate(padding.toFloat(), padding.toFloat(), 0f) @@ -1312,17 +1303,7 @@ interface Renderable { override fun render(posX: Int, posY: Int) { Minecraft.getMinecraft().textureManager.bindTexture(texture) GlStateManager.color(1f, 1f, 1f, alpha / 255f) - Utils.drawTexturedRect( - 0f, - 0f, - width.toFloat(), - height.toFloat(), - uMin, - uMax, - vMin, - vMax, - GL11.GL_NEAREST, - ) + GuiRenderUtils.drawTexturedRect(0, 0, width, height, uMin, uMax, vMin, vMax) GlStateManager.color(1f, 1f, 1f, 1f) } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt index b37c89bb552d..b6c4225ba275 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/RenderableTooltips.kt @@ -5,7 +5,6 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.RenderUtils import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXAligned -import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.Minecraft import net.minecraft.client.gui.ScaledResolution import net.minecraft.client.renderer.GlStateManager @@ -44,8 +43,8 @@ object RenderableTooltips { val tips = tooltip.tips if (tips.isEmpty()) return - val x = Utils.getMouseX() + 12 - val y = Utils.getMouseY() - if (tips.size > 1) 1 else -7 + val x = RenderUtils.getMouseX() + 12 + val y = RenderUtils.getMouseY() - if (tips.size > 1) 1 else -7 val borderColorStart = tooltip.getBorderColor() val scaled = ScaledResolution(Minecraft.getMinecraft()) val isSpacedTitle = tooltip.isSpacedTitle() From f78d580531941d52bbbeca625c3f078beae8996a Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:55:25 +0200 Subject: [PATCH 080/102] code cleanup --- .../at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt index 97a3f580c555..9feb170c1f08 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/NeuRepositoryReloadEvent.kt @@ -15,9 +15,7 @@ import java.io.File import java.lang.reflect.Type class NeuRepositoryReloadEvent : LorenzEvent() { - fun getConstant(file: String): JsonObject? { - return File(manager.repoLocation, "constants/$file.json").getJson() - } + fun getConstant(file: String): JsonObject? = File(manager.repoLocation, "constants/$file.json").getJson() inline fun readConstant(file: String, gson: Gson = ConfigManager.gson): T { val data = getConstant(file) ?: ErrorManager.skyHanniError("$file failed to load from neu repo!") From 7c9f889efd9c5d889394f8bf00288ef1c38f8ae4 Mon Sep 17 00:00:00 2001 From: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:11:18 +1000 Subject: [PATCH 081/102] Backend: Add Http Request Patching (#2578) --- .../skyhanni/config/commands/Commands.kt | 4 +- .../at/hannibal2/skyhanni/data/MayorAPI.kt | 4 +- .../data/bazaar/HypixelBazaarFetcher.kt | 4 +- .../features/chat/translation/Translator.kt | 4 +- .../features/garden/GardenNextJacobContest.kt | 6 +- .../garden/farming/FarmingWeightDisplay.kt | 8 +-- .../inventory/bazaar/BazaarDataHolder.kt | 4 +- .../mining/eventtracker/MiningEventTracker.kt | 6 +- .../features/misc/update/UpdateManager.kt | 4 +- .../utils/{APIUtil.kt => APIUtils.kt} | 54 ++++++++++++------ src/main/resources/mykeystore.jks | Bin 0 -> 124141 bytes 11 files changed, 59 insertions(+), 39 deletions(-) rename src/main/java/at/hannibal2/skyhanni/utils/{APIUtil.kt => APIUtils.kt} (82%) create mode 100644 src/main/resources/mykeystore.jks 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 cf4679e396d6..a52852a8193f 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -94,7 +94,7 @@ import at.hannibal2.skyhanni.test.command.TestChatCommand import at.hannibal2.skyhanni.test.command.TrackParticlesCommand import at.hannibal2.skyhanni.test.command.TrackSoundsCommand import at.hannibal2.skyhanni.test.graph.GraphEditor -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ExtendedChatColor import at.hannibal2.skyhanni.utils.ItemPriceUtils @@ -382,7 +382,7 @@ object Commands { registerCommand( "shtogglehypixelapierrors", "Show/hide hypixel api error messages in chat", - ) { APIUtil.toggleApiErrorMessages() } + ) { APIUtils.toggleApiErrorMessages() } registerCommand( "shclearcropspeed", "Reset garden crop speed data and best crop time data", diff --git a/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt index b4fb7eaa2dc8..469c48489245 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt @@ -16,7 +16,7 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.features.fame.ReminderUtils import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter import at.hannibal2.skyhanni.utils.CollectionUtils.put @@ -216,7 +216,7 @@ object MayorAPI { SkyHanniMod.coroutineScope.launch { val url = "https://api.hypixel.net/v2/resources/skyblock/election" - val jsonObject = withContext(dispatcher) { APIUtil.getJSONResponse(url) } + val jsonObject = withContext(dispatcher) { APIUtils.getJSONResponse(url) } rawMayorData = ConfigManager.gson.fromJson(jsonObject) val data = rawMayorData ?: return@launch val map = mutableMapOf() diff --git a/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt b/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt index 29a16944c3d3..13044b7c3151 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/bazaar/HypixelBazaarFetcher.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarData import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ItemUtils.itemName import at.hannibal2.skyhanni.utils.LorenzUtils @@ -46,7 +46,7 @@ object HypixelBazaarFetcher { val fetchType = if (nextFetchIsManual) "manual" else "automatic" nextFetchIsManual = false try { - val jsonResponse = withContext(Dispatchers.IO) { APIUtil.getJSONResponse(URL) }.asJsonObject + val jsonResponse = withContext(Dispatchers.IO) { APIUtils.getJSONResponse(URL) }.asJsonObject val response = ConfigManager.gson.fromJson(jsonResponse) if (response.success) { latestProductInformation = process(response.products) diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt index 389b57fb0fc7..24809faac798 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf @@ -112,7 +112,7 @@ object Translator { */ private fun getJSONResponse(urlString: String) = - APIUtil.getJSONResponseAsElement(urlString, false, "Google Translate API") + APIUtils.getJSONResponseAsElement(urlString, false, "Google Translate API") private fun getTranslationToEnglish(message: String): String { val url = diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt index ab8820b40fd6..4285a24af969 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextJacobContest.kt @@ -15,7 +15,7 @@ import at.hannibal2.skyhanni.events.TabListUpdateEvent import at.hannibal2.skyhanni.features.garden.GardenAPI.addCropIcon import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ConfigUtils import at.hannibal2.skyhanni.utils.HypixelCommands @@ -543,7 +543,7 @@ object GardenNextJacobContest { suspend fun fetchUpcomingContests() { try { val url = "https://api.elitebot.dev/contests/at/now" - val result = withContext(dispatcher) { APIUtil.getJSONResponse(url) }.asJsonObject + val result = withContext(dispatcher) { APIUtils.getJSONResponse(url) }.asJsonObject val newContests = mutableMapOf() @@ -613,7 +613,7 @@ object GardenNextJacobContest { val url = "https://api.elitebot.dev/contests/at/now" val body = Gson().toJson(formatted) - val result = withContext(dispatcher) { APIUtil.postJSONIsSuccessful(url, body) } + val result = withContext(dispatcher) { APIUtils.postJSONIsSuccessful(url, body) } if (result) { ChatUtils.chat("Successfully submitted this years upcoming contests, thank you for helping everyone out!") diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt index 222e75f4f296..1fa4b57e6a56 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt @@ -20,7 +20,7 @@ import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getSpeed import at.hannibal2.skyhanni.features.garden.pests.PestType import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.round @@ -447,7 +447,7 @@ object FarmingWeightDisplay { val atRank = if (isEtaEnabled() && goalRank != 10001) "&atRank=$goalRank" else "" val url = "https://api.elitebot.dev/leaderboard/rank/farmingweight/$uuid/$profileId$includeUpcoming$atRank" - val apiResponse = APIUtil.getJSONResponse(url) + val apiResponse = APIUtils.getJSONResponse(url) try { val apiData = toEliteLeaderboardJson(apiResponse).data @@ -477,7 +477,7 @@ object FarmingWeightDisplay { private fun loadWeight(localProfile: String) { val uuid = LorenzUtils.getPlayerUuid() val url = "https://api.elitebot.dev/weight/$uuid" - val apiResponse = APIUtil.getJSONResponse(url) + val apiResponse = APIUtils.getJSONResponse(url) var error: Throwable? = null @@ -573,7 +573,7 @@ object FarmingWeightDisplay { if (attemptingCropWeightFetch || hasFetchedCropWeights) return attemptingCropWeightFetch = true val url = "https://api.elitebot.dev/weights/all" - val apiResponse = APIUtil.getJSONResponse(url) + val apiResponse = APIUtils.getJSONResponse(url) try { val apiData = eliteWeightApiGson.fromJson(apiResponse) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt index 3d7867ad9540..cf9cb6d2ec06 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/bazaar/BazaarDataHolder.kt @@ -5,7 +5,7 @@ import at.hannibal2.skyhanni.config.ConfigManager import at.hannibal2.skyhanni.data.jsonobjects.other.SkyblockItemsDataJson import at.hannibal2.skyhanni.features.rift.RiftAPI import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.json.fromJson @@ -22,7 +22,7 @@ class BazaarDataHolder { private fun loadNpcPrices(): MutableMap { val list = mutableMapOf() - val apiResponse = APIUtil.getJSONResponse("https://api.hypixel.net/v2/resources/skyblock/items") + val apiResponse = APIUtils.getJSONResponse("https://api.hypixel.net/v2/resources/skyblock/items") try { val itemsData = ConfigManager.gson.fromJson(apiResponse) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt index 97806b6fb0ad..a076ba68f0c3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/eventtracker/MiningEventTracker.kt @@ -13,7 +13,7 @@ import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.events.SecondPassedEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager -import at.hannibal2.skyhanni.utils.APIUtil +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland @@ -160,7 +160,7 @@ object MiningEventTracker { private fun sendData(json: String) { val response = try { - APIUtil.postJSON("https://api.soopy.dev/skyblock/chevents/set", json) + APIUtils.postJSON("https://api.soopy.dev/skyblock/chevents/set", json) } catch (e: IOException) { if (LorenzUtils.debug) { ErrorManager.logErrorWithData( @@ -194,7 +194,7 @@ object MiningEventTracker { canRequestAt = SimpleTimeMark.now() + defaultCooldown SkyHanniMod.coroutineScope.launch { val data = try { - APIUtil.getJSONResponse("https://api.soopy.dev/skyblock/chevents/get") + APIUtils.getJSONResponse("https://api.soopy.dev/skyblock/chevents/get") } catch (e: Exception) { apiErrorCount++ canRequestAt = SimpleTimeMark.now() + 20.minutes diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt index 040b6741a847..9948d5eeb973 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/update/UpdateManager.kt @@ -5,12 +5,12 @@ import at.hannibal2.skyhanni.config.features.About.UpdateStream import at.hannibal2.skyhanni.events.ConfigLoadEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle import at.hannibal2.skyhanni.utils.DelayedRun import at.hannibal2.skyhanni.utils.LorenzLogger import com.google.gson.JsonElement -import io.github.moulberry.notenoughupdates.util.ApiUtil import io.github.notenoughupdates.moulconfig.observer.Property import io.github.notenoughupdates.moulconfig.processor.MoulConfigProcessor import moe.nea.libautoupdate.CurrentVersion @@ -161,7 +161,7 @@ object UpdateManager { context.cleanup() UpdateUtils.patchConnection { if (it is HttpsURLConnection) { - ApiUtil.patchHttpsRequest(it) + APIUtils.patchHttpsRequest(it) } } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/APIUtils.kt similarity index 82% rename from src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt rename to src/main/java/at/hannibal2/skyhanni/utils/APIUtils.kt index 483bb6d047ab..d28263af6c7d 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/APIUtil.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/APIUtils.kt @@ -16,17 +16,41 @@ import org.apache.http.impl.client.HttpClientBuilder import org.apache.http.impl.client.HttpClients import org.apache.http.message.BasicHeader import org.apache.http.util.EntityUtils -import java.io.BufferedReader -import java.io.File -import java.io.FileInputStream -import java.io.InputStreamReader -import java.nio.charset.StandardCharsets +import java.security.KeyStore +import javax.net.ssl.HttpsURLConnection +import javax.net.ssl.KeyManagerFactory +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManagerFactory -object APIUtil { +object APIUtils { private val parser = JsonParser() private var showApiErrors = false + private val ctx: SSLContext? = run { + try { + val myKeyStore = KeyStore.getInstance("JKS") + myKeyStore.load(APIUtils.javaClass.getResourceAsStream("/keystore.jks"), "changeit".toCharArray()) + val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()) + val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + kmf.init(myKeyStore, null) + tmf.init(myKeyStore) + SSLContext.getInstance("TLS").apply { + init(kmf.keyManagers, tmf.trustManagers, null) + } + } catch (e: Exception) { + println("Failed to load keystore. A lot of API requests won't work") + e.printStackTrace() + null + } + } + + fun patchHttpsRequest(connection: HttpsURLConnection) { + ctx?.let { + connection.sslSocketFactory = it.socketFactory + } + } + data class ApiResponse(val success: Boolean, val message: String?, val data: JsonObject) private val builder: HttpClientBuilder = @@ -34,19 +58,19 @@ object APIUtil { .setDefaultHeaders( mutableListOf( BasicHeader("Pragma", "no-cache"), - BasicHeader("Cache-Control", "no-cache") - ) + BasicHeader("Cache-Control", "no-cache"), + ), ) .setDefaultRequestConfig( RequestConfig.custom() - .build() + .build(), ) .useSystemProperties() /** * TODO * make suspend - * use withContext(Dispatchers.IO) { APIUtil.getJSONResponse(url) }.asJsonObject + * use withContext(Dispatchers.IO) { APIUtils.getJSONResponse(url) }.asJsonObject */ fun getJSONResponse(urlString: String, silentError: Boolean = false) = getJSONResponseAsElement(urlString, silentError) as JsonObject @@ -76,7 +100,7 @@ object APIUtil { ChatUtils.clickableChat( "Problems with detecting the Hypixel API. §eClick here to hide this message for now.", onClick = { toggleApiErrorMessages() }, - "§eClick to run /shtogglehypixelapierrors!" + "§eClick to run /shtogglehypixelapierrors!", ) } ErrorManager.skyHanniError( @@ -84,7 +108,7 @@ object APIUtil { "error message" to "$message(502 Bad Gateway)", "apiName" to apiName, "urlString" to urlString, - "returnedData" to retSrc + "returnedData" to retSrc, ) } else { ErrorManager.skyHanniError( @@ -92,7 +116,7 @@ object APIUtil { "error message" to message, "apiName" to apiName, "urlString" to urlString, - "returnedData" to retSrc + "returnedData" to retSrc, ) } } @@ -181,10 +205,6 @@ object APIUtil { return false } - fun readFile(file: File): BufferedReader { - return BufferedReader(InputStreamReader(FileInputStream(file), StandardCharsets.UTF_8)) - } - // TODO remove command, use clickable chat message instead fun toggleApiErrorMessages() { showApiErrors = !showApiErrors diff --git a/src/main/resources/mykeystore.jks b/src/main/resources/mykeystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..75039b79a01089ca19b91d4cb2bc4538236f8157 GIT binary patch literal 124141 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

-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 Date: Mon, 23 Sep 2024 16:47:07 +0200 Subject: [PATCH 082/102] Feature: Reindrake Warp Helper (#2569) --- .../features/event/winter/WinterConfig.java | 6 +++ .../event/winter/ReindrakeWarpHelper.kt | 42 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/event/winter/ReindrakeWarpHelper.kt diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/winter/WinterConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/winter/WinterConfig.java index 837f4c7ea811..550ceaeee0ec 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/event/winter/WinterConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/winter/WinterConfig.java @@ -46,4 +46,10 @@ public class WinterConfig { @FeatureToggle public boolean newYearCakeReminder = true; + @Expose + @ConfigOption(name = "Reindrake Warp Helper", desc = "Sends a clickable message in chat to warp to the Winter Island spawn when a Reindrake spawns.") + @ConfigEditorBoolean + @FeatureToggle + public boolean reindrakeWarpHelper = true; + } diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/winter/ReindrakeWarpHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/event/winter/ReindrakeWarpHelper.kt new file mode 100644 index 000000000000..7131426d42b3 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/event/winter/ReindrakeWarpHelper.kt @@ -0,0 +1,42 @@ +package at.hannibal2.skyhanni.features.event.winter + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.HypixelCommands +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +@SkyHanniModule +object ReindrakeWarpHelper { + + private val config get() = SkyHanniMod.feature.event.winter + + private val patternGroup = RepoPattern.group("event.winter.reindrakewarphelper") + + /** + * REGEX-TEST: §c§lWOAH! §cA §4Reindrake §cwas summoned from the depths! + */ + private val spawnPattern by patternGroup.pattern( + "spawn.message", + "§c§lWOAH! §cA §4Reindrake §cwas summoned from the depths!", + ) + + @SubscribeEvent + fun onMessage(event: LorenzChatEvent) { + if (!isEnabled()) return + if (!spawnPattern.matches(event.message)) return + ChatUtils.clickToActionOrDisable( + "A Reindrake was detected. Click to warp to the Winter Island spawn!", + config::reindrakeWarpHelper, + actionName = "warp to winter island spawn", + action = { HypixelCommands.warp("winter") } + ) + } + + fun isEnabled() = IslandType.WINTER.isInIsland() && config.reindrakeWarpHelper +} From 2eed7efac2dab7ec54043d188d814b17052cebdd Mon Sep 17 00:00:00 2001 From: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Date: Tue, 24 Sep 2024 01:16:31 +1000 Subject: [PATCH 083/102] Backend: Replace round and roundToPrecision with roundTo (#1931) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../core/config/gui/GuiPositionEditor.kt | 5 ++-- .../at/hannibal2/skyhanni/data/QuiverAPI.kt | 4 +-- .../at/hannibal2/skyhanni/data/model/Graph.kt | 4 +-- .../skyhanni/events/PlaySoundEvent.kt | 6 ++--- .../skyhanni/events/ReceiveParticleEvent.kt | 4 +-- .../skyhanni/features/combat/BestiaryData.kt | 4 +-- .../damageindicator/DamageIndicatorManager.kt | 4 +-- .../combat/ghostcounter/GhostCounter.kt | 4 +-- .../features/combat/ghostcounter/GhostUtil.kt | 8 +++--- .../features/event/diana/BurrowWarpHelper.kt | 8 +++--- .../event/hoppity/HoppityCollectionStats.kt | 4 +-- .../event/hoppity/HoppityEggLocator.kt | 4 +-- .../features/garden/FarmingFortuneDisplay.kt | 4 +-- .../features/garden/GardenYawAndPitch.kt | 6 ++--- .../garden/composter/ComposterOverlay.kt | 6 ++--- .../garden/contest/FarmingPersonalBestGain.kt | 4 +-- .../contest/JacobContestFFNeededDisplay.kt | 4 +-- .../contest/JacobContestStatsSummary.kt | 4 +-- .../garden/contest/JacobContestTimeNeeded.kt | 6 ++--- .../features/garden/farming/CropSpeedMeter.kt | 6 ++--- .../garden/farming/FarmingWeightDisplay.kt | 6 ++--- .../farming/GardenCropMilestoneDisplay.kt | 6 ++--- .../farming/lane/FarmingLaneFeatures.kt | 6 ++--- .../inventory/GardenCropMilestoneInventory.kt | 4 +-- .../garden/inventory/SkyMartCopperPrice.kt | 4 +-- .../ChocolateFactoryDataLoader.kt | 8 +++--- .../ChocolateFactoryTooltip.kt | 4 +-- .../abilitycooldown/ItemAbility.kt | 4 +-- .../abilitycooldown/ItemAbilityCooldown.kt | 4 +-- .../features/mining/MineshaftPityDisplay.kt | 6 ++--- .../features/mining/PowderPerHotmPerk.kt | 4 +-- .../solver/FossilSolverDisplay.kt | 4 +-- .../skyhanni/features/misc/IslandAreas.kt | 4 +-- .../features/misc/MovementSpeedDisplay.kt | 4 +-- .../skyhanni/features/misc/TpsCounter.kt | 4 +-- .../features/misc/UserLuckBreakdown.kt | 8 +++--- .../features/misc/limbo/LimboPlaytime.kt | 4 +-- .../features/misc/limbo/LimboTimeTracker.kt | 10 +++---- .../features/misc/pets/PetExpTooltip.kt | 6 ++--- .../livingcave/LivingMetalSuitProgress.kt | 4 +-- .../rift/everywhere/CruxTalismanDisplay.kt | 4 +-- .../rift/everywhere/motes/RiftMotesOrb.kt | 4 +-- .../features/skillprogress/SkillProgress.kt | 4 +-- .../features/skillprogress/SkillTooltip.kt | 6 ++--- .../at/hannibal2/skyhanni/test/PacketTest.kt | 4 +-- .../skyhanni/test/SkyHanniDebugsAndTests.kt | 11 ++++---- .../test/command/TrackParticlesCommand.kt | 4 +-- .../test/command/TrackSoundsCommand.kt | 4 +-- .../skyhanni/utils/GuiRenderUtils.kt | 4 +-- .../hannibal2/skyhanni/utils/LorenzUtils.kt | 21 ++++----------- .../at/hannibal2/skyhanni/utils/LorenzVec.kt | 10 +++---- .../at/hannibal2/skyhanni/utils/NumberUtil.kt | 26 +++++++++---------- 52 files changed, 145 insertions(+), 160 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt b/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt index 3e64d5f5abff..ba3a576754dc 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt @@ -27,11 +27,10 @@ import at.hannibal2.skyhanni.data.OtherInventoryData import at.hannibal2.skyhanni.mixins.transformers.gui.AccessorGuiContainer import at.hannibal2.skyhanni.utils.GuiRenderUtils import at.hannibal2.skyhanni.utils.KeyboardManager -import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.compat.GuiScreenUtils import at.hannibal2.skyhanni.utils.compat.SkyhanniBaseScreen import net.minecraft.client.Minecraft -import net.minecraft.client.gui.ScaledResolution import net.minecraft.client.gui.inventory.GuiContainer import net.minecraft.client.renderer.GlStateManager import org.lwjgl.input.Keyboard @@ -100,7 +99,7 @@ class GuiPositionEditor( } val pos = positions[displayPos] - val location = "§7x: §e${pos.rawX}§7, y: §e${pos.rawY}§7, scale: §e${pos.scale.round(2)}" + val location = "§7x: §e${pos.rawX}§7, y: §e${pos.rawY}§7, scale: §e${pos.scale.roundTo(2)}" GuiRenderUtils.drawStringCentered("§b" + pos.internalName, getScaledWidth() / 2, 18) GuiRenderUtils.drawStringCentered(location, getScaledWidth() / 2, 28) if (pos.canJumpToConfigOptions()) diff --git a/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt index 88daf40b9520..2f516610ca51 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/QuiverAPI.kt @@ -16,10 +16,10 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull import at.hannibal2.skyhanni.utils.ItemUtils.getLore 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.formatInt +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getExtraAttributes @@ -236,7 +236,7 @@ object QuiverAPI { } } - fun Int.asArrowPercentage() = ((this.toFloat() / MAX_ARROW_AMOUNT) * 100).round(1) + fun Int.asArrowPercentage() = ((this.toFloat() / MAX_ARROW_AMOUNT) * 100).roundTo(1) fun hasBowInInventory() = hasBow diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt index b9ad1251ca8c..bc4be57a16d4 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt @@ -1,7 +1,7 @@ package at.hannibal2.skyhanni.data.model -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.json.SkyHanniTypeAdapters.registerTypeAdapter import at.hannibal2.skyhanni.utils.json.fromJson import com.google.gson.GsonBuilder @@ -63,7 +63,7 @@ value class Graph( out.beginObject() for ((node, weight) in it.neighbours) { val id = node.id.toString() - out.name(id).value(weight.round(2)) + out.name(id).value(weight.roundTo(2)) } out.endObject() diff --git a/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt index 2f793ff28860..386167123768 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt @@ -1,8 +1,8 @@ package at.hannibal2.skyhanni.events import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import net.minecraftforge.fml.common.eventhandler.Cancelable @Cancelable @@ -12,9 +12,7 @@ class PlaySoundEvent(val soundName: String, val location: LorenzVec, val pitch: val distanceToPlayer by lazy { location.distanceToPlayer() } override fun toString(): String { return "PlaySoundEvent(soundName='$soundName', pitch=$pitch, volume=$volume, location=${location.round(1)}, distanceToPlayer=${ - distanceToPlayer.round( - 1 - ) + distanceToPlayer.roundTo(1) })" } } diff --git a/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt index ef296326da84..7c695c7eec2f 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt @@ -1,8 +1,8 @@ package at.hannibal2.skyhanni.events import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import net.minecraft.util.EnumParticleTypes import net.minecraftforge.fml.common.eventhandler.Cancelable @@ -26,7 +26,7 @@ class ReceiveParticleEvent( 1 ) }, longDistance=$longDistance, particleArgs=${particleArgs.contentToString()}, distanceToPlayer=${ - distanceToPlayer.round( + distanceToPlayer.roundTo( 1 ) })" diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt index 5d056f456818..957f1ecd0b29 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt @@ -20,7 +20,7 @@ import at.hannibal2.skyhanni.utils.LorenzUtils.addButton import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatLong import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary -import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.NumberUtil.toRoman import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher @@ -327,7 +327,7 @@ object BestiaryData { DisplayTypeEntry.GLOBAL_NEXT -> mob.killNeededForNextLevel else -> 0 } - val percentage = ((currentKill.toDouble() / killNeeded) * 100).roundToPrecision(2) + val percentage = ((currentKill.toDouble() / killNeeded) * 100).roundTo(2) val suffix = if (type == DisplayTypeEntry.GLOBAL_NEXT) "§ato level ${mob.getNextLevel()}" else "" "§7(§b${currentKill.formatNumber()}§7/§b${killNeeded.formatNumber()}§7) §a$percentage§6% $suffix" } diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt index b7af2d72b2d4..0391e40a3697 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt @@ -34,10 +34,10 @@ import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.baseMaxHealth -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.NumberUtil import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText @@ -718,7 +718,7 @@ object DamageIndicatorManager { if (existed > 40) { val end = (20 * 26) - existed val time = end.toDouble() / 20 - entityData.nameAbove = "Mania Circles: §b${time.round(1)}s" + entityData.nameAbove = "Mania Circles: §b${time.roundTo(1)}s" return "" } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt index 85d5778fe25b..b337d58b1487 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt @@ -48,7 +48,7 @@ import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble import at.hannibal2.skyhanni.utils.NumberUtil.formatLong import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal -import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems @@ -154,7 +154,7 @@ object GhostCounter { 0.0 -> "0" else -> { val mf = (((storage?.totalMF!! / Option.TOTALDROPS.get()) + Math.ulp(1.0)) * 100) / 100 - mf.roundToPrecision(2).toString() + mf.roundTo(2).toString() } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt index d568fc49288f..36a4edfb3cd4 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.data.ProfileStorageData import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators -import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import java.io.FileReader @@ -106,8 +106,8 @@ object GhostUtil { ) } - fun String.formatText(value: Double, session: Double) = this.replace("%value%", value.roundToPrecision(2).addSeparators()) - .replace("%session%", session.roundToPrecision(2).addSeparators()) + fun String.formatText(value: Double, session: Double) = this.replace("%value%", value.roundTo(2).addSeparators()) + .replace("%session%", session.roundTo(2).addSeparators()) .replace("&", "§") fun String.formatBestiary(currentKill: Int, killNeeded: Int): String { @@ -129,5 +129,5 @@ object GhostUtil { } private fun percent(number: Double) = - 100.0.coerceAtMost(((number / 100_000) * 100).roundToPrecision(4)).toString() + 100.0.coerceAtMost(((number / 100_000) * 100).roundTo(4)).toString() } diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt index 34dab01e6ce1..4074b465de33 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt @@ -12,8 +12,8 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.sorted import at.hannibal2.skyhanni.utils.HypixelCommands import at.hannibal2.skyhanni.utils.LocationUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.SimpleTimeMark import net.minecraft.client.Minecraft import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -104,11 +104,11 @@ object BurrowWarpHelper { debug?.add("warpPoint: ${warpPoint.displayName}") val playerDistance = playerLocation.distance(target) - debug?.add("playerDistance: ${playerDistance.round(1)}") + debug?.add("playerDistance: ${playerDistance.roundTo(1)}") val warpDistance = warpPoint.distance(target) - debug?.add("warpDistance: ${warpDistance.round(1)}") + debug?.add("warpDistance: ${warpDistance.roundTo(1)}") val difference = playerDistance - warpDistance - debug?.add("difference: ${difference.round(1)}") + debug?.add("difference: ${difference.roundTo(1)}") val setWarpPoint = difference > 10 debug?.add("setWarpPoint: $setWarpPoint") currentWarp = if (setWarpPoint) warpPoint else null diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt index 7be0913e9fb2..e84d93dfa6a3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt @@ -22,11 +22,11 @@ import at.hannibal2.skyhanni.utils.KSerializable import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzRarity 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.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatInt +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.RegexUtils.anyMatches import at.hannibal2.skyhanni.utils.RegexUtils.findMatcher @@ -435,7 +435,7 @@ object HoppityCollectionStats { add("§7Total Rabbits Found: §a${displayFound + displayDuplicates}") add("") add("§7Chocolate Per Second: §a${displayChocolatePerSecond.addSeparators()}") - add("§7Chocolate Multiplier: §a${displayChocolateMultiplier.round(3)}") + add("§7Chocolate Multiplier: §a${displayChocolateMultiplier.roundTo(3)}") } table.add( DisplayTableEntry( diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt index 8e9e68e16731..468fecbcb480 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt @@ -19,10 +19,10 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NumberUtil.formatInt +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RecalculatingValue import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine import at.hannibal2.skyhanni.utils.RenderUtils.drawColor @@ -249,7 +249,7 @@ object HoppityEggLocator { }.sortedBy { it.second } eggLocationWeights = sortedEggs.map { - it.second.round(3) + it.second.roundTo(3) }.take(5) val filteredEggs = sortedEggs.filter { diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt index 29b92f24afd8..55898266e440 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/FarmingFortuneDisplay.kt @@ -17,9 +17,9 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.nextAfter import at.hannibal2.skyhanni.utils.HypixelCommands import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getLore -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables @@ -167,7 +167,7 @@ object FarmingFortuneDisplay { list.add( Renderable.string( "§6Farming Fortune§7: §e" + if (!recentlySwitchedTool && farmingFortune != -1.0) { - farmingFortune.round(0).addSeparators() + farmingFortune.roundTo(0).addSeparators() } else "§7" + (displayCrop.getLatestTrueFarmingFortune()?.addSeparators() ?: "?"), ), ) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt index cdbb07963d8d..ad03a7784cf3 100755 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt @@ -7,7 +7,7 @@ import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.LocationUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings import at.hannibal2.skyhanni.utils.SimpleTimeMark import net.minecraft.client.Minecraft @@ -41,8 +41,8 @@ object GardenYawAndPitch { if (!config.showAlways && lastChange.passedSince() > config.timeout.seconds) return - val yawText = yaw.round(config.yawPrecision).toBigDecimal().toPlainString() - val pitchText = pitch.round(config.pitchPrecision).toBigDecimal().toPlainString() + val yawText = yaw.roundTo(config.yawPrecision).toBigDecimal().toPlainString() + val pitchText = pitch.roundTo(config.pitchPrecision).toBigDecimal().toPlainString() val displayList = listOf( "§aYaw: §f$yawText", "§aPitch: §f$pitchText", diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt index aee2a93f428f..fa45e579dfa3 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterOverlay.kt @@ -31,7 +31,6 @@ import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.KeyboardManager import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.NONE import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName @@ -41,6 +40,7 @@ import at.hannibal2.skyhanni.utils.NEUItems.getPrice import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.SimpleTimeMark @@ -338,10 +338,10 @@ object ComposterOverlay { val multiplier = multiDropFactor * timeMultiplier val multiplierPreview = multiDropFactorPreview * timeMultiplierPreview val compostPerTitlePreview = - if (multiplier != multiplierPreview) " §c➜ §e" + multiplierPreview.round(2) else "" + if (multiplier != multiplierPreview) " §c➜ §e" + multiplierPreview.roundTo(2) else "" val compostPerTitle = if (currentTimeType == TimeType.COMPOST) "Compost multiplier" else "Composts per $timeText" - newList.addAsSingletonList(" §7$compostPerTitle: §e${multiplier.round(2)}$compostPerTitlePreview") + newList.addAsSingletonList(" §7$compostPerTitle: §e${multiplier.roundTo(2)}$compostPerTitlePreview") val organicMatterPrice = getPrice(organicMatterItem) val organicMatterFactor = organicMatterFactors[organicMatterItem]!! diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingPersonalBestGain.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingPersonalBestGain.kt index 7d009629b7c9..a2e8a859f703 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingPersonalBestGain.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingPersonalBestGain.kt @@ -6,8 +6,8 @@ import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.DelayedRun -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -88,7 +88,7 @@ object FarmingPersonalBestGain { val oldFF = oldCollected / collectionPerFF val ffDiff = newFF - oldFF - ChatUtils.chat("This is §6${ffDiff.round(2)}☘ $crop Fortune §emore than previously!") + ChatUtils.chat("This is §6${ffDiff.roundTo(2)}☘ $crop Fortune §emore than previously!") } fun isEnabled() = GardenAPI.inGarden() && config.contestPersonalBestIncreaseFF diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt index 963193c4ba74..5d9b7f92f34e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt @@ -12,8 +12,8 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.SimpleTimeMark import net.minecraft.item.ItemStack @@ -81,7 +81,7 @@ object JacobContestFFNeededDisplay { addAsSingletonList("§cassuming 19.9 instead.") } else { if (blocksPerSecond < 15.0) { - add(listOf("§7Your latest ", crop.icon, "§7blocks/second: §e${blocksPerSecond.round(2)}")) + add(listOf("§7Your latest ", crop.icon, "§7blocks/second: §e${blocksPerSecond.roundTo(2)}")) add(listOf("§cThis is too low, showing 19.9 Blocks/second instead!")) blocksPerSecond = 19.9 } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestStatsSummary.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestStatsSummary.kt index 3d4fef9a3c66..144abc0c2486 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestStatsSummary.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestStatsSummary.kt @@ -6,8 +6,8 @@ import at.hannibal2.skyhanni.events.FarmingContestEvent import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.TimeUtils.format import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -41,7 +41,7 @@ object JacobContestStatsSummary { FarmingContestPhase.STOP -> { val duration = startTime.passedSince() - val blocksPerSecond = (blocksBroken.toDouble() / duration.inWholeSeconds).round(2) + val blocksPerSecond = (blocksBroken.toDouble() / duration.inWholeSeconds).roundTo(2) val cropName = event.crop.cropName ChatUtils.chat("Stats for $cropName Contest:") val time = duration.format() diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt index 3e99c2683fd2..bb9f9ea21773 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt @@ -11,8 +11,8 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList import at.hannibal2.skyhanni.utils.CollectionUtils.sorted import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.TimeUtils.format import at.hannibal2.skyhanni.utils.renderables.Renderable @@ -102,7 +102,7 @@ object JacobContestTimeNeeded { return } - val speed = (ff * crop.baseDrops * bps / 100).round(1).toInt() + val speed = (ff * crop.baseDrops * bps / 100).roundTo(1).toInt() renderCrop(speed, crop, averages, sorted, map) } @@ -174,7 +174,7 @@ object JacobContestTimeNeeded { add("") val latestFF = crop.getLatestTrueFarmingFortune() ?: 0.0 add("§7Latest FF: §e${(latestFF).addSeparators()}") - val bps = crop.getBps()?.round(1) ?: 0 + val bps = crop.getBps()?.roundTo(1) ?: 0 add("§7${addBpsTitle()}§e${bps.addSeparators()}") addAll(lowBPSWarning) }) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/CropSpeedMeter.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/CropSpeedMeter.kt index aa977b65a575..74c59674f580 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/CropSpeedMeter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/CropSpeedMeter.kt @@ -9,8 +9,8 @@ import at.hannibal2.skyhanni.features.garden.CropType import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -91,7 +91,7 @@ object CropSpeedMeter { currentCrop?.let { val crops = it.getCounter() - startCrops[it]!! val blocks = currentBlocks - val cropsPerBlocks = (crops.toDouble() / blocks.toDouble()).round(3) + val cropsPerBlocks = (crops.toDouble() / blocks.toDouble()).roundTo(3) val list = mutableListOf() list.add("") @@ -101,7 +101,7 @@ object CropSpeedMeter { list.add(" §7Crops per Block: " + cropsPerBlocks.addSeparators()) val baseDrops = it.baseDrops - val farmingFortune = (cropsPerBlocks * 100 / baseDrops).round(3) + val farmingFortune = (cropsPerBlocks * 100 / baseDrops).roundTo(3) list.add(" §7Calculated farming Fortune: §e" + farmingFortune.addSeparators()) list.add("§cOpen /cropmilestones again to recalculate!") diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt index 1fa4b57e6a56..da461d749604 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt @@ -23,8 +23,8 @@ import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.APIUtils import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.SimpleTimeMark @@ -234,7 +234,7 @@ object FarmingWeightDisplay { } val totalWeight = (localWeight + weight) - return "§e" + totalWeight.round(2).addSeparators() + return "§e" + totalWeight.roundTo(2).addSeparators() } private fun getRankGoal(): Int { @@ -326,7 +326,7 @@ object FarmingWeightDisplay { " §7(§b$format§7)" } else "" - val weightFormat = weightUntilOvertake.round(2).addSeparators() + val weightFormat = weightUntilOvertake.roundTo(2).addSeparators() val text = "§e$weightFormat$timeFormat §7behind §b$nextName" return if (showRankGoal) { Renderable.string(text) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt index c37eb41b7cdf..7189199a9987 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt @@ -26,8 +26,8 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.addString import at.hannibal2.skyhanni.utils.ConditionalUtils import at.hannibal2.skyhanni.utils.ConfigUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.SimpleTimeMark @@ -187,7 +187,7 @@ object GardenCropMilestoneDisplay { val farmingFortune = FarmingFortuneDisplay.getCurrentFarmingFortune() val speed = GardenCropSpeed.averageBlocksPerSecond - val farmingFortuneSpeed = ((100.0 + farmingFortune) * crop.baseDrops * speed / 100).round(1).toInt() + val farmingFortuneSpeed = ((100.0 + farmingFortune) * crop.baseDrops * speed / 100).roundTo(1).toInt() if (farmingFortuneSpeed > 0) { crop.setSpeed(farmingFortuneSpeed) @@ -219,7 +219,7 @@ object GardenCropMilestoneDisplay { val hourFormat = (farmingFortuneSpeed * 60 * 60).addSeparators() lineMap[MilestoneTextEntry.CROPS_PER_HOUR] = Renderable.string("§7Crops/Hour§8: §e$hourFormat") - val formatBps = speed.round(config.blocksBrokenPrecision).addSeparators() + val formatBps = speed.roundTo(config.blocksBrokenPrecision).addSeparators() lineMap[MilestoneTextEntry.BLOCKS_PER_SECOND] = Renderable.string("§7Blocks/Second§8: §e$formatBps") } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/lane/FarmingLaneFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/lane/FarmingLaneFeatures.kt index cb63d40de1d8..e407d0ad4deb 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/lane/FarmingLaneFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/lane/FarmingLaneFeatures.kt @@ -14,8 +14,8 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.LocationUtils import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings @@ -76,7 +76,7 @@ object FarmingLaneFeatures { if (config.distanceDisplay) { display = buildList { - add("§7Distance until switch: §e${currentDistance.round(1)}") + add("§7Distance until switch: §e${currentDistance.roundTo(1)}") val normal = movementState == MovementState.NORMAL val color = if (normal) "§b" else "§8" @@ -152,7 +152,7 @@ object FarmingLaneFeatures { private var sameSpeedCounter = 0 private fun calculateSpeed(): Boolean { - val speed = MovementSpeedDisplay.speed.round(2) + val speed = MovementSpeedDisplay.speed.roundTo(2) movementState = calculateMovementState(speed) if (movementState != MovementState.NORMAL) return false diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/GardenCropMilestoneInventory.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/GardenCropMilestoneInventory.kt index 7a2f8d415584..e0cb070739b9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/GardenCropMilestoneInventory.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/GardenCropMilestoneInventory.kt @@ -12,8 +12,8 @@ import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.indexOfFirst import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.StringUtils import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -35,7 +35,7 @@ object GardenCropMilestoneInventory { val tier = GardenCropMilestones.getTierForCropCount(counter, cropType, allowOverflow) tiers.add(tier.toDouble()) } - average = (tiers.sum() / CropType.entries.size).round(2) + average = (tiers.sum() / CropType.entries.size).roundTo(2) } @SubscribeEvent diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/SkyMartCopperPrice.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/SkyMartCopperPrice.kt index 5ef15090739a..078ef16724d2 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/SkyMartCopperPrice.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/inventory/SkyMartCopperPrice.kt @@ -12,12 +12,12 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.itemName import at.hannibal2.skyhanni.utils.ItemUtils.loreCosts import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUItems.getPrice import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatInt import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.renderables.Renderable @@ -115,7 +115,7 @@ object SkyMartCopperPrice { event.move(3, "garden.skyMartCopperPriceAdvancedStats", "garden.skyMart.copperPriceAdvancedStats") event.move(3, "garden.skyMartCopperPricePos", "garden.skyMart.copperPricePos") event.transform(32, "garden.skyMart.itemScale") { - JsonPrimitive((it.asDouble / 1.851).round(1)) + JsonPrimitive((it.asDouble / 1.851).roundTo(1)) } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryDataLoader.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryDataLoader.kt index 3724dbc53576..eebbb19e93a7 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryDataLoader.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryDataLoader.kt @@ -14,11 +14,11 @@ import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.getSkullTexture import at.hannibal2.skyhanni.utils.ItemUtils.name -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble import at.hannibal2.skyhanni.utils.NumberUtil.formatInt import at.hannibal2.skyhanni.utils.NumberUtil.formatLong import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchFirst import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches @@ -334,7 +334,7 @@ object ChocolateFactoryDataLoader { val itemName = item.name.removeColor() val lore = item.getLore() val upgradeCost = ChocolateFactoryAPI.getChocolateBuyCost(lore) - val averageChocolate = ChocolateAmount.averageChocPerSecond().round(2) + val averageChocolate = ChocolateAmount.averageChocPerSecond().roundTo(2) val isMaxed = upgradeCost == null if (slotIndex in ChocolateFactoryAPI.rabbitSlots) { @@ -415,8 +415,8 @@ object ChocolateFactoryDataLoader { newAverageChocolate: Double, isRabbit: Boolean, ) { - val extra = (newAverageChocolate - averageChocolate).round(2) - val effectiveCost = (upgradeCost!! / extra).round(2) + val extra = (newAverageChocolate - averageChocolate).roundTo(2) + val effectiveCost = (upgradeCost!! / extra).roundTo(2) val upgrade = ChocolateFactoryUpgrade(slotIndex, level, upgradeCost, extra, effectiveCost, isRabbit = isRabbit) list.add(upgrade) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryTooltip.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryTooltip.kt index 8973bc1e4fbe..88ac9d4747ae 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryTooltip.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/chocolatefactory/ChocolateFactoryTooltip.kt @@ -3,8 +3,8 @@ package at.hannibal2.skyhanni.features.inventory.chocolatefactory import at.hannibal2.skyhanni.events.LorenzToolTipEvent import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI.profileStorage import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import net.minecraftforge.fml.common.eventhandler.EventPriority import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -35,7 +35,7 @@ object ChocolateFactoryTooltip { if (upgradeInfo.effectiveCost == null) return - event.toolTip.add("§7Extra: §6${upgradeInfo.extraPerSecond?.round(2) ?: "N/A"} §7choc/s") + event.toolTip.add("§7Extra: §6${upgradeInfo.extraPerSecond?.roundTo(2) ?: "N/A"} §7choc/s") event.toolTip.add("§7Effective Cost: §6${upgradeInfo.effectiveCost.addSeparators() ?: "N/A"}") if (slotIndex == ChocolateFactoryAPI.timeTowerIndex) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt index de7bc8bdf77f..020feba76976 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbility.kt @@ -2,10 +2,10 @@ package at.hannibal2.skyhanni.features.itemabilities.abilitycooldown import at.hannibal2.skyhanni.features.dungeon.DungeonAPI import at.hannibal2.skyhanni.utils.LorenzColor -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.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import kotlin.math.floor enum class ItemAbility( @@ -106,7 +106,7 @@ enum class ItemAbility( duration /= 100 var d = duration.toDouble() d /= 10.0 - d.round(1).addSeparators() + d.roundTo(1).addSeparators() } else { duration /= 1000 duration++ diff --git a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt index 567adb251af5..5748220c5e38 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/itemabilities/abilitycooldown/ItemAbilityCooldown.kt @@ -23,8 +23,8 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.between -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getAbilityScrolls @@ -168,7 +168,7 @@ object ItemAbilityCooldown { ItemAbility.STAFF_OF_THE_VOLCANO.sound() } // Holy Ice - event.soundName == "random.drink" && event.pitch.round(1) == 1.8f && event.volume == 1.0f -> { + event.soundName == "random.drink" && event.pitch.roundTo(1) == 1.8f && event.volume == 1.0f -> { ItemAbility.HOLY_ICE.sound() } // Royal Pigeon diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt index ec9a390edff0..4415b59f5872 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/MineshaftPityDisplay.kt @@ -17,8 +17,8 @@ import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay.PityBlock.Comp import at.hannibal2.skyhanni.features.mining.OreType.Companion.getOreType import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.SimpleTimeMark @@ -118,7 +118,7 @@ object MineshaftPityDisplay { add("§7Pity Counter: §e$pityCounter") add( "§7Chance: " + - "§e1§6/§e${chance.round(1)} " + + "§e1§6/§e${chance.roundTo(1)} " + "§7(§b${((1.0 / chance) * 100).addSeparators()}%§7)", ) minedBlocks.forEach { @@ -214,7 +214,7 @@ object MineshaftPityDisplay { MineshaftPityLine.COUNTER to Renderable.string("§3Pity Counter: §e$counterUntilPity§6/§e$MAX_COUNTER"), MineshaftPityLine.CHANCE to Renderable.string( "§3Chance: §e1§6/§e${ - chance.round(1).addSeparators() + chance.roundTo(1).addSeparators() } §7(§b${((1.0 / chance) * 100).addSeparators()}%§7)", ), MineshaftPityLine.NEEDED_TO_PITY to neededToPityRenderable, diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/PowderPerHotmPerk.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/PowderPerHotmPerk.kt index 58fa83753fe9..e268beb16f94 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/PowderPerHotmPerk.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/PowderPerHotmPerk.kt @@ -6,9 +6,9 @@ import at.hannibal2.skyhanni.events.LorenzToolTipEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.fractionOf +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.StringUtils.removeColor import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -48,7 +48,7 @@ object PowderPerHotmPerk { private fun handlePowderSpend(perk: HotmData): String { val currentPowderSpend = perk.calculateTotalCost(perk.rawLevel) val maxPowderNeeded = perk.totalCostMaxLevel - val percentage = (currentPowderSpend.fractionOf(maxPowderNeeded) * 100).round(2) + val percentage = (currentPowderSpend.fractionOf(maxPowderNeeded) * 100).roundTo(2) return when (config.powderSpentDesign) { PowderSpentDesign.NUMBER -> { diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/solver/FossilSolverDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/solver/FossilSolverDisplay.kt index 82fc73030a45..cb50e3c85459 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/solver/FossilSolverDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/solver/FossilSolverDisplay.kt @@ -17,7 +17,7 @@ import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland -import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.RenderUtils.renderString @@ -218,7 +218,7 @@ object FossilSolverDisplay { } fun nextData(slotToClick: FossilTile, correctPercentage: Double, fossilsRemaining: Int) { - val formattedPercentage = (correctPercentage * 100).round(1) + val formattedPercentage = (correctPercentage * 100).roundTo(1) possibleFossilsRemaining = fossilsRemaining FossilSolverDisplay.slotToClick = slotToClick.toSlotIndex() diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt index d5e57d513380..3c1e844a628a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/IslandAreas.kt @@ -23,7 +23,7 @@ import at.hannibal2.skyhanni.utils.LocationUtils.canBeSeen import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable import at.hannibal2.skyhanni.utils.renderables.Renderable @@ -164,7 +164,7 @@ object IslandAreas { } } - val distance = difference.round(1) + val distance = difference.roundTo(1) val text = "${coloredName}§7: §e$distance$suffix" if (!foundCurrentArea) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/MovementSpeedDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/MovementSpeedDisplay.kt index 50148f031878..685ffa3cdc25 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/MovementSpeedDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/MovementSpeedDisplay.kt @@ -7,8 +7,8 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.BlockUtils.getBlockAt import at.hannibal2.skyhanni.utils.LocationUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderString import net.minecraft.client.Minecraft import net.minecraft.init.Blocks @@ -59,7 +59,7 @@ object MovementSpeedDisplay { } usingSoulsandSpeed = movingOnSoulsand && soulsandSpeeds.size == 6 if (isEnabled()) { - display = "Movement Speed: ${speed.round(2)}" + display = "Movement Speed: ${speed.roundTo(2)}" } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/TpsCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/TpsCounter.kt index 0a3a60295371..01d84daa3fdc 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/TpsCounter.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/TpsCounter.kt @@ -10,7 +10,7 @@ import at.hannibal2.skyhanni.events.minecraft.packet.PacketReceivedEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderString import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.concurrent.fixedRateTimer @@ -55,7 +55,7 @@ object TpsCounter { "§eTPS: §f(${current}s)" } else { val sum = tpsList.sum().toDouble() - var tps = (sum / tpsList.size).round(1) + var tps = (sum / tpsList.size).roundTo(1) if (tps > 20) tps = 20.0 val color = getColor(tps) "§eTPS: $color$tps" diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt index 7f041bc27f9a..f1fad4e63aca 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/UserLuckBreakdown.kt @@ -14,10 +14,10 @@ import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern @@ -160,7 +160,7 @@ object UserLuckBreakdown { skillCalcCoolDown = SimpleTimeMark.now() calcSkillLuck() } - val limboLuck = storage?.limbo?.userLuck?.round(1) ?: 0.0f + val limboLuck = storage?.limbo?.userLuck?.roundTo(1) ?: 0.0f when (event.slot.inventory.name) { "Your Equipment and Stats" -> equipmentMenuTooltip(event, limboLuck) "Your Stats Breakdown" -> statsBreakdownLoreTooltip(event, limboLuck) @@ -270,7 +270,7 @@ object UserLuckBreakdown { calcSkillLuck() return when (type) { "mainMenu" -> { - val luckString = tryTruncateFloat(luckInput.round(2)) + val luckString = tryTruncateFloat(luckInput.roundTo(2)) if (luckInput == 0.0f) { arrayOf( "§7SkyHanni User Luck is the best stat.", @@ -293,7 +293,7 @@ object UserLuckBreakdown { } "limbo" -> { - val luckString = tryTruncateFloat(luckInput.round(2)) + val luckString = tryTruncateFloat(luckInput.roundTo(2)) arrayOf( "§8Action", "", diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt index 666671e9ca3f..1c46b40d6529 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboPlaytime.kt @@ -8,10 +8,10 @@ import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems.getItemStack import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern @@ -105,7 +105,7 @@ object LimboPlaytime { if ((wholeMinutes % 60) == 0) { hoursString = "$wholeHours" } else { - val minutes: Float = ((wholeMinutes - wholeHours * 60).toFloat() / 60).round(1) + val minutes: Float = ((wholeMinutes - wholeHours * 60).toFloat() / 60).roundTo(1) hoursString = wholeHours.addSeparators() if (findFloatDecimalPlace(minutes) != 0) { val minutesString = minutes.toString() diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboTimeTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboTimeTracker.kt index 62f4c46561c6..bfb8ce715d2f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboTimeTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/limbo/LimboTimeTracker.kt @@ -15,7 +15,7 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.LocationUtils.isPlayerInside import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderString import at.hannibal2.skyhanni.utils.SimpleTimeMark import at.hannibal2.skyhanni.utils.TimeUtils.format @@ -116,7 +116,7 @@ object LimboTimeTracker { if (passedSince > currentPB) { oldPB = currentPB storage?.personalBest = passedSince.toInt(DurationUnit.SECONDS) - userLuck = ((storage?.personalBest ?: 0) * USER_LUCK_MULTIPLIER).round(2) + userLuck = ((storage?.personalBest ?: 0) * USER_LUCK_MULTIPLIER).roundTo(2) if (onFire) userLuck *= FIRE_MULTIPLIER ChatUtils.chat("§fYou were in Limbo for §e$duration§f! §d§lPERSONAL BEST§r§f!") if (oldPB != 0.seconds) { @@ -126,9 +126,9 @@ object LimboTimeTracker { } else ChatUtils.chat("§fYou were in Limbo for §e$duration§f.") if (userLuck > oldLuck) { if (onFire) { - ChatUtils.chat("§fYour §aPersonal Bests§f perk is now granting you §a+${userLuck.round(2)}§c✴ §aSkyHanni User Luck§f! ") + ChatUtils.chat("§fYour §aPersonal Bests§f perk is now granting you §a+${userLuck.roundTo(2)}§c✴ §aSkyHanni User Luck§f! ") } else { - ChatUtils.chat("§fYour §aPersonal Bests§f perk is now granting you §a+${userLuck.round(2)}✴ SkyHanni User Luck§f!") + ChatUtils.chat("§fYour §aPersonal Bests§f perk is now granting you §a+${userLuck.roundTo(2)}✴ SkyHanni User Luck§f!") } storage?.userLuck = userLuck } @@ -152,7 +152,7 @@ object LimboTimeTracker { val currentPB = storage?.personalBest ?: 0 val userLuck = storage?.userLuck ?: 0f val limboPB: Int = if (currentPB < timeInLimbo) timeInLimbo else currentPB - var luckString = tryTruncateFloat(userLuck.round(2)) + var luckString = tryTruncateFloat(userLuck.roundTo(2)) if (userLuck > 0) luckString = "+$luckString" var firstMessage = "§fYour current PB is §e${limboPB.seconds}§f, granting you §a$luckString✴ SkyHanni User Luck§f!" diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/pets/PetExpTooltip.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/pets/PetExpTooltip.kt index 8e544a3dfefc..e5c3179cc4a9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/pets/PetExpTooltip.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/pets/PetExpTooltip.kt @@ -11,8 +11,8 @@ import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.KeyboardManager import at.hannibal2.skyhanni.utils.LorenzRarity import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.ReflectionUtils.makeAccessible import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getPetExp @@ -36,7 +36,7 @@ object PetExpTooltip { if (!KeyboardManager.isShiftKeyDown() && !config.showAlways) return val itemStack = event.itemStack - val petExperience = itemStack.getPetExp()?.round(1) ?: return + val petExperience = itemStack.getPetExp()?.roundTo(1) ?: return val name = itemStack.name try { @@ -105,7 +105,7 @@ object PetExpTooltip { val maxLevel = if (useGoldenDragonLevels) 200 else 100 val maxXp = when { - useGoldenDragonLevels -> LEVEL_200_LEGENDARY // lvl 200 legendary + useGoldenDragonLevels -> LEVEL_200_LEGENDARY petName.contains("Bingo") -> LEVEL_100_COMMON else -> LEVEL_100_LEGENDARY diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/area/livingcave/LivingMetalSuitProgress.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/area/livingcave/LivingMetalSuitProgress.kt index b0d4676f2e31..9c65a273e867 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/rift/area/livingcave/LivingMetalSuitProgress.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/rift/area/livingcave/LivingMetalSuitProgress.kt @@ -7,7 +7,7 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getLivingMetalProgress import net.minecraft.item.ItemStack @@ -39,7 +39,7 @@ object LivingMetalSuitProgress { if (progressMap.isEmpty()) return@buildList - val totalProgress = progressMap.values.map { it ?: 1.0 }.average().roundToPrecision(1) + val totalProgress = progressMap.values.map { it ?: 1.0 }.average().roundTo(1) val formatPercentage = LorenzUtils.formatPercentage(totalProgress) addAsSingletonList("§7Living Metal Suit Progress: ${if (isMaxed) "§a§lMAXED!" else "§a$formatPercentage"}") diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/CruxTalismanDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/CruxTalismanDisplay.kt index ccdb19fc5094..c333550f6349 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/CruxTalismanDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/CruxTalismanDisplay.kt @@ -11,7 +11,7 @@ import at.hannibal2.skyhanni.utils.InventoryUtils import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.StringUtils.removeColor @@ -80,7 +80,7 @@ object CruxTalismanDisplay { } } } - percentValue = ((percent.toDouble() / 600) * 100).roundToPrecision(1) + percentValue = ((percent.toDouble() / 600) * 100).roundTo(1) if (bonusesLine.isNotEmpty() && config.showBonuses.get()) { addAsSingletonList("§7Bonuses:") bonusesLine.forEach { addAsSingletonList(" $it") } diff --git a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/motes/RiftMotesOrb.kt b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/motes/RiftMotesOrb.kt index d1ada6a38e98..4fc0ccbf5fce 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/motes/RiftMotesOrb.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/rift/everywhere/motes/RiftMotesOrb.kt @@ -9,8 +9,8 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.CollectionUtils.editCopy import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzColor -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled @@ -79,7 +79,7 @@ object RiftMotesOrb { val ageInSeconds = (System.currentTimeMillis() - orb.startTime).toDouble() / 1000 if (ageInSeconds < 0.5) continue - val particlesPerSecond = (orb.counter.toDouble() / ageInSeconds).round(1) + val particlesPerSecond = (orb.counter.toDouble() / ageInSeconds).roundTo(1) if (particlesPerSecond < 60 || particlesPerSecond > 90) continue orb.isOrb = true diff --git a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt index 87228447fadc..a16f78b52e45 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt @@ -23,7 +23,7 @@ import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble import at.hannibal2.skyhanni.utils.NumberUtil.interpolate -import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.Quad import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems @@ -441,7 +441,7 @@ object SkillProgress { val percent = if (currentXpMax == 0L) 100F else 100F * currentXp / currentXpMax if (config.usePercentage.get()) - append("§7(§6${percent.roundToPrecision(2)}%§7)") + append("§7(§6${percent.roundTo(2)}%§7)") else { if (currentXpMax == 0L) append("§7(§6${currentXp.addSeparators()}§7)") diff --git a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt index 0a94f9ee8b28..8c1d3f618c29 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt @@ -10,7 +10,7 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators -import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.toRoman import at.hannibal2.skyhanni.utils.StringUtils import at.hannibal2.skyhanni.utils.StringUtils.isRoman @@ -40,7 +40,7 @@ object SkillTooltip { val maxReached = "§7§8Max Skill level reached!" if (line.contains(maxReached) && overflowConfig.enableInSkillMenuTooltip) { val progress = (skillInfo.overflowCurrentXp.toDouble() / skillInfo.overflowCurrentXpMax) * 100 - val percent = "§e${progress.roundToPrecision(1)}%" + val percent = "§e${progress.roundTo(1)}%" val currentLevel = skillInfo.overflowLevel val level = if (useRoman) currentLevel.toRoman() else currentLevel @@ -74,7 +74,7 @@ object SkillTooltip { val progress = have.toDouble() / need val progressBar = StringUtils.progressBar(progress) val nextLevel = if (useRoman) targetLevel.toRoman() else targetLevel - val percent = "§e${(progress * 100).roundToPrecision(1)}%" + val percent = "§e${(progress * 100).roundTo(1)}%" iterator.add("") iterator.add("§7Progress to Level $nextLevel: $percent") iterator.add("$progressBar §e${have.addSeparators()}§6/§e${need.addSeparators()}") diff --git a/src/main/java/at/hannibal2/skyhanni/test/PacketTest.kt b/src/main/java/at/hannibal2/skyhanni/test/PacketTest.kt index 99dd9fcbfc76..a0ab34edee3a 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/PacketTest.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/PacketTest.kt @@ -7,9 +7,9 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.EntityUtils import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.NumberUtil.isInt +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.ReflectionUtils.makeAccessible import at.hannibal2.skyhanni.utils.getLorenzVec import at.hannibal2.skyhanni.utils.toLorenzVec @@ -187,7 +187,7 @@ object PacketTest { } private fun getDistance(location: LorenzVec?): Double { - return location?.distanceToPlayer()?.round(1) ?: 0.0 + return location?.distanceToPlayer()?.roundTo(1) ?: 0.0 } private fun getLocation(packet: Packet<*>, entity: Entity?): LorenzVec? { diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index c0c03b6771ab..156ce82a31c3 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -38,7 +38,6 @@ import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzDebug import at.hannibal2.skyhanni.utils.LorenzLogger import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName @@ -47,6 +46,7 @@ import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.ReflectionUtils.makeAccessible import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText @@ -324,9 +324,9 @@ object SkyHanniDebugsAndTests { fun copyLocation(args: Array) { val location = LocationUtils.playerLocation() - val x = (location.x + 0.001).round(1) - val y = (location.y + 0.001).round(1) - val z = (location.z + 0.001).round(1) + val x = (location.x + 0.001).roundTo(1) + val y = (location.y + 0.001).roundTo(1) + val z = (location.z + 0.001).roundTo(1) if (args.size == 1 && args[0].equals("json", false)) { OSUtils.copyToClipboard("\"$x:$y:$z\"") return @@ -576,7 +576,8 @@ object SkyHanniDebugsAndTests { }.editCopy { this.add( 0, - generateSequence(scale) { it + 0.1 }.take(25).map { Renderable.string(it.round(1).toString()) }.toList(), + generateSequence(scale) { it + 0.1 }.take(25).map { Renderable.string(it.roundTo(1).toString()) } + .toList(), ) } config.debugItemPos.renderRenderables( diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/TrackParticlesCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/command/TrackParticlesCommand.kt index 0892d11252a1..69c16daeb414 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/command/TrackParticlesCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/command/TrackParticlesCommand.kt @@ -7,8 +7,8 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.ReceiveParticleEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables @@ -116,7 +116,7 @@ object TrackParticlesCommand { event.drawDynamicText(key, "§7§l${particle.type}", 0.8) event.drawDynamicText( key.up(-0.2), - "§7C: §e${particle.count} §7S: §a${particle.speed.round(2)}", + "§7C: §e${particle.count} §7S: §a${particle.speed.roundTo(2)}", scaleMultiplier = 0.8 ) } diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/TrackSoundsCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/command/TrackSoundsCommand.kt index 50b0c417dc85..1fadab62da24 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/command/TrackSoundsCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/command/TrackSoundsCommand.kt @@ -7,8 +7,8 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.PlaySoundEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.utils.ChatUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.OSUtils import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables @@ -123,7 +123,7 @@ object TrackSoundsCommand { event.drawDynamicText(key, "§7§l${sound.soundName}", 0.8) event.drawDynamicText( key.up(-0.2), - "§7P: §e${sound.pitch.round(2)} §7V: $volumeColor${sound.volume.round(2)}", + "§7P: §e${sound.pitch.roundTo(2)} §7V: $volumeColor${sound.volume.roundTo(2)}", scaleMultiplier = 0.8, ) } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt index ba1081a5f3dd..121b9cce337a 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt @@ -3,8 +3,8 @@ package at.hannibal2.skyhanni.utils import at.hannibal2.skyhanni.config.features.skillprogress.SkillProgressBarConfig import at.hannibal2.skyhanni.features.chroma.ChromaShaderManager import at.hannibal2.skyhanni.features.chroma.ChromaType -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.NumberUtil.fractionOf +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment import at.hannibal2.skyhanni.utils.renderables.Renderable import net.minecraft.client.Minecraft @@ -204,7 +204,7 @@ object GuiRenderUtils { scale = scale, horizontalAlign = HorizontalAlignment.LEFT, ), Renderable.string( - "§2${(percent * 100).round(1)}%", + "§2${(percent * 100).roundTo(1)}%", scale = scale, horizontalAlign = HorizontalAlignment.RIGHT, ), diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt index 1fd5419d9622..cd96121c426d 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt @@ -16,6 +16,7 @@ import at.hannibal2.skyhanni.test.TestBingo import at.hannibal2.skyhanni.utils.ChatUtils.lastButtonClicked import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.fromNow import at.hannibal2.skyhanni.utils.StringUtils.capAtMinecraftLength import at.hannibal2.skyhanni.utils.StringUtils.removeColor @@ -100,23 +101,11 @@ object LorenzUtils { return originalMessage.stripHypixelMessage() } - fun Double.round(decimals: Int): Double { - var multiplier = 1.0 - repeat(decimals) { multiplier *= 10 } - val result = kotlin.math.round(this * multiplier) / multiplier - val a = result.toString() - val b = toString() - return if (a.length > b.length) this else result - } + @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) + fun Double.round(decimals: Int) = this.roundTo(decimals) - fun Float.round(decimals: Int): Float { - var multiplier = 1.0 - repeat(decimals) { multiplier *= 10 } - val result = kotlin.math.round(this * multiplier) / multiplier - val a = result.toString().length - val b = toString().length - return if (a > b) this else result.toFloat() - } + @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) + fun Float.round(decimals: Int) = this.roundTo(decimals) // TODO replace all calls with regex @Deprecated("Do not use complicated string operations", ReplaceWith("Regex")) diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index a573ed78daeb..2e5748c6c590 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -1,6 +1,6 @@ package at.hannibal2.skyhanni.utils -import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import net.minecraft.client.renderer.GlStateManager import net.minecraft.entity.Entity import net.minecraft.network.play.server.S2APacketParticles @@ -153,12 +153,12 @@ data class LorenzVec( return result } - fun round(decimals: Int) = LorenzVec(x.round(decimals), y.round(decimals), z.round(decimals)) + fun round(decimals: Int) = LorenzVec(x.roundTo(decimals), y.roundTo(decimals), z.roundTo(decimals)) fun roundLocationToBlock(): LorenzVec { - val x = (x - .499999).round(0) - val y = (y - .499999).round(0) - val z = (z - .499999).round(0) + val x = (x - .499999).roundTo(0) + val y = (y - .499999).roundTo(0) + val z = (z - .499999).roundTo(0) return LorenzVec(x, y, z) } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt index 4d5fe0a1dd6f..58441ecabc4b 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt @@ -1,13 +1,11 @@ package at.hannibal2.skyhanni.utils import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.RegexUtils.matches import java.text.NumberFormat import java.util.Locale import java.util.TreeMap import kotlin.math.pow -import kotlin.math.roundToInt object NumberUtil { @@ -83,21 +81,21 @@ object NumberUtil { * @link https://stackoverflow.com/a/22186845 * @author jpdymond */ - fun Double.roundToPrecision(precision: Int): Double { // TODO is this the same as LorenzUtils.round() ? - val scale = 10.0.pow(precision).toInt() - return (this * scale).roundToInt().toDouble() / scale + fun Double.roundTo(precision: Int): Double { + val scale = 10.0.pow(precision) + return kotlin.math.round(scale) / scale } - /** - * This code was unmodified and taken under CC BY-SA 3.0 license - * @link https://stackoverflow.com/a/22186845 - * @author jpdymond - */ - fun Float.roundToPrecision(precision: Int): Float { - val scale = 10.0.pow(precision).toInt() - return (this * scale).roundToInt().toFloat() / scale + fun Float.roundTo(precision: Int): Float { + return toDouble().roundTo(precision).toFloat() } + @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) + fun Double.roundToPrecision(precision: Int) = this.roundTo(precision) + + @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) + fun Float.roundToPrecision(precision: Int) = this.roundTo(precision) + fun Number.ordinal(): String { val long = this.toLong() if (long % 100 in 11..13) return "th" @@ -209,7 +207,7 @@ object NumberUtil { fun Number.percentWithColorCode(max: Number, round: Int = 1): String { val fraction = this.fractionOf(max) val color = percentageColor(fraction) - val amount = (fraction * 100.0).round(round) + val amount = (fraction * 100.0).roundTo(round) return "${color.getChatColor()}$amount%" } From a8be86761f6302894346498df121a499be3f93ad Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:19:44 +0200 Subject: [PATCH 084/102] fix rounding --- .../experimentationtable/ExperimentsProfitTracker.kt | 4 ++-- src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt | 4 ++-- src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt | 6 ++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt index 7b6fa9bc5f1e..6e755d874018 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/experimentationtable/ExperimentsProfitTracker.kt @@ -25,10 +25,10 @@ import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull import at.hannibal2.skyhanni.utils.LorenzUtils -import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzVec import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.roundTo import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches @@ -162,7 +162,7 @@ object ExperimentsProfitTracker { val price = internalName.getPrice() val npcPrice = internalName.getNpcPriceOrNull() ?: 0.0 val maxPrice = npcPrice.coerceAtLeast(price) - startCostTemp += maxPrice.round(0).toInt() + startCostTemp += maxPrice.roundTo(0).toInt() iterator.remove() } tracker.modify { diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt index cd96121c426d..e4e74b0d213f 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt @@ -101,10 +101,10 @@ object LorenzUtils { return originalMessage.stripHypixelMessage() } - @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) + @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(decimals)")) fun Double.round(decimals: Int) = this.roundTo(decimals) - @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) + @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(decimals)")) fun Float.round(decimals: Int) = this.roundTo(decimals) // TODO replace all calls with regex diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt index 58441ecabc4b..dc7c4828c431 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt @@ -83,12 +83,10 @@ object NumberUtil { */ fun Double.roundTo(precision: Int): Double { val scale = 10.0.pow(precision) - return kotlin.math.round(scale) / scale + return kotlin.math.round(this * scale) / scale } - fun Float.roundTo(precision: Int): Float { - return toDouble().roundTo(precision).toFloat() - } + fun Float.roundTo(precision: Int): Float = toDouble().roundTo(precision).toFloat() @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) fun Double.roundToPrecision(precision: Int) = this.roundTo(precision) From 9a3620ea19f08104cc97560bb16feb2092b85847 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:20:03 +0200 Subject: [PATCH 085/102] code cleanup --- .../at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt index ad03a7784cf3..171df87e70ab 100755 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt @@ -30,7 +30,7 @@ object GardenYawAndPitch { if (GardenAPI.toolInHand == null && !config.showWithoutTool) return val player = Minecraft.getMinecraft().thePlayer - var yaw = LocationUtils.calculatePlayerYaw() + val yaw = LocationUtils.calculatePlayerYaw() val pitch = player.rotationPitch if (yaw != lastYaw || pitch != lastPitch) { From e4821befd023da38f8a054773e38b3f9a6f28210 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:20:33 +0200 Subject: [PATCH 086/102] replacing round to roundTo in LorenzVec --- .../java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt | 2 +- .../at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt | 4 ++-- .../skyhanni/features/combat/mobs/ArachneSpawnTimer.kt | 2 +- .../skyhanni/features/garden/pests/PestParticleWaypoint.kt | 4 ++-- .../features/mining/glacitemineshaft/MineshaftWaypoints.kt | 2 +- .../hannibal2/skyhanni/features/misc/trevor/TrevorSolver.kt | 2 +- .../at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt | 2 +- src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt | 6 +++++- 8 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt index 386167123768..96f228a6ba85 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/PlaySoundEvent.kt @@ -11,7 +11,7 @@ class PlaySoundEvent(val soundName: String, val location: LorenzVec, val pitch: val distanceToPlayer by lazy { location.distanceToPlayer() } override fun toString(): String { - return "PlaySoundEvent(soundName='$soundName', pitch=$pitch, volume=$volume, location=${location.round(1)}, distanceToPlayer=${ + return "PlaySoundEvent(soundName='$soundName', pitch=$pitch, volume=$volume, location=${location.roundTo(1)}, distanceToPlayer=${ distanceToPlayer.roundTo(1) })" } diff --git a/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt index 7c695c7eec2f..75fe8af8a3c2 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/ReceiveParticleEvent.kt @@ -21,8 +21,8 @@ class ReceiveParticleEvent( val distanceToPlayer by lazy { location.distanceToPlayer() } override fun toString(): String { - return "ReceiveParticleEvent(type='$type', location=${location.round(1)}, count=$count, speed=$speed, offset=${ - offset.round( + return "ReceiveParticleEvent(type='$type', location=${location.roundTo(1)}, count=$count, speed=$speed, offset=${ + offset.roundTo( 1 ) }, longDistance=$longDistance, particleArgs=${particleArgs.contentToString()}, distanceToPlayer=${ diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt index 2fee0577e8f6..d2e6f7ed67e5 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneSpawnTimer.kt @@ -99,7 +99,7 @@ object ArachneSpawnTimer { val packet = event.packet if (packet is S2APacketParticles) { - val location = packet.toLorenzVec().round(2) + val location = packet.toLorenzVec().roundTo(2) if (arachneAltarLocation.distance(location) > 30) return if (packet.particleType == EnumParticleTypes.REDSTONE && packet.particleSpeed == 1.0f) { particleCounter += 1 diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt index 23ead6f911d0..d07e55816732 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleWaypoint.kt @@ -86,7 +86,7 @@ object PestParticleWaypoint { val yellow = LorenzVec(0.8, 0.8, 0.0) val redPest = LorenzVec(0.8, 0.4, 0.0) val redPlot = LorenzVec(0.8, 0.0, 0.0) - isPointingToPest = when (event.offset.round(5)) { + isPointingToPest = when (event.offset.roundTo(5)) { redPlot -> false redPest, yellow, darkYellow -> true else -> return @@ -150,7 +150,7 @@ object PestParticleWaypoint { waypoint, color, 3, - false + false, ) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/MineshaftWaypoints.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/MineshaftWaypoints.kt index 7476e212b455..ae17e783bb16 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/MineshaftWaypoints.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/glacitemineshaft/MineshaftWaypoints.kt @@ -38,7 +38,7 @@ object MineshaftWaypoints { fun onIslandChange(event: IslandChangeEvent) { if (event.newIsland != IslandType.MINESHAFT) return - val playerLocation = LocationUtils.playerLocation().round(0).add(y = -1) + val playerLocation = LocationUtils.playerLocation().roundTo(0).add(y = -1) if (config.mineshaftWaypoints.entranceLocation) { waypoints.add(MineshaftWaypoint(waypointType = MineshaftWaypointType.ENTRANCE, location = playerLocation)) diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/trevor/TrevorSolver.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/trevor/TrevorSolver.kt index 2188cb1744f0..94fefbdf8042 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/trevor/TrevorSolver.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/trevor/TrevorSolver.kt @@ -29,7 +29,7 @@ object TrevorSolver { var averageHeight = (minHeight + maxHeight) / 2 fun findMobHeight(height: Int, above: Boolean) { - val playerPosition = LocationUtils.playerLocation().round(2) + val playerPosition = LocationUtils.playerLocation().roundTo(2) val mobHeight = if (above) playerPosition.y + height else playerPosition.y - height if (maxHeight == 0.0) { diff --git a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt index 156ce82a31c3..cac3349e11bd 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/SkyHanniDebugsAndTests.kt @@ -606,7 +606,7 @@ object SkyHanniDebugsAndTests { @SubscribeEvent fun onReceiveParticle(event: ReceiveParticleEvent) { // val particleType = event.type -// val distance = LocationUtils.playerLocation().distance(event.location).round(2) +// val distance = LocationUtils.playerLocation().distance(event.location).roundTo(2) // // println("") // println("particleType: $particleType") diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt index 2e5748c6c590..6769210ef26b 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzVec.kt @@ -153,7 +153,10 @@ data class LorenzVec( return result } - fun round(decimals: Int) = LorenzVec(x.roundTo(decimals), y.roundTo(decimals), z.roundTo(decimals)) + @Deprecated("Use roundTo instead", ReplaceWith("this.roundTo(precision)")) + fun round(precision: Int) = roundTo(precision) + + fun roundTo(precision: Int) = LorenzVec(x.roundTo(precision), y.roundTo(precision), z.roundTo(precision)) fun roundLocationToBlock(): LorenzVec { val x = (x - .499999).roundTo(0) @@ -164,6 +167,7 @@ data class LorenzVec( fun slope(other: LorenzVec, factor: Double) = this + (other - this).scale(factor) + // TODO better name. dont confuse with roundTo() fun roundLocation(): LorenzVec { val x = if (this.x < 0) x.toInt() - 1 else x.toInt() val y = y.toInt() - 1 From cf039c7e215b26f78eeb468cd911c5d8f8586115 Mon Sep 17 00:00:00 2001 From: Clicks <58398364+CuzImClicks@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:26:42 +0200 Subject: [PATCH 087/102] Improvement: Option to hide all Tooltips inside of Excavator (#2579) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../mining/ExcavatorTooltipHiderConfig.java | 21 ++++++++++++++ .../mining/FossilExcavatorConfig.java | 13 ++++----- .../fossilexcavator/ExcavatorTooltipHider.kt | 28 +++++++++++++++++-- 3 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/mining/ExcavatorTooltipHiderConfig.java diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/ExcavatorTooltipHiderConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/ExcavatorTooltipHiderConfig.java new file mode 100644 index 000000000000..b1489ace876a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/ExcavatorTooltipHiderConfig.java @@ -0,0 +1,21 @@ +package at.hannibal2.skyhanni.config.features.mining; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +public class ExcavatorTooltipHiderConfig { + + @Expose + @ConfigOption(name = "Hide Dirt", desc = "Hides tooltips of the Dirt inside of the Fossil Excavator.") + @ConfigEditorBoolean + @FeatureToggle + public boolean hideDirt = true; + + @Expose + @ConfigOption(name = "Hide Everything", desc = "Hide all tooltips inside of the Fossil Excavator.") + @ConfigEditorBoolean + @FeatureToggle + public boolean hideEverything = false; +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java index 8dfb12854232..8445afe5336a 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java @@ -18,6 +18,11 @@ public class FossilExcavatorConfig { @Accordion public ExcavatorProfitTrackerConfig profitTracker = new ExcavatorProfitTrackerConfig(); + @Expose + @ConfigOption(name = "Excavator Tooltip Hider", desc = "") + @Accordion + public ExcavatorTooltipHiderConfig tooltipHider = new ExcavatorTooltipHiderConfig(); + @Expose @ConfigOption( name = "Profit per Excavation", @@ -36,12 +41,4 @@ public class FossilExcavatorConfig { @FeatureToggle public boolean glacitePowderStack = false; - @Expose - @ConfigOption( - name = "Hide Excavator Tooltips", - desc = "Hides tooltips of items inside of the Fossil Excavator." - ) - @ConfigEditorBoolean - @FeatureToggle - public boolean hideExcavatorTooltips = true; } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt index 8b58402cdd63..0b0469b91675 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ExcavatorTooltipHider.kt @@ -3,20 +3,42 @@ package at.hannibal2.skyhanni.features.mining.fossilexcavator import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.LorenzToolTipEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.client.player.inventory.ContainerLocalMenu import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @SkyHanniModule object ExcavatorTooltipHider { - private val config get() = SkyHanniMod.feature.mining.fossilExcavator + private val config get() = SkyHanniMod.feature.mining.fossilExcavator.tooltipHider + + /** + * REGEX-TEST: §6Dirt + */ + private val dirtPattern by RepoPattern.pattern( + "excavator.dirt.name", + "§6Dirt", + ) @SubscribeEvent fun onTooltip(event: LorenzToolTipEvent) { if (!isEnabled()) return + if (event.slot.inventory !is ContainerLocalMenu) return - event.cancel() + if (config.hideEverything) { + event.cancel() + return + } + + if (config.hideDirt) { + val isDirt = dirtPattern.matches(event.itemStack.name) + if (isDirt) { + event.cancel() + } + } } - fun isEnabled() = FossilExcavatorAPI.inInventory && !FossilExcavatorAPI.inExcavatorMenu && config.hideExcavatorTooltips + fun isEnabled() = FossilExcavatorAPI.inInventory && !FossilExcavatorAPI.inExcavatorMenu } From 04d2530a67ef9ae2b877a448e15ce16fadd4c25b Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 24 Sep 2024 10:27:37 +0200 Subject: [PATCH 088/102] Fix: Hard Stone in Crystal Hollows misdetected as Mithril (#2580) --- src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt index 8e7cbf0821da..4275a809ca92 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/OreBlock.kt @@ -30,7 +30,7 @@ enum class OreBlock( // MITHRIL LOW_TIER_MITHRIL( checkBlock = ::isLowTierMithril, - checkArea = { inDwarvenMines || inCrystalHollows || inGlacite }, + checkArea = { inDwarvenMines || inGlacite }, ), MID_TIER_MITHRIL( checkBlock = { it.block == Blocks.prismarine }, From 1e9764d1d1209177d4a939a9b812931a7d49a926 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:27:50 +0200 Subject: [PATCH 089/102] Backend: PrimitiveIngredient changes and code cleanup (#2576) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: Cal --- .../skyhanni/features/bingo/MinionCraftHelper.kt | 9 ++++----- .../garden/visitor/GardenVisitorFeatures.kt | 13 ++++++------- .../garden/visitor/GardenVisitorSupercraft.kt | 14 +++++++------- .../at/hannibal2/skyhanni/utils/ItemUtils.kt | 5 ++--- .../java/at/hannibal2/skyhanni/utils/NEUItems.kt | 9 +++++---- .../skyhanni/utils/PrimitiveIngredient.kt | 16 +++++++++++++--- .../skyhanni/utils/PrimitiveItemStack.kt | 2 ++ 7 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt index dbec4ea5788c..6f7a062f85e5 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt @@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent import at.hannibal2.skyhanni.events.LorenzTickEvent import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.hasEnchantments import at.hannibal2.skyhanni.utils.ItemUtils.itemName @@ -18,6 +19,7 @@ import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.NEUItems.getCachedIngredients import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary +import at.hannibal2.skyhanni.utils.PrimitiveIngredient.Companion.toPrimitiveItemStacks import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings import at.hannibal2.skyhanni.utils.StringUtils.removeColor @@ -197,12 +199,9 @@ object MinionCraftHelper { val output = recipe.output ?: continue if (!output.internalName.contains("_GENERATOR_")) continue val map = mutableMapOf() - for (input in recipe.ingredients) { - val itemId = input.internalName + for ((itemId, count) in recipe.ingredients.toPrimitiveItemStacks()) { if (minionId != itemId) { - val count = input.count.toInt() - val old = map.getOrDefault(itemId, 0) - map[itemId] = old + count + map.addOrPut(itemId, count) } } var allDone = true diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt index 2e8d789660ea..70392bb46d78 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt @@ -29,6 +29,7 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.ConfigUtils import at.hannibal2.skyhanni.utils.EntityUtils import at.hannibal2.skyhanni.utils.HypixelCommands @@ -51,6 +52,7 @@ import at.hannibal2.skyhanni.utils.NEUItems.getPrice import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.formatInt import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.PrimitiveIngredient.Companion.toPrimitiveItemStacks import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RenderUtils.drawString import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems @@ -253,16 +255,13 @@ object GardenVisitorFeatures { ?.ingredients ?: emptySet() if (ingredients.isEmpty()) return - // TODO change key to NEUInternalName - val requiredIngredients = mutableMapOf() - for (ingredient in ingredients) { - val key = ingredient.internalName.asString() - requiredIngredients[key] = - requiredIngredients.getOrDefault(key, 0) + ingredient.count.toInt() + val requiredIngredients = mutableMapOf() + for ((key, count) in ingredients.toPrimitiveItemStacks()) { + requiredIngredients.addOrPut(key, count) } var hasIngredients = true for ((key, value) in requiredIngredients) { - val sackItem = key.asInternalName().getAmountInSacks() + val sackItem = key.getAmountInSacks() if (sackItem < value * (amount - amountInSacks)) { hasIngredients = false break diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt index f2e4f80fb8f1..9b14483dc476 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorSupercraft.kt @@ -7,12 +7,14 @@ import at.hannibal2.skyhanni.events.garden.visitor.VisitorOpenEvent import at.hannibal2.skyhanni.events.render.gui.ReplaceItemEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.HypixelCommands import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.NEUInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.NEUItems.getItemStack +import at.hannibal2.skyhanni.utils.PrimitiveIngredient.Companion.toPrimitiveItemStacks import at.hannibal2.skyhanni.utils.SimpleTimeMark import net.minecraft.entity.player.InventoryPlayer import net.minecraftforge.fml.common.eventhandler.EventPriority @@ -73,15 +75,13 @@ object GardenVisitorSupercraft { // TODO describe what this line does .firstOrNull { !it.ingredients.first().internalName.contains("PEST") } ?.ingredients ?: return - // TODO change key to NEUInternalName - val ingredientReqs = mutableMapOf() - for (ingredient in ingredients) { - val key = ingredient.internalName.asString() - ingredientReqs[key] = ingredientReqs.getOrDefault(key, 0) + ingredient.count.toInt() + val requiredIngredients = mutableMapOf() + for ((key, count) in ingredients.toPrimitiveItemStacks()) { + requiredIngredients.addOrPut(key, count) } hasIngredients = true - for ((key, value) in ingredientReqs) { - val sackItem = key.asInternalName().getAmountInSacks() + for ((key, value) in requiredIngredients) { + val sackItem = key.getAmountInSacks() lastSuperCraftMaterial = internalName.asString() if (sackItem < value * amount) { hasIngredients = false diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt index add66cc0adcf..d39b37c8abb1 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ItemUtils.kt @@ -9,6 +9,7 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice import at.hannibal2.skyhanni.utils.NEUItems.getItemStackOrNull import at.hannibal2.skyhanni.utils.NumberUtil.formatInt +import at.hannibal2.skyhanni.utils.PrimitiveIngredient.Companion.toPrimitiveItemStacks import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher import at.hannibal2.skyhanni.utils.RegexUtils.matches import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.cachedData @@ -511,9 +512,7 @@ object ItemUtils { fun neededItems(recipe: PrimitiveRecipe): Map { val neededItems = mutableMapOf() - for (ingredient in recipe.ingredients) { - val material = ingredient.internalName - val amount = ingredient.count.toInt() + for ((material, amount) in recipe.ingredients.toPrimitiveItemStacks()) { neededItems.addOrPut(material, amount) } return neededItems diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt index 198b43a856a6..2f17ea4d1d31 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/NEUItems.kt @@ -9,10 +9,12 @@ import at.hannibal2.skyhanni.events.NeuRepositoryReloadEvent import at.hannibal2.skyhanni.events.RepositoryReloadEvent import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule import at.hannibal2.skyhanni.test.command.ErrorManager +import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut import at.hannibal2.skyhanni.utils.ItemBlink.checkBlinkItem import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName import at.hannibal2.skyhanni.utils.NumberUtil.isInt +import at.hannibal2.skyhanni.utils.PrimitiveIngredient.Companion.toPrimitiveItemStacks import at.hannibal2.skyhanni.utils.PrimitiveItemStack.Companion.makePrimitiveStack import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getItemId import at.hannibal2.skyhanni.utils.json.BaseGsonBuilder @@ -308,8 +310,8 @@ object NEUItems { if (!recipe.isCraftingRecipe()) continue val map = mutableMapOf() - for (ingredient in recipe.getCachedIngredients()) { - val count = ingredient.count.toInt() + for (ingredient in recipe.getCachedIngredients().toPrimitiveItemStacks()) { + val amount = ingredient.amount var internalItemId = ingredient.internalName // ignore cactus green if (internalName == "ENCHANTED_CACTUS_GREEN".asInternalName() && internalItemId == "INK_SACK-2".asInternalName()) { @@ -331,8 +333,7 @@ object NEUItems { continue } - val old = map.getOrDefault(internalItemId, 0) - map[internalItemId] = old + count + map.addOrPut(internalItemId, amount) } if (map.size != 1) continue val current = map.iterator().next().toPair() diff --git a/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt index e8d1edefcc44..b1692a119b62 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveIngredient.kt @@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.utils import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.SKYBLOCK_COIN import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName +import at.hannibal2.skyhanni.utils.NumberUtil.formatDouble import io.github.moulberry.notenoughupdates.recipes.Ingredient class PrimitiveIngredient(val internalName: NEUInternalName, val count: Double = 1.0) { @@ -10,17 +11,26 @@ class PrimitiveIngredient(val internalName: NEUInternalName, val count: Double = constructor(ingredientIdentifier: String) : this( ingredientIdentifier.substringBefore(':').asInternalName(), - ingredientIdentifier.substringAfter(':').toDoubleOrNull() ?: 1.0, + // if second part is blank, the count is assumed to be 1 + ingredientIdentifier.substringAfter(':', "").let { if (it.isBlank()) 1.0 else it.formatDouble() }, ) companion object { fun coinIngredient(count: Double = 1.0) = PrimitiveIngredient(SKYBLOCK_COIN, count) - fun fromNeuIngredient(neuIngredient: Ingredient) = PrimitiveIngredient(neuIngredient.internalItemId.asInternalName(), neuIngredient.count) + fun fromNeuIngredient(neuIngredient: Ingredient) = + PrimitiveIngredient(neuIngredient.internalItemId.asInternalName(), neuIngredient.count) + + fun Set.toPrimitiveItemStacks(): List = + map { it.toPrimitiveItemStack() } } fun isCoin() = internalName == SKYBLOCK_COIN - override fun toString() = "$internalName x$count" + + fun toPair() = Pair(internalName, count) + + // TODO should maybe throw an error when trying to use with internalName == SKYBLOCK_COIN + fun toPrimitiveItemStack() = PrimitiveItemStack(internalName, count.toInt()) } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveItemStack.kt b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveItemStack.kt index 52faaba30066..f9d2b89f7b00 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveItemStack.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/PrimitiveItemStack.kt @@ -15,6 +15,8 @@ data class PrimitiveItemStack(val internalName: NEUInternalName, val amount: Int val itemName by lazy { internalName.itemName } + fun toPair() = Pair(internalName, amount) + companion object { fun NEUInternalName.makePrimitiveStack(amount: Int = 1) = PrimitiveItemStack(this, amount) From cc8a3dba69e8f1474a33a7e47b32d452f11735fd Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:28:06 +0200 Subject: [PATCH 090/102] Feature: /shnavigate (#2575) Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../skyhanni/config/commands/Commands.kt | 2 + .../skyhanni/data/model/GraphNodeTag.kt | 28 ++++- .../skyhanni/features/commands/HelpCommand.kt | 14 +-- .../misc/pathfind/NavigationHelper.kt | 110 ++++++++++++++++++ .../misc/reminders/ReminderManager.kt | 16 +-- .../skyhanni/test/SkyHanniDebugsAndTests.kt | 4 +- .../test/graph/GraphEditorBugFinder.kt | 11 ++ .../skyhanni/test/graph/GraphNodeEditor.kt | 6 + .../at/hannibal2/skyhanni/utils/chat/Text.kt | 7 ++ 9 files changed, 168 insertions(+), 30 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt 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 a52852a8193f..9f2e3cc3fd71 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -72,6 +72,7 @@ import at.hannibal2.skyhanni.features.misc.TpsCounter import at.hannibal2.skyhanni.features.misc.discordrpc.DiscordRPCManager import at.hannibal2.skyhanni.features.misc.limbo.LimboTimeTracker import at.hannibal2.skyhanni.features.misc.massconfiguration.DefaultConfigFeatures +import at.hannibal2.skyhanni.features.misc.pathfind.NavigationHelper import at.hannibal2.skyhanni.features.misc.reminders.ReminderManager import at.hannibal2.skyhanni.features.misc.update.UpdateManager import at.hannibal2.skyhanni.features.misc.visualwords.VisualWordGui @@ -182,6 +183,7 @@ object Commands { ) registerCommand("shremind", "Set a reminder for yourself") { ReminderManager.command(it) } registerCommand("shwords", "Opens the config list for modifying visual words") { openVisualWords() } + registerCommand("shnavigate", "Using path finder to go to locatons") { NavigationHelper.onCommand(it) } } private fun usersNormal() { diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt index 2cdc9e7e0d1d..6bf00edafcf6 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt @@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.utils.LorenzColor enum class GraphNodeTag( val internalName: String, val color: LorenzColor, - cleanName: String, + val cleanName: String, val description: String, val onlyIsland: IslandType? = null, ) { @@ -17,7 +17,7 @@ enum class GraphNodeTag( AREA("area", LorenzColor.DARK_GREEN, "Area", "A big SkyBlock area."), SMALL_AREA("small_area", LorenzColor.GREEN, "Small Area", "A small SkyBlock area, e.g. a house."), POI("poi", LorenzColor.WHITE, "Point of Interest", "A relevant spot or a landmark on the map."), -// LAUNCH_PAD("launch", LorenzColor.WHITE, "Launch Pad", "Slime blocks sending you to another server."), + // LAUNCH_PAD("launch", LorenzColor.WHITE, "Launch Pad", "Slime blocks sending you to another server."), TELEPORT("teleport", LorenzColor.BLUE, "Teleport", "A spot from/to teleport."), // on multiple islands @@ -43,18 +43,36 @@ enum class GraphNodeTag( // Rift RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the Rift.", onlyIsland = IslandType.THE_RIFT), RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Rift Eye", "An Eye in the Rift to teleport to.", onlyIsland = IslandType.THE_RIFT), - RIFT_MONTEZUMA("rift_montezuma", LorenzColor.GRAY, "Montezuma Soul Piece", "A piece of the Montezuma Soul.", onlyIsland = IslandType.THE_RIFT), + RIFT_MONTEZUMA( + "rift_montezuma", + LorenzColor.GRAY, + "Montezuma Soul Piece", + "A piece of the Montezuma Soul.", + onlyIsland = IslandType.THE_RIFT, + ), RIFT_EFFIGY("rift_effigy", LorenzColor.RED, "Blood Effigies", "Locations of the Blood Effigies.", onlyIsland = IslandType.THE_RIFT), // Spider's Den - SPIDER_RELIC("SPIDER_RELIC", LorenzColor.DARK_PURPLE, "Spider's Relic", "An relic in the Spider's Den.", onlyIsland = IslandType.SPIDER_DEN), + SPIDER_RELIC( + "SPIDER_RELIC", + LorenzColor.DARK_PURPLE, + "Spider's Relic", + "An relic in the Spider's Den.", + onlyIsland = IslandType.SPIDER_DEN, + ), // Dwarven Mines MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Mines Emissary", "An Emissary to the king.", onlyIsland = IslandType.DWARVEN_MINES), // commission areas // Crimson Isles - CRIMSON_MINIBOSS("crimson_miniboss", LorenzColor.RED, "Crimson Miniboss", "A Miniboss in the Crimson Isle.", onlyIsland = IslandType.CRIMSON_ISLE), + CRIMSON_MINIBOSS( + "crimson_miniboss", + LorenzColor.RED, + "Crimson Miniboss", + "A Miniboss in the Crimson Isle.", + onlyIsland = IslandType.CRIMSON_ISLE, + ), // The End END_GOLEM("end_golem", LorenzColor.RED, "Golem Spawn", "A spot where the golem can spawn in the End.", onlyIsland = IslandType.THE_END), diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/HelpCommand.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/HelpCommand.kt index 1890c62930d1..0f07c47f8d14 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/commands/HelpCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/commands/HelpCommand.kt @@ -5,13 +5,10 @@ import at.hannibal2.skyhanni.utils.StringUtils.splitLines import at.hannibal2.skyhanni.utils.chat.Text import at.hannibal2.skyhanni.utils.chat.Text.asComponent import at.hannibal2.skyhanni.utils.chat.Text.center -import at.hannibal2.skyhanni.utils.chat.Text.fitToChat import at.hannibal2.skyhanni.utils.chat.Text.hover import at.hannibal2.skyhanni.utils.chat.Text.onClick import at.hannibal2.skyhanni.utils.chat.Text.send -import at.hannibal2.skyhanni.utils.chat.Text.style import at.hannibal2.skyhanni.utils.chat.Text.suggest -import net.minecraft.util.EnumChatFormatting import net.minecraft.util.IChatComponent import kotlin.math.ceil @@ -20,11 +17,6 @@ object HelpCommand { private const val COMMANDS_PER_PAGE = 15 private const val HELP_ID = -6457563 - private fun createDivider() = Text.HYPHEN.fitToChat().style { - strikethrough = true - color = EnumChatFormatting.BLUE - } - private fun createCommandEntry(command: Commands.CommandInfo): IChatComponent { val category = command.category val color = category.color @@ -60,7 +52,7 @@ object HelpCommand { val text = mutableListOf() - text.add(createDivider()) + text.add(Text.createDivider()) text.add(title.asComponent().center()) text.add( Text.join( @@ -77,7 +69,7 @@ object HelpCommand { } else null, ).center(), ) - text.add(createDivider()) + text.add(Text.createDivider()) if (filtered.isEmpty()) { text.add(Text.EMPTY) @@ -91,7 +83,7 @@ object HelpCommand { } } - text.add(createDivider()) + text.add(Text.createDivider()) Text.multiline(text).send(HELP_ID) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt new file mode 100644 index 000000000000..b27292559d34 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/pathfind/NavigationHelper.kt @@ -0,0 +1,110 @@ +package at.hannibal2.skyhanni.features.misc.pathfind + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandGraphs +import at.hannibal2.skyhanni.data.model.GraphNode +import at.hannibal2.skyhanni.data.model.GraphNodeTag +import at.hannibal2.skyhanni.data.model.findShortestDistance +import at.hannibal2.skyhanni.utils.CollectionUtils.sorted +import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.chat.Text +import at.hannibal2.skyhanni.utils.chat.Text.asComponent +import at.hannibal2.skyhanni.utils.chat.Text.center +import at.hannibal2.skyhanni.utils.chat.Text.hover +import at.hannibal2.skyhanni.utils.chat.Text.onClick +import at.hannibal2.skyhanni.utils.chat.Text.send +import kotlinx.coroutines.launch +import net.minecraft.util.IChatComponent + +object NavigationHelper { + private val NAVIGATION_CHAT_ID = -6457562 + + val allowedTags = listOf( + GraphNodeTag.NPC, + GraphNodeTag.AREA, + GraphNodeTag.SMALL_AREA, + GraphNodeTag.POI, + GraphNodeTag.SLAYER, + GraphNodeTag.GRIND_MOBS, + GraphNodeTag.GRIND_ORES, + GraphNodeTag.GRIND_CROPS, + GraphNodeTag.MINES_EMISSARY, + GraphNodeTag.CRIMSON_MINIBOSS, + ) + + fun onCommand(args: Array) { + SkyHanniMod.coroutineScope.launch { + doCommandAsync(args) + } + } + + private fun doCommandAsync(args: Array) { + val searchTerm = args.joinToString(" ").lowercase() + val distances = calculateDistances(searchTerm) + val names = calculateNames(distances) + + val text = mutableListOf() + text.add(Text.createDivider()) + text.add("§7Found ${names.size} locations ($searchTerm)".asComponent().center()) + val goBack = { + onCommand(searchTerm.split(" ").toTypedArray()) + IslandGraphs.stop() + } + // TODO dont show a too long list, add pages + for ((name, node) in names) { + val distance = distances[node]!!.round(1) + val component = "$name §e$distance".asComponent() + component.onClick { + IslandGraphs.pathFind(node.position) + sendNavigateMessage(name, goBack) + } + val tag = node.tags.first { it in allowedTags } + // TODO include most closest area, if this is no area (type in area = forger in forge) + component.hover = + ("§eClick to start navigating to\n" + "§7Type: §r${tag.displayName}\n" + "§7Distance: §e$distance blocks").asComponent() + text.add(component) + } + text.add(Text.createDivider()) + Text.multiline(text).send(NAVIGATION_CHAT_ID) + } + + private fun sendNavigateMessage(name: String, goBack: () -> Unit) { + val componentText = "§7Navigating to §r$name".asComponent() + componentText.onClick(onClick = goBack) + componentText.send(NAVIGATION_CHAT_ID) + } + + private fun calculateNames(distances: Map): MutableMap { + val names = mutableMapOf() + for (node in distances.sorted().keys) { + val tag = node.tags.first { it in allowedTags } + val name = "${node.name} §7(${tag.displayName}§7)" + if (name in names) continue + names[name] = node + } + return names + } + + private fun calculateDistances( + searchTerm: String, + ): Map { + val grapth = IslandGraphs.currentIslandGraph ?: return emptyMap() + val closedNote = IslandGraphs.closedNote ?: return emptyMap() + + val nodes = grapth.nodes + val distances = mutableMapOf() + for (node in nodes) { + val name = node.name ?: continue + val remainingTags = node.tags.filter { it in allowedTags } + if (remainingTags.isEmpty()) continue + if (name.lowercase().contains(searchTerm)) { + distances[node] = grapth.findShortestDistance(closedNote, node) + } + if (remainingTags.size != 1) { + println("found node with invalid amount of tags: ${node.name} (${remainingTags.map { it.cleanName }}") + } + } + return distances + } + +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/reminders/ReminderManager.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/reminders/ReminderManager.kt index 472aac2de6be..1df878ac9a2f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/reminders/ReminderManager.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/reminders/ReminderManager.kt @@ -13,13 +13,10 @@ import at.hannibal2.skyhanni.utils.chat.Text import at.hannibal2.skyhanni.utils.chat.Text.asComponent import at.hannibal2.skyhanni.utils.chat.Text.center import at.hannibal2.skyhanni.utils.chat.Text.command -import at.hannibal2.skyhanni.utils.chat.Text.fitToChat import at.hannibal2.skyhanni.utils.chat.Text.hover import at.hannibal2.skyhanni.utils.chat.Text.send -import at.hannibal2.skyhanni.utils.chat.Text.style import at.hannibal2.skyhanni.utils.chat.Text.suggest import at.hannibal2.skyhanni.utils.chat.Text.wrap -import net.minecraft.util.EnumChatFormatting import net.minecraft.util.IChatComponent import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import kotlin.time.Duration @@ -44,11 +41,6 @@ object ReminderManager { private fun sendMessage(message: String) = Text.join("§e[Reminder]", " ", message).send(REMINDERS_ACTION_ID) - private fun createDivider() = Text.HYPHEN.fitToChat().style { - strikethrough = true - color = EnumChatFormatting.BLUE - } - private fun parseDuration(text: String): Duration? = try { val duration = TimeUtils.getDuration(text) if (duration <= 1.seconds) null else duration @@ -64,7 +56,7 @@ object ReminderManager { val text: MutableList = mutableListOf() - text.add(createDivider()) + text.add(Text.createDivider()) text.add( Text.join( @@ -113,7 +105,7 @@ object ReminderManager { text.add(Text.EMPTY) } - text.add(createDivider()) + text.add(Text.createDivider()) Text.join(*text.toTypedArray(), separator = Text.NEWLINE).send(REMINDERS_LIST_ID) } @@ -183,7 +175,7 @@ object ReminderManager { } private fun help() { - createDivider().send() + Text.createDivider().send() "§6SkyHanni Reminder Commands:".asComponent().send() "§e/shremind