diff options
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt | 13 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/data/SendTitleHelper.kt | 2 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/minion/MinionCraftHelper.kt | 208 |
4 files changed, 171 insertions, 53 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index fcfc9b068..bc527e656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ + Added **Time to Kill** - Show the time it takes to kill the Slayer boss. + Added skill and collection level as item stack. + Added **Auction Highlighter** - Highlight own items that are sold in green and that are expired in red. ++ Add support for tier 1 minions in the minion craft helper. ### Garden Features + Added **Copper Price** - Show copper to coin prices inside the Sky Mart inventory. diff --git a/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt index d6b1506a3..9d620f565 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/HyPixelData.kt @@ -26,6 +26,8 @@ class HyPixelData { var stranded = false var bingo = false + var profile = "" + fun readSkyBlockArea(): String { return ScoreboardData.sidebarLinesFormatted() .firstOrNull { it.startsWith(" §7⏣ ") } @@ -61,12 +63,15 @@ class HyPixelData { val message = event.message.removeColor().lowercase() if (message.startsWith("your profile was changed to:")) { - val stripped = message.replace("your profile was changed to:", "").replace("(co-op)", "").trim() - ProfileJoinEvent(stripped).postAndCatch() + val newProfile = message.replace("your profile was changed to:", "").replace("(co-op)", "").trim() + profile = newProfile + ProfileJoinEvent(newProfile).postAndCatch() } if (message.startsWith("you are playing on profile:")) { - val stripped = message.replace("you are playing on profile:", "").replace("(co-op)", "").trim() - ProfileJoinEvent(stripped).postAndCatch() + val newProfile = message.replace("you are playing on profile:", "").replace("(co-op)", "").trim() + if (profile == newProfile) return + profile = newProfile + ProfileJoinEvent(newProfile).postAndCatch() } } diff --git a/src/main/java/at/hannibal2/skyhanni/data/SendTitleHelper.kt b/src/main/java/at/hannibal2/skyhanni/data/SendTitleHelper.kt index ec7f0b5ae..502bc2023 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/SendTitleHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/SendTitleHelper.kt @@ -14,7 +14,7 @@ class SendTitleHelper { private var endTime = 0L fun sendTitle(text: String, duration: Int) { - display = text + display = "§f$text" endTime = System.currentTimeMillis() + duration } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionCraftHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionCraftHelper.kt index 8a6a052a4..52b38e742 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionCraftHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionCraftHelper.kt @@ -1,17 +1,22 @@ package at.hannibal2.skyhanni.features.minion 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.romanToDecimal +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 @@ -22,6 +27,19 @@ class MinionCraftHelper { 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) { @@ -31,20 +49,48 @@ class MinionCraftHelper { tick++ - if (tick % 60 == 0) { + if (tick % 10 == 0) { val mainInventory = Minecraft.getMinecraft()?.thePlayer?.inventory?.mainInventory ?: return hasMinionInInventory = mainInventory .mapNotNull { it?.name?.removeColor() } .any { it.contains(" Minion ") } } - if (!hasMinionInInventory) return + 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 % 5 != 0) 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>() @@ -54,31 +100,71 @@ class MinionCraftHelper { 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) + } - 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).romanToDecimal() - addMinion(cleanName, number, minionId, 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 addMinion( - minionName: String, - minionNumber: Int, - minionId: String, - otherItems: MutableMap<String, Int> - ) { - val nextNumber = minionNumber + 1 - display.add("$minionName Minion $minionNumber -> $nextNumber") - val recipes: List<NeuRecipe> = NEUItems.manager.getAvailableUsagesFor(minionId) + 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 @@ -93,6 +179,7 @@ class MinionCraftHelper { map[itemId] = old + count } } + var allDone = true for ((rawId, need) in map) { val (itemId, multiplier) = getMultiplier(rawId) val needAmount = need * multiplier @@ -101,17 +188,20 @@ class MinionCraftHelper { val itemName = NEUItems.getItemStack(rawId).name ?: "§cName??§f" if (percentage >= 1) { display.add(" $itemName§8: §aDONE") - display.add(" ") otherItems[itemId] = have - needAmount - addMinion(minionName, minionNumber + 1, minionId.addOneToId(), otherItems) } 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)") - display.add(" ") + allDone = false } } + display.add(" ") + if (allDone) { + addMinion(name, nextTier, nextMinionId, otherItems) + notify(minionName) + } } } @@ -122,22 +212,43 @@ class MinionCraftHelper { SkyHanniMod.feature.minions.minionCraftHelperPos.renderStrings(display, center = true) } -} -private fun String.addOneToId(): String { - val lastText = split("_").last() - val next = lastText.toInt() + 1 - return replace(lastText, "" + next) -} + 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) + } -var multiplierCache = mutableMapOf<String, Pair<String, Int>>() - fun getMultiplier(rawId: String): Pair<String, Int> { - if (multiplierCache.contains(rawId)) { - return multiplierCache[rawId]!! - } - for (recipe in NEUItems.manager.getAvailableRecipesFor(rawId)) { - if (recipe is CraftingRecipe) { + 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() @@ -145,20 +256,21 @@ var multiplierCache = mutableMapOf<String, Pair<String, Int>>() val old = map.getOrDefault(internalItemId, 0) map[internalItemId] = old + count } - if (map.size == 1) { - val pair = map.iterator().next().toPair() - val id = pair.first - val amount = pair.second - - val multiplier = getMultiplier(id) - val result = Pair(multiplier.first, multiplier.second * amount) + 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 - return result + result + } else { + Pair(parent, 1) } } - } - val result = Pair(rawId, 1) - multiplierCache[rawId] = result - return result + val result = Pair(rawId, 1) + multiplierCache[rawId] = result + return result + } } |