diff options
author | hannibal2 <24389977+hannibal00212@users.noreply.github.com> | 2023-03-02 02:00:00 +0100 |
---|---|---|
committer | hannibal2 <24389977+hannibal00212@users.noreply.github.com> | 2023-03-02 02:00:00 +0100 |
commit | 8de53239f61c0f424dab6cf2ae83f45feb4d232b (patch) | |
tree | 61578c327b7756b32cfd8f4e65c11ff307ee4fae /src/main/java/at/hannibal2/skyhanni/features/bingo | |
parent | 4bce581e2783726f6cc869dec5d7d6d4974e59a4 (diff) | |
download | skyhanni-8de53239f61c0f424dab6cf2ae83f45feb4d232b.tar.gz skyhanni-8de53239f61c0f424dab6cf2ae83f45feb4d232b.tar.bz2 skyhanni-8de53239f61c0f424dab6cf2ae83f45feb4d232b.zip |
bingo card small reworks for current bingo event, made craft minion helper feature bingo only.
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/bingo')
3 files changed, 329 insertions, 20 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoCardDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoCardDisplay.kt index f1c87bf15..824fd7942 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoCardDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoCardDisplay.kt @@ -27,6 +27,11 @@ class BingoCardDisplay { private var tick = 0 private val display = mutableListOf<String>() + private val config get() = SkyHanniMod.feature.bingo + + init { + update() + } companion object { val personalGoals = mutableListOf<PersonalGoal>() @@ -48,6 +53,7 @@ class BingoCardDisplay { @SubscribeEvent fun onTick(event: TickEvent.ClientTickEvent) { if (!LorenzUtils.isBingoProfile) return + if (!config.cardDisplay) return if (event.phase != TickEvent.Phase.START) return tick++ @@ -117,13 +123,17 @@ class BingoCardDisplay { display.clear() display.add("Community Goals") - communityGoals.mapTo(display) { " " + it.description + if (it.done) " §aDONE" else "" } - - val todo = personalGoals.filter { !it.done } - val done = MAX_PERSONAL_GOALS - todo.size - display.add(" ") - display.add("Personal Goals: ($done/$MAX_PERSONAL_GOALS done)") - todo.mapTo(display) { " " + it.description } + if (communityGoals.isEmpty()) { + display.add("§7Open the §e/bingo §7card.") + } else { + communityGoals.mapTo(display) { " " + it.description + if (it.done) " §aDONE" else "" } + + val todo = personalGoals.filter { !it.done } + val done = MAX_PERSONAL_GOALS - todo.size + display.add(" ") + display.add("Personal Goals: ($done/$MAX_PERSONAL_GOALS done)") + todo.mapTo(display) { " " + it.description } + } } private var lastSneak = false @@ -132,7 +142,7 @@ class BingoCardDisplay { @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) { if (!LorenzUtils.isBingoProfile) return - if (!SkyHanniMod.feature.bingo.bingoCard) return + if (!config.cardDisplay) return val stack = Minecraft.getMinecraft().thePlayer.heldItem //TODO into ItemUtils or InventoryUtils if (ItemUtils.isSkyBlockMenuItem(stack)) { @@ -147,18 +157,22 @@ class BingoCardDisplay { } } } + if (!config.stepHelper && displayMode == 1) { + displayMode = 0 + } if (displayMode == 0) { if (Minecraft.getMinecraft().currentScreen !is GuiChat) { - SkyHanniMod.feature.bingo.bingoCardPos.renderStrings(display) + config.bingoCardPos.renderStrings(display) } } else if (displayMode == 1) { - SkyHanniMod.feature.bingo.bingoCardPos.renderStrings(BingoNextStepHelper.currentHelp) + config.bingoCardPos.renderStrings(BingoNextStepHelper.currentHelp) } } @SubscribeEvent fun onChat(event: LorenzChatEvent) { if (!LorenzUtils.isBingoProfile) return + if (!config.cardDisplay) return val message = event.message //§6§lBINGO GOAL COMPLETE! §r§eRaw Salmon Collector diff --git a/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoNextStepHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoNextStepHelper.kt index b0ec44f02..91b5b9bd6 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoNextStepHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bingo/BingoNextStepHelper.kt @@ -1,5 +1,6 @@ package at.hannibal2.skyhanni.features.bingo +import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.data.SkillExperience import at.hannibal2.skyhanni.events.LorenzChatEvent @@ -20,6 +21,8 @@ class BingoNextStepHelper { private val itemIslandRequired = mutableMapOf<String, IslandVisitStep>() private val itemRequired = mutableMapOf<String, NextStep>() private val islands = mutableMapOf<IslandType, IslandVisitStep>() + private val collectionPattern = Pattern.compile("Reach ([0-9]+(?:,\\d+)*) (.*) Collection\\.") + private val crystalPattern = Pattern.compile("Obtain a (\\w+) Crystal in the Crystal Hollows\\.") companion object { private val finalSteps = mutableListOf<NextStep>() @@ -39,7 +42,12 @@ class BingoNextStepHelper { } currentHelp.clear() - currentHelp.add("Bingo Helper:") + currentHelp.add("Bingo Step Helper:") + + if (currentSteps.isEmpty()) { + currentHelp.add("§7Open the §e/bingo §7card.") + } + for (currentStep in currentSteps) { val text = getName(currentStep) currentHelp.add(" §7$text") @@ -92,6 +100,7 @@ class BingoNextStepHelper { @SubscribeEvent fun onTick(event: TickEvent.ClientTickEvent) { if (!LorenzUtils.isBingoProfile) return + if (!SkyHanniMod.feature.bingo.cardDisplay) return if (event.phase != TickEvent.Phase.START) return tick++ @@ -106,7 +115,8 @@ class BingoNextStepHelper { @SubscribeEvent fun onChat(event: LorenzChatEvent) { - if (!LorenzUtils.inSkyBlock) return + if (!LorenzUtils.isBingoProfile) return + if (!SkyHanniMod.feature.bingo.cardDisplay) return //TODO add thys message // if (event.message == "thys message") { @@ -148,7 +158,9 @@ class BingoNextStepHelper { done = true updateResult() if (!silent) { - LorenzUtils.chat("§e[SkyHanni] A bingo goal step is done! ($displayName)") + if (SkyHanniMod.feature.bingo.stepHelper) { + LorenzUtils.chat("§e[SkyHanni] A bingo goal step is done! ($displayName)") + } } } @@ -177,11 +189,10 @@ class BingoNextStepHelper { for (goal in personalGoals) { val description = goal.description.removeColor() - val pattern = Pattern.compile("Reach ([0-9]+(?:,\\d+)*) (.*) Collection\\.") - val matcher = pattern.matcher(description) - if (matcher.matches()) { - val amount = matcher.group(1).replace(",", "").toInt() - val name = matcher.group(2) + val collectionMatcher = collectionPattern.matcher(description) + if (collectionMatcher.matches()) { + val amount = collectionMatcher.group(1).replace(",", "").toInt() + val name = collectionMatcher.group(2) val collectionStep = CollectionStep(name, amount).apply { finalSteps.add(this) } createItemIslandRequirement(name, collectionStep) @@ -201,7 +212,12 @@ class BingoNextStepHelper { "Jacob's Ticket", 32, mapOf("Jacob's Ticket" to 1) - ).apply { this requires IslandType.HUB.getStep() }.addItemRequirements() + ).apply { this requires IslandType.GARDEN.getStep() }.addItemRequirements() + } + val crystalMatcher = crystalPattern.matcher(description) + if (crystalMatcher.matches()) { + val crystal = crystalMatcher.group(1) + ChatMessageStep("Obtain a $crystal Crystal").apply { finalSteps.add(this) } requires IslandType.CRYSTAL_HOLLOWS.getStep() } println("No help for goal: '$description'") @@ -269,11 +285,14 @@ class BingoNextStepHelper { IslandType.DWARVEN_MINES.getStep() requires SkillLevelStep("Mining", 12) IslandType.CRYSTAL_HOLLOWS.getStep() requires IslandType.DWARVEN_MINES.getStep() + // TODO add skyblock level requirement +// IslandType.GARDEN.getStep() requires SkyBlockLevelStep(6) + IslandType.GARDEN.getStep() requires IslandType.HUB.getStep() + val farmingContest = ChatMessageStep("Farming Contest") farmingContest requires SkillLevelStep("Farming", 10) itemRequired["Jacob's Ticket"] = farmingContest - // enchantedCharcoal(7) // compactor(7) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt new file mode 100644 index 000000000..c0ae5d6fa --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt @@ -0,0 +1,276 @@ +package at.hannibal2.skyhanni.features.bingo + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.SendTitleHelper +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.ProfileJoinEvent +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.NEUItems +import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNeeded +import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import io.github.moulberry.notenoughupdates.NotEnoughUpdates +import io.github.moulberry.notenoughupdates.recipes.CraftingRecipe +import io.github.moulberry.notenoughupdates.recipes.NeuRecipe +import net.minecraft.client.Minecraft +import net.minecraft.item.ItemStack +import net.minecraftforge.event.world.WorldEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import java.util.regex.Pattern + +class MinionCraftHelper { + + private var minionNamePattern = Pattern.compile("(.*) Minion (.*)") + private var tick = 0 + private var display = mutableListOf<String>() + private var hasMinionInInventory = false + private var hasItemsForMinion = false + private val tierOneMinions = mutableListOf<String>() + private val tierOneMinionsDone = mutableListOf<String>() + private val allIngredients = mutableListOf<String>() + private val alreadyNotified = mutableListOf<String>() + private val recipesCache = mutableMapOf<String, List<NeuRecipe>>() + private var multiplierCache = mutableMapOf<String, Pair<String, Int>>() + + @SubscribeEvent + fun onWorldChange(event: WorldEvent.Load) { + alreadyNotified.clear() + recipesCache.clear() + } + + @SubscribeEvent + fun onTick(event: TickEvent.ClientTickEvent) { + if (event.phase != TickEvent.Phase.START) return + if (!LorenzUtils.isBingoProfile) return + if (!SkyHanniMod.feature.bingo.minionCraftHelperEnabled) return + + tick++ + + if (tick % 10 == 0) { + val mainInventory = Minecraft.getMinecraft()?.thePlayer?.inventory?.mainInventory ?: return + hasMinionInInventory = mainInventory + .mapNotNull { it?.name?.removeColor() } + .any { it.contains(" Minion ") } + } + + if (tick % (60 * 2) == 0) { + val mainInventory = Minecraft.getMinecraft()?.thePlayer?.inventory?.mainInventory ?: return + hasItemsForMinion = loadFromInventory(mainInventory).first.isNotEmpty() + } + + if (!hasMinionInInventory && !hasItemsForMinion) { + display.clear() + return + } + + if (tick % 3 != 0) return +// if (tick % 60 != 0) return + + val mainInventory = Minecraft.getMinecraft()?.thePlayer?.inventory?.mainInventory ?: return + + val (minions, otherItems) = loadFromInventory(mainInventory) + + display.clear() + for ((minionName, minionId) in minions) { + val matcher = minionNamePattern.matcher(minionName) + if (!matcher.matches()) return + val cleanName = matcher.group(1).removeColor() + val number = matcher.group(2).romanToDecimalIfNeeded() + addMinion(cleanName, number, minionId, otherItems) + } + } + + @SubscribeEvent + fun onProfileJoin(event: ProfileJoinEvent) { + tierOneMinionsDone.clear() + } + + private fun loadFromInventory(mainInventory: Array<ItemStack>): Pair<MutableMap<String, String>, MutableMap<String, Int>> { + init() + + val minions = mutableMapOf<String, String>() + val otherItems = mutableMapOf<String, Int>() + + for (item in mainInventory) { + val name = item?.name?.removeColor() ?: continue + val rawId = item.getInternalName() + if (name.contains(" Minion ")) { + minions[name] = rawId + } else { + if (!allIngredients.contains(rawId)) continue + val (itemId, multiplier) = getMultiplier(rawId) + val old = otherItems.getOrDefault(itemId, 0) + otherItems[itemId] = old + item.stackSize * multiplier + } + } + firstMinionTier(otherItems, minions) + return Pair(minions, otherItems) + } + + private fun init() { + if (tierOneMinions.isNotEmpty()) return + + allIngredients.clear() + + for (internalId in NotEnoughUpdates.INSTANCE.manager.itemInformation.keys) { + if (internalId.endsWith("_GENERATOR_1")) { + tierOneMinions.add(internalId) + } + + if (internalId.contains("_GENERATOR_")) { + for (recipe in getRecipes(internalId)) { + if (recipe !is CraftingRecipe) continue + + for (ingredient in recipe.ingredients) { + val id = ingredient.internalItemId + if (!id.contains("_GENERATOR_")) { + if (!allIngredients.contains(id)) { + allIngredients.add(id) + } + } + } + } + } + } + } + + private fun firstMinionTier(otherItems: Map<String, Int>, minions: MutableMap<String, String>) { + val help = otherItems.filter { !it.key.startsWith("WOOD_") } + val tierOneMinionsFiltered = tierOneMinions.filter { it !in tierOneMinionsDone } + for (minionId in tierOneMinionsFiltered) { + val prefix = minionId.dropLast(1) + if (minions.any { it.value.startsWith(prefix) }) { + tierOneMinionsDone.add(minionId) + } + } + for (minionId in tierOneMinionsFiltered) { + + for (recipe in getRecipes(minionId)) { + if (recipe !is CraftingRecipe) continue + if (recipe.ingredients.any { help.contains(it.internalItemId) }) { + val name = recipe.output.itemStack.name!!.removeColor() + val abc = name.replace(" I", " 0") + minions[abc] = minionId.replace("_1", "_0") + } + } + } + } + + private fun addMinion(name: String, minionTier: Int, minionId: String, otherItems: MutableMap<String, Int>) { + val nextTier = minionTier + 1 + val minionName = "§9$name Minion $nextTier" + display.add(minionName) + val nextMinionId = minionId.addOneToId() + val recipes: List<NeuRecipe> = getRecipes(nextMinionId) + for (recipe in recipes) { + if (recipe !is CraftingRecipe) continue + val output = recipe.output + val internalItemId = output.internalItemId + if (!internalItemId.contains("_GENERATOR_")) continue + val map = mutableMapOf<String, Int>() + for (input in recipe.inputs) { + val itemId = input.internalItemId + if (minionId != itemId) { + val count = input.count.toInt() + val old = map.getOrDefault(itemId, 0) + map[itemId] = old + count + } + } + var allDone = true + for ((rawId, need) in map) { + val (itemId, multiplier) = getMultiplier(rawId) + val needAmount = need * multiplier + val have = otherItems.getOrDefault(itemId, 0) + val percentage = have.toDouble() / needAmount + val itemName = NEUItems.getItemStack(rawId).name ?: "§cName??§f" + if (percentage >= 1) { + display.add(" $itemName§8: §aDONE") + otherItems[itemId] = have - needAmount + } else { + val format = LorenzUtils.formatPercentage(percentage) + val haveFormat = LorenzUtils.formatInteger(have) + val needFormat = LorenzUtils.formatInteger(needAmount) + display.add("$itemName§8: §e$format §8(§7$haveFormat§8/§7$needFormat§8)") + allDone = false + } + } + display.add(" ") + if (allDone) { + addMinion(name, nextTier, nextMinionId, otherItems) + notify(minionName) + } + } + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) { + if (!LorenzUtils.isBingoProfile) return + if (!SkyHanniMod.feature.bingo.minionCraftHelperEnabled) return + + SkyHanniMod.feature.bingo.minionCraftHelperPos.renderStrings(display, center = true) + } + + private fun getRecipes(minionId: String): List<NeuRecipe> { + if (recipesCache.contains(minionId)) { + return recipesCache[minionId]!! + } + val recipes = NEUItems.manager.getAvailableRecipesFor(minionId) + recipesCache[minionId] = recipes + return recipes + } + + private fun notify(minionName: String) { + if (alreadyNotified.contains(minionName)) return + + SendTitleHelper.sendTitle("Can craft $minionName", 3_000) + alreadyNotified.add(minionName) + } + + private fun String.addOneToId(): String { + val lastText = split("_").last() + val next = lastText.toInt() + 1 + return replace(lastText, "" + next) + } + + + private fun getMultiplier(rawId: String, tryCount: Int = 0, parent: String? = null): Pair<String, Int> { + if (multiplierCache.contains(rawId)) { + return multiplierCache[rawId]!! + } + if (tryCount == 10) { + val message = "Error reading getMultiplier for item '$rawId'" + Error(message).printStackTrace() + LorenzUtils.error(message) + return Pair(rawId, 1) + } + for (recipe in getRecipes(rawId)) { + if (recipe !is CraftingRecipe) continue + + val map = mutableMapOf<String, Int>() + for (ingredient in recipe.ingredients) { + val count = ingredient.count.toInt() + val internalItemId = ingredient.internalItemId + val old = map.getOrDefault(internalItemId, 0) + map[internalItemId] = old + count + } + if (map.size != 1) continue + val current = map.iterator().next().toPair() + val id = current.first + return if (id != parent) { + val child = getMultiplier(id, tryCount + 1, rawId) + val result = Pair(child.first, child.second * current.second) + multiplierCache[rawId] = result + result + } else { + Pair(parent, 1) + } + } + + val result = Pair(rawId, 1) + multiplierCache[rawId] = result + return result + } +} |