diff options
author | ILike2WatchMemes <ilike2watchmemes@gmail.com> | 2024-09-20 23:32:10 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-20 23:32:10 +0200 |
commit | 19f23335cc4852e8f9c7d56036e7e89c52d230fe (patch) | |
tree | b6003aa5aca4f36f77cc6fce69b34acfed6a752e /src/main/java/at/hannibal2/skyhanni/features | |
parent | 62d45680f79c47479fe7d16a651eb8c7081586fe (diff) | |
download | skyhanni-19f23335cc4852e8f9c7d56036e7e89c52d230fe.tar.gz skyhanni-19f23335cc4852e8f9c7d56036e7e89c52d230fe.tar.bz2 skyhanni-19f23335cc4852e8f9c7d56036e7e89c52d230fe.zip |
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>
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features')
8 files changed, 858 insertions, 47 deletions
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 000000000..80433b68e --- /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<EntityArmorStand>().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 \\((?<experiment>\\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", + "^ \\+(?<amount>\\d+|\\d+,\\d+)k? Enchanting Exp$", + ) + + /** + * REGEX-TEST: +Smite VII + * REGEX-TEST: +42,000 Enchanting Exp + */ + val experimentsDropPattern by patternGroup.pattern( + "drop", + "^ \\+(?<reward>.*)\$", + ) + + /** + * 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", + "(?<amount>\\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! \\((?<current>\\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(?<enchant>.*)", + ) + + /** + * 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 000000000..0071d3af3 --- /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 000000000..50d033842 --- /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<String>() + + 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 000000000..16c0305cb --- /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<ItemStack>() + private var lastSplashTime = SimpleTimeMark.farPast() + private var lastBottlesInInventory = mutableMapOf<NEUInternalName, Int>() + private var currentBottlesInInventory = mutableMapOf<NEUInternalName, Int>() + + class Data : ItemTrackerData() { + override fun resetItems() { + experimentsDone = 0L + xpGained = 0L + bitCost = 0L + startCost = 0L + } + + override fun getDescription(timesGained: Long): List<String> { + 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<String>() + + @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<Searchable> = 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 index 9ae93f227..216cbf7c6 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 000000000..21bc6052e --- /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<String>() + + private var uncoveredAt = 0 + private var uncoveredItems = mutableListOf<Pair<Int, String>>() + 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<Pair<Item?, ItemPair?>, String>() + + private var toCheck = mutableListOf<Pair<Int, Int>>() + private var lastClicked = mutableListOf<Pair<Int, Int>>() + 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<Pair<Int, Int>>): List<String> { + 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<String>() + 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<Pair<Int, Int>>): 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?, ItemPair?>): Item = it.first ?: Item(-1, "") + + private fun right(it: Pair<Item?, ItemPair?>): ItemPair = it.second ?: ItemPair(Item(-1, ""), Item(-1, "")) + + private fun toEither(it: Any): Pair<Item?, ItemPair?> = 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 index 5c5011c85..ef99c40ba 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 index 1cc849cad..e6a9dda42 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(?<enchant>.*)" - ) - 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 } |