diff options
10 files changed, 255 insertions, 36 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc9ab43c..6dccd18af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### New Features + Added Visitor Drop Counter to track all the drops from visitors. ++ Added **Contest Time Needed** - Show the time and missing FF for every crop inside Jacob's Farming Contest inventory. ### Changes + Added Options for displays Crop Milestone and Best Crop Time. diff --git a/FEATURES.md b/FEATURES.md index fe903d810..4764893da 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -221,6 +221,7 @@ + **FF for Contest** - Show the minimum needed Farming Fortune for reaching a medal in the Jacob's Farming Contest inventory. + **yaw and pitch display** - Shows yaw and pitch with customizable precision while holding a farm tool. Automatically fades out if there is no movement for a customizable duration (Contributed by Sefer) + Warning when 6th visitors is ready (Contributed by CalMWolfs) ++ **Contest Time Needed** - Show the time and missing FF for every crop inside Jacob's Farming Contest inventory. ## Commands + **/wiki <search term>** - using hypixel-skyblock.fandom.com instead of Hypixel wiki. diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index bf86f5282..db90d9252 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -28,6 +28,7 @@ import at.hannibal2.skyhanni.features.garden.composter.ComposterOverlay import at.hannibal2.skyhanni.features.garden.composter.GardenComposterInventoryFeatures import at.hannibal2.skyhanni.features.garden.contest.FarmingContestAPI import at.hannibal2.skyhanni.features.garden.contest.JacobContestFFNeededDisplay +import at.hannibal2.skyhanni.features.garden.contest.JacobContestTimeNeeded import at.hannibal2.skyhanni.features.garden.contest.JacobFarmingContestsInventory import at.hannibal2.skyhanni.features.garden.farming.* import at.hannibal2.skyhanni.features.garden.inventory.* @@ -261,6 +262,7 @@ class SkyHanniMod { loadModule(GardenBurrowingSporesNotifier()) loadModule(WildStrawberryDyeNotification()) loadModule(JacobContestFFNeededDisplay()) + loadModule(JacobContestTimeNeeded()) loadModule(GardenYawAndPitch()) loadModule(MovementSpeedDisplay()) loadModule(ChumBucketHider()) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java b/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java index 4f4349f55..6be404745 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java @@ -1203,6 +1203,17 @@ public class Garden { public Position farmingFortuneForContestPos = new Position(180, 156, false, true); @Expose + @ConfigOption( + name = "Contest Time Needed", + desc = "Show the time and missing FF for every crop inside Jacob's Farming Contest inventory." + ) + @ConfigEditorBoolean + public boolean jacobContextTimes = true; + + @Expose + public Position jacobContextTimesPos = new Position(180, 156, false, true); + + @Expose @ConfigOption(name = "Always Finnegan", desc = "Forcefully set the Finnegan Farming Simulator perk to be active. This is useful if the auto mayor detection fails.") @ConfigEditorBoolean public boolean forcefullyEnabledAlwaysFinnegan = false; 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 64a2ecc46..1e027b6ab 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 @@ -8,6 +8,7 @@ import at.hannibal2.skyhanni.features.garden.composter.ComposterAPI.getLevel import at.hannibal2.skyhanni.utils.* import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector import at.hannibal2.skyhanni.utils.LorenzUtils.round import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators @@ -273,24 +274,13 @@ class ComposterOverlay { val fuelItem = currentFuelItem if (organicMatterItem == "" || fuelItem == "") return - val clickableList = mutableListOf<Any>() - clickableList.add("§7Per ") - for (type in TimeType.values()) { - val display = type.display - if (type == currentTimeType) { - clickableList.add("§a[$display]") - } else { - clickableList.add("§e[") - clickableList.add(Renderable.link("§e$display") { - currentTimeType = type - update() - }) - clickableList.add("§e]") - } - clickableList.add(" ") - } - newList.add(clickableList) - + newList.addSelector("§7Per ", TimeType.values(), + getName = { type -> type.display }, + isCurrent = { it == currentTimeType }, + onChange = { + currentTimeType = it + update() + }) val list = mutableListOf<Any>() list.add("§7Using: ") diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt index 27a6d1515..3f943d037 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt @@ -1,10 +1,12 @@ package at.hannibal2.skyhanni.features.garden.contest import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.InventoryOpenEvent import at.hannibal2.skyhanni.features.garden.CropType import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher import io.github.moulberry.notenoughupdates.util.SkyBlockTime import net.minecraft.item.ItemStack @@ -29,6 +31,11 @@ object FarmingContestAPI { inInventory = false } + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + inInventory = false + } + fun getSbTimeFor(text: String) = timePattern.matchMatcher(text) { val month = group("month") val monthNr = LorenzUtils.getSBMonthByName(month) @@ -62,4 +69,18 @@ object FarmingContestAPI { fun getContestAtTime(time: Long) = contests[time] fun getContestsOfType(crop: CropType) = contests.values.filter { it.crop == crop } + + fun calculateAverages(crop: CropType): Pair<Int, Map<ContestRank, Int>> { + var amount = 0 + val map = mutableMapOf<ContestRank, Int>() + for (contest in getContestsOfType(crop).associateWith { it.time }.sortedDesc().keys) { + amount++ + for ((rank, count) in contest.ranks) { + val old = map.getOrDefault(rank, 0) + map[rank] = count + old + } + if (amount == 10) break + } + return Pair(amount, map.mapValues { (_, counter) -> counter / amount }) + } }
\ No newline at end of file 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 e7b356b63..7e45a9fe3 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,7 +12,6 @@ import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList import at.hannibal2.skyhanni.utils.LorenzUtils.round -import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import net.minecraft.item.ItemStack @@ -65,7 +64,7 @@ class JacobContestFFNeededDisplay { } addAsSingletonList("") - val (size, averages) = calculateAverages(crop) + val (size, averages) = FarmingContestAPI.calculateAverages(crop) add(listOf("§7For the last §e$size ", crop.icon, "§7${crop.cropName} contests:")) for (rank in ContestRank.values()) { addAsSingletonList(getLine(rank, averages, crop)) @@ -116,26 +115,12 @@ class JacobContestFFNeededDisplay { return " ${rank.displayName}§f: §6$farmingFortune FF §7(${counter.addSeparators()} crops)" } - private fun calculateAverages(crop: CropType): Pair<Int, Map<ContestRank, Int>> { - var amount = 0 - val map = mutableMapOf<ContestRank, Int>() - for (contest in FarmingContestAPI.getContestsOfType(crop).associateWith { it.time }.sortedDesc().keys) { - amount++ - for ((rank, count) in contest.ranks) { - val old = map.getOrDefault(rank, 0) - map[rank] = count + old - } - if (amount == 10) break - } - return Pair(amount, map.mapValues { (_, counter) -> counter / amount }) - } - @SubscribeEvent fun onRenderOverlay(event: GuiRenderEvent.ChestBackgroundRenderEvent) { if (!isEnabled()) return if (!FarmingContestAPI.inInventory) return if (System.currentTimeMillis() > lastToolTipTime + 200) return - config.farmingFortuneForContestPos.renderStringsAndItems(display, posLabel = "Estimated Item Value") + config.farmingFortuneForContestPos.renderStringsAndItems(display, posLabel = "Jacob Contest Crop Data") } fun isEnabled() = LorenzUtils.inSkyBlock && config.farmingFortuneForContest 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 new file mode 100644 index 000000000..94498ed69 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestTimeNeeded.kt @@ -0,0 +1,123 @@ +package at.hannibal2.skyhanni.features.garden.contest + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LateInventoryOpenEvent +import at.hannibal2.skyhanni.features.garden.CropType +import at.hannibal2.skyhanni.features.garden.FarmingFortuneDisplay.Companion.getLatestTrueFarmingFortune +import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getLatestBlocksPerSecond +import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getSpeed +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.LorenzUtils.addSelector +import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.LorenzUtils.sorted +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import at.hannibal2.skyhanni.utils.TimeUtils +import at.hannibal2.skyhanni.utils.renderables.Renderable +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class JacobContestTimeNeeded { + private val config get() = SkyHanniMod.feature.garden + private var display = listOf<List<Any>>() + private var currentBracket = ContestRank.GOLD + + @SubscribeEvent(priority = EventPriority.LOW) + fun onLateInventoryOpen(event: LateInventoryOpenEvent) { + if (FarmingContestAPI.inInventory) { + update() + } + } + + private fun update() { + val sorted = mutableMapOf<CropType, Double>() + val map = mutableMapOf<CropType, Renderable>() + for (crop in CropType.values()) { + val speed = crop.getSpeed() + if (speed == null) { + sorted[crop] = Double.MAX_VALUE + map[crop] = Renderable.hoverTips( + "§9${crop.cropName} §cNo speed data!", + listOf("§cFarm ${crop.cropName} to show data!") + ) + continue + } + + val averages = FarmingContestAPI.calculateAverages(crop).second + if (averages.isEmpty()) { + sorted[crop] = Double.MAX_VALUE - 2 + map[crop] = Renderable.hoverTips( + "§9${crop.cropName} §cNo contest data!", + listOf( + "§cOpen more pages or participate", + "§cin a ${crop.cropName} Contest to show data!" + ) + ) + continue + } + var showLine = "" + val brackets = mutableListOf<String>() + for ((bracket, amount) in averages) { + val timeInMinutes = amount.toDouble() / speed / 60 + val formatDuration = TimeUtils.formatDuration((timeInMinutes * 60 * 1000).toLong()) + val color = if (timeInMinutes < 20) "§b" else "§c" + if (bracket == currentBracket) { + sorted[crop] = timeInMinutes + } + var bracketText = "${bracket.displayName} $color$formatDuration" + if (timeInMinutes < 20) { + showLine = "§9${crop.cropName} §b$formatDuration" + } else { + showLine = + "§9${crop.cropName} §cNo ${currentBracket.displayName} §cMedal!" + + val cropFF = crop.getLatestTrueFarmingFortune() ?: 0.0 + val blocksPerSecond = crop.getLatestBlocksPerSecond() ?: 20.0 + val cropsPerSecond = amount.toDouble() / blocksPerSecond / 60 + val ffNeeded = cropsPerSecond * 100 / 20 / crop.baseDrops + val missing = (ffNeeded - cropFF).toInt() + bracketText += " §7(${missing.addSeparators()} more FF needed!)" + } + brackets.add(bracketText) + } + map[crop] = Renderable.hoverTips(showLine, buildList { + add("§7Time Needed for §9${crop.cropName} Medals§7:") + addAll(brackets) + add("") + val cropFF = crop.getLatestTrueFarmingFortune() ?: 0.0 + add("§7Current FF: §e${(cropFF).addSeparators()}") + val bps = crop.getLatestBlocksPerSecond()?.round(1) ?: 0 + add("§7Blocks/Second: §e${bps.addSeparators()}") + + }) + } + + this.display = buildList { + addAsSingletonList("§e§lTime Needed for ${currentBracket.displayName} §eMedal!") + + addSelector("§7Bracket: ", ContestRank.values(), + getName = { type -> type.name.lowercase() }, + isCurrent = { it == currentBracket }, + onChange = { + currentBracket = it + update() + }) + addAsSingletonList("") + for (crop in sorted.sorted().keys) { + val text = map[crop]!! + add(listOf(crop.icon, text)) + } + } + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.ChestBackgroundRenderEvent) { + if (!isEnabled()) return + if (!FarmingContestAPI.inInventory) return + config.jacobContextTimesPos.renderStringsAndItems(display, posLabel = "Jacob Contest Time Needed") + } + + fun isEnabled() = LorenzUtils.inSkyBlock && config.jacobContextTimes +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt index 135335067..b7930bfd1 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt @@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.features.dungeon.DungeonData import at.hannibal2.skyhanni.test.TestBingo import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.StringUtils.toDashlessUUID +import at.hannibal2.skyhanni.utils.renderables.Renderable import io.github.moulberry.moulconfig.observer.Observer import io.github.moulberry.moulconfig.observer.Property import io.github.moulberry.notenoughupdates.mixins.AccessorGuiEditSign @@ -286,4 +287,29 @@ object LorenzUtils { else -> "Special" } } + + fun <T> MutableList<List<Any>>.addSelector( + prefix: String, + values: Array<T>, + getName: (T) -> String, + isCurrent: (T) -> Boolean, + onChange: (T) -> Unit, + ) { + val newList = mutableListOf<Any>() + newList.add(prefix) + for (entry in values) { + val display = getName(entry) + if (isCurrent(entry)) { + newList.add("§a[$display]") + } else { + newList.add("§e[") + newList.add(Renderable.link("§e$display") { + onChange(entry) + }) + newList.add("§e]") + } + newList.add(" ") + } + add(newList) + } }
\ No newline at end of file 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 4d8351083..9917799f7 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt @@ -2,12 +2,14 @@ package at.hannibal2.skyhanni.utils.renderables import at.hannibal2.skyhanni.config.core.config.gui.GuiPositionEditor import at.hannibal2.skyhanni.data.ToolTipData +import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzLogger import at.hannibal2.skyhanni.utils.NEUItems.renderOnScreen import io.github.moulberry.moulconfig.gui.GuiScreenElementWrapper import io.github.moulberry.notenoughupdates.util.Utils import net.minecraft.client.Minecraft import net.minecraft.client.gui.Gui +import net.minecraft.client.gui.GuiScreen import net.minecraft.client.gui.inventory.GuiEditSign import net.minecraft.client.renderer.GlStateManager import net.minecraft.item.ItemStack @@ -16,9 +18,10 @@ import kotlin.math.max interface Renderable { val width: Int + val height: Int fun isHovered(posX: Int, posY: Int) = Utils.getMouseX() in (posX..posX + width) - && Utils.getMouseY() in (posY..posY + 10) // TODO: adjust for variable height? + && Utils.getMouseY() in (posY..posY + height) // TODO: adjust for variable height? /** * N.B.: the offset is absolute, not relative to the position and should not be used for rendering @@ -49,6 +52,7 @@ interface Renderable { object : Renderable { override val width: Int get() = render.width + override val height = 10 private var wasDown = false @@ -62,8 +66,58 @@ interface Renderable { wasDown = isDown render.render(posX, posY) } + } + + fun hoverTips(text: String, tips: List<String>, condition: () -> Boolean = { true }): Renderable { + val render = string(text) + return object : Renderable { + override val width: Int + get() = render.width + override val height = 11 + override fun render(posX: Int, posY: Int) { + render.render(posX, posY) + if (isHovered(posX, posY)) { + if (condition() && shouldAllowLink(true)) { + renderToolTips(posX, posY, tips) + } + } + } } + } + + private fun renderToolTips(posX: Int, posY: Int, tips: List<String>, border: Int = 1) { + val x = Utils.getMouseX() - posX + 10 + val startY = Utils.getMouseY() - posY - 10 + var maxX = 0 + var y = startY + val renderer = Minecraft.getMinecraft().fontRendererObj + + GlStateManager.translate(0f, 0f, 2f) + for (line in tips) { + renderer.drawStringWithShadow( + "§f$line", + 1f + x, + 1f + y, + 0 + ) + val currentX = renderer.getStringWidth(line) + if (currentX > maxX) { + maxX = currentX + } + y += 10 + } + GlStateManager.translate(0f, 0f, -1f) + + GuiScreen.drawRect( + x - border, + startY - border, + x + maxX + 10 + border, + y + border, + LorenzColor.DARK_GRAY.toColor().rgb + ) + GlStateManager.translate(0f, 0f, -1f) + } private fun shouldAllowLink(debug: Boolean = false): Boolean { val isGuiScreen = Minecraft.getMinecraft().currentScreen != null @@ -94,6 +148,7 @@ interface Renderable { fun underlined(renderable: Renderable) = object : Renderable { override val width: Int get() = renderable.width + override val height = 10 override fun render(posX: Int, posY: Int) { Gui.drawRect(0, 10, width, 11, 0xFFFFFFFF.toInt()) @@ -106,6 +161,7 @@ interface Renderable { object : Renderable { override val width: Int get() = max(hovered.width, unhovered.width) + override val height = 10 override fun render(posX: Int, posY: Int) { if (isHovered(posX, posY) && condition() && shouldAllowLink()) @@ -118,6 +174,7 @@ interface Renderable { fun itemStack(any: ItemStack, scale: Double = 1.0) = object : Renderable { override val width: Int get() = 12 + override val height = 10 override fun render(posX: Int, posY: Int) { any.renderOnScreen(0F, 0F, scaleMultiplier = scale) @@ -127,6 +184,7 @@ interface Renderable { fun string(string: String) = object : Renderable { override val width: Int get() = Minecraft.getMinecraft().fontRendererObj.getStringWidth(string) + override val height = 10 override fun render(posX: Int, posY: Int) { Minecraft.getMinecraft().fontRendererObj.drawStringWithShadow("§f$string", 1f, 1f, 0) @@ -135,6 +193,7 @@ interface Renderable { fun placeholder(width: Int) = object : Renderable { override val width: Int = width + override val height = 10 override fun render(posX: Int, posY: Int) { } |