diff options
Diffstat (limited to 'src/main/java/at')
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java | 5 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/config/features/Garden.java | 11 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt | 4 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/garden/contest/ContestRank.kt | 11 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContest.kt | 5 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt | 65 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt | 102 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt (renamed from src/main/java/at/hannibal2/skyhanni/features/inventory/JacobFarmingContestsInventory.kt) | 29 | ||||
-rw-r--r-- | src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt | 6 |
9 files changed, 218 insertions, 20 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java index 4707bf5eb..66452e7e2 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java @@ -32,6 +32,9 @@ import at.hannibal2.skyhanni.features.garden.composter.ComposterDisplay; import at.hannibal2.skyhanni.features.garden.composter.ComposterInventoryNumbers; 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.JacobFarmingContestsInventory; import at.hannibal2.skyhanni.features.garden.farming.*; import at.hannibal2.skyhanni.features.garden.inventory.*; import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorColorNames; @@ -157,6 +160,7 @@ public class SkyHanniMod { loadModule(new BazaarApi()); loadModule(GardenAPI.INSTANCE); loadModule(new CollectionAPI()); + loadModule(FarmingContestAPI.INSTANCE); // features loadModule(new BazaarOrderHelper()); @@ -280,6 +284,7 @@ public class SkyHanniMod { loadModule(new GardenCropMilestoneFix()); loadModule(new GardenBurrowingSporesNotifier()); loadModule(new WildStrawberryDyeNotification()); + loadModule(new JacobContestFFNeededDisplay()); Commands.INSTANCE.init(); 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 392223d53..825b2b3bf 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java @@ -1013,6 +1013,17 @@ public class Garden { public boolean wildStrawberryDyeNotification = true; @Expose + @ConfigOption( + name = "FF for Contest", + desc = "Show the minimum needed Farming Fortune for reaching a medal in the Jacob's Farming Contest inventory." + ) + @ConfigEditorBoolean + public boolean farmingFortuneForContest = true; + + @Expose + public Position farmingFortuneForContestPos = 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/GardenAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt index dd149b5dd..bd393589a 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt @@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.data.ScoreboardData import at.hannibal2.skyhanni.events.* import at.hannibal2.skyhanni.features.garden.composter.ComposterOverlay +import at.hannibal2.skyhanni.features.garden.contest.FarmingContestAPI import at.hannibal2.skyhanni.features.garden.farming.GardenBestCropTime import at.hannibal2.skyhanni.features.garden.inventory.SkyMartCopperPrice import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName @@ -161,7 +162,8 @@ object GardenAPI { fun isSpeedDataEmpty() = cropsPerSecond.values.sum() < 0 - fun hideExtraGuis() = ComposterOverlay.inInventory || AnitaMedalProfit.inInventory || SkyMartCopperPrice.inInventory + fun hideExtraGuis() = ComposterOverlay.inInventory || AnitaMedalProfit.inInventory || + SkyMartCopperPrice.inInventory || FarmingContestAPI.inInventory fun clearCropSpeed() { for (type in CropType.values()) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/ContestRank.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/ContestRank.kt new file mode 100644 index 000000000..5bae5ef0d --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/ContestRank.kt @@ -0,0 +1,11 @@ +package at.hannibal2.skyhanni.features.garden.contest + +enum class ContestRank(val color: String) { + GOLD("6"), + SILVER("f"), + BRONZE("c"), + ; + + val displayName = "§$color§l$name" + val pattern = "$displayName §7\\(§bTop \\d{1,2}%§7\\): §$color(?<amount>.*)".toPattern() +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContest.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContest.kt new file mode 100644 index 000000000..03fb4b0a8 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContest.kt @@ -0,0 +1,5 @@ +package at.hannibal2.skyhanni.features.garden.contest + +import at.hannibal2.skyhanni.features.garden.CropType + +data class FarmingContest(val time: Long, val crop: CropType, val ranks: Map<ContestRank, Int>)
\ No newline at end of file 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 new file mode 100644 index 000000000..27a6d1515 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/FarmingContestAPI.kt @@ -0,0 +1,65 @@ +package at.hannibal2.skyhanni.features.garden.contest + +import at.hannibal2.skyhanni.events.GuiContainerEvent +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.StringUtils.matchMatcher +import io.github.moulberry.notenoughupdates.util.SkyBlockTime +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object FarmingContestAPI { + private val timePattern = "§a(?<month>.*) (?<day>.*)(?:rd|st|nd|th), Year (?<year>.*)".toPattern() + private val contests = mutableMapOf<Long, FarmingContest>() + private val cropPattern = "§8(?<crop>.*) Contest".toPattern() + + var inInventory = false + + @SubscribeEvent + fun onInventoryOpen(event: InventoryOpenEvent) { + if (event.inventoryName == "Your Contests") { + inInventory = true + } + } + + @SubscribeEvent + fun onInventoryClose(event: GuiContainerEvent.CloseWindowEvent) { + inInventory = false + } + + fun getSbTimeFor(text: String) = timePattern.matchMatcher(text) { + val month = group("month") + val monthNr = LorenzUtils.getSBMonthByName(month) + + val year = group("year").toInt() + val day = group("day").toInt() + SkyBlockTime(year, monthNr, day).toMillis() + } + + fun addContest(time: Long, item: ItemStack) { + contests.putIfAbsent(time, createContest(time, item)) + } + + private fun createContest(time: Long, item: ItemStack): FarmingContest { + val lore = item.getLore() + val crop = lore.firstNotNullOfOrNull { + cropPattern.matchMatcher(it) { CropType.getByName(group("crop")) } + } ?: error("Crop not found in lore!") + + val ranks = ContestRank.values().associateWith { rank -> + lore.firstNotNullOfOrNull { + rank.pattern.matchMatcher(it) { + group("amount").replace(",", "").toInt() + } + } ?: error("Farming contest rank not found in lore!") + } + + return FarmingContest(time, crop, ranks) + } + + fun getContestAtTime(time: Long) = contests[time] + + fun getContestsOfType(crop: CropType) = contests.values.filter { it.crop == crop } +}
\ 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 new file mode 100644 index 000000000..e80c0a03f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobContestFFNeededDisplay.kt @@ -0,0 +1,102 @@ +package at.hannibal2.skyhanni.features.garden.contest + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.RenderItemTooltipEvent +import at.hannibal2.skyhanni.features.garden.CropType +import at.hannibal2.skyhanni.utils.InventoryUtils +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.sortedDesc +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import kotlin.math.ceil + +class JacobContestFFNeededDisplay { + private val config get() = SkyHanniMod.feature.garden + private var display = listOf<List<Any>>() + private var lastToolTipTime = 0L + private val cache = mutableMapOf<ItemStack, List<List<Any>>>() + + @SubscribeEvent + fun onTooltip(event: RenderItemTooltipEvent) { + if (!isEnabled()) return + + if (!InventoryUtils.openInventoryName().contains("Your Contests")) return + val stack = event.stack + + val oldData = cache[stack] + if (oldData != null) { + display = oldData + lastToolTipTime = System.currentTimeMillis() + return + } + + val name = stack.name ?: return + val time = FarmingContestAPI.getSbTimeFor(name) ?: return + val contest = FarmingContestAPI.getContestAtTime(time) ?: return + + val newDisplay = drawDisplay(contest) + display = newDisplay + cache[stack] = newDisplay + lastToolTipTime = System.currentTimeMillis() + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + cache.clear() + } + + private fun drawDisplay(contest: FarmingContest) = buildList<List<Any>> { + addAsSingletonList("§6Minimum Farming Fortune needed") + addAsSingletonList("") + + val crop = contest.crop + add(listOf("For this ", crop.icon, "${crop.cropName} contest:")) + for (rank in ContestRank.values()) { + addAsSingletonList(getLine(rank, contest.ranks, crop)) + } + addAsSingletonList("") + + val (size, averages) = calculateAverages(crop) + add(listOf("For the last $size ", crop.icon, "${crop.cropName} contests:")) + for (rank in ContestRank.values()) { + addAsSingletonList(getLine(rank, averages, crop)) + } + } + + private fun getLine(rank: ContestRank, map: Map<ContestRank, Int>, crop: CropType): String { + val counter = map[rank]!! + val cropsPerSecond = counter.toDouble() / 20 / 60 + val farmingFortune = ceil(cropsPerSecond * 100 / 20 / crop.baseDrops) + return " ${rank.displayName}§f: §6${farmingFortune.addSeparators()} FF §7(${counter.addSeparators()} crops)" + } + + private fun calculateAverages(crop: CropType): Pair<Int, Map<ContestRank, Int>> { + var amount = 0 + val map = mutableMapOf<ContestRank, Int>() + for (entry in FarmingContestAPI.getContestsOfType(crop).associateWith { it.time }.sortedDesc().keys) { + amount++ + for ((rank, count) in entry.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") + } + + fun isEnabled() = LorenzUtils.inSkyBlock && config.farmingFortuneForContest +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/JacobFarmingContestsInventory.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt index 3eccc0cfe..5efe01d9e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/JacobFarmingContestsInventory.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/contest/JacobFarmingContestsInventory.kt @@ -1,4 +1,4 @@ -package at.hannibal2.skyhanni.features.inventory +package at.hannibal2.skyhanni.features.garden.contest import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.events.GuiContainerEvent @@ -11,7 +11,6 @@ import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.RenderUtils.highlight -import io.github.moulberry.notenoughupdates.util.SkyBlockTime import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @@ -25,7 +24,6 @@ class JacobFarmingContestsInventory { private val formatDay = SimpleDateFormat("dd MMMM yyyy", Locale.US) private val formatTime = SimpleDateFormat("HH:mm", Locale.US) - private val pattern = "§a(.*) (.*)(?:rd|st|nd|th), Year (.*)".toPattern() private val config get() = SkyHanniMod.feature.inventory // Render the contests a tick delayed to feel smoother @@ -59,27 +57,19 @@ class JacobFarmingContestsInventory { } else { foundEvents.add(name) } + val time = FarmingContestAPI.getSbTimeFor(name) ?: continue + FarmingContestAPI.addContest(time, item) if (config.jacobFarmingContestRealTime) { - readRealTime(name, slot) + readRealTime(time, slot) } } hideEverything = false } - private fun readRealTime(name: String, slot: Int) { - val matcher = pattern.matcher(name) - if (!matcher.matches()) return - - val month = matcher.group(1) - val day = matcher.group(2).toInt() - val year = matcher.group(3).toInt() - - val monthNr = LorenzUtils.getSBMonthByName(month) - val time = SkyBlockTime(year, monthNr, day) - val toMillis = time.toMillis() - val dayFormat = formatDay.format(toMillis) - val startTimeFormat = formatTime.format(toMillis) - val endTimeFormat = formatTime.format(toMillis + 1000 * 60 * 20) + private fun readRealTime(time: Long, slot: Int) { + val dayFormat = formatDay.format(time) + val startTimeFormat = formatTime.format(time) + val endTimeFormat = formatTime.format(time + 1000 * 60 * 20) realTime[slot] = "$dayFormat $startTimeFormat-$endTimeFormat" } @@ -137,7 +127,8 @@ class JacobFarmingContestsInventory { if (config.jacobFarmingContestHideDuplicates) { if (duplicateSlots.contains(slot)) { event.toolTip.clear() - event.toolTip.add("§7Duplicate contest!") + event.toolTip.add("§7Duplicate contest") + event.toolTip.add("§7hidden by SkyHanni!") return } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt index 659f93ecf..06187da3c 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/StringUtils.kt @@ -2,6 +2,8 @@ package at.hannibal2.skyhanni.utils import org.intellij.lang.annotations.Language import java.util.* +import java.util.regex.Matcher +import java.util.regex.Pattern object StringUtils { fun String.firstLetterUppercase(): String { @@ -56,4 +58,8 @@ object StringUtils { fun String.matchRegex(@Language("RegExp") regex: String): Boolean = regex.toRegex().matches(this) private fun String.removeAtBeginning(text: String): String = if (this.startsWith(text)) substring(text.length) else this + + // TODO find better name for this method + inline fun <T> Pattern.matchMatcher(text: String, consumer: Matcher.() -> T) = + matcher(text).let { if (it.matches()) consumer(it) else null } } |