aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/bingo
diff options
context:
space:
mode:
authorhannibal2 <24389977+hannibal00212@users.noreply.github.com>2023-03-02 02:00:00 +0100
committerhannibal2 <24389977+hannibal00212@users.noreply.github.com>2023-03-02 02:00:00 +0100
commit8de53239f61c0f424dab6cf2ae83f45feb4d232b (patch)
tree61578c327b7756b32cfd8f4e65c11ff307ee4fae /src/main/java/at/hannibal2/skyhanni/features/bingo
parent4bce581e2783726f6cc869dec5d7d6d4974e59a4 (diff)
downloadskyhanni-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')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/bingo/BingoCardDisplay.kt34
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/bingo/BingoNextStepHelper.kt39
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/bingo/MinionCraftHelper.kt276
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
+ }
+}