aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/garden
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/garden')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/CropMoneyDisplay.kt171
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/DicerRngDropCounter.kt130
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/EliteFarmingWeight.kt235
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt98
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenBestCropTime.kt84
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenCropMilestoneDisplay.kt148
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenCustomKeybinds.kt47
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenDeskInSBMenu.kt51
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenInventoryNumbers.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenLevelDisplay.kt147
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenNextPlotPrice.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenOptimalSpeed.kt9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorFeatures.kt48
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt11
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/SkyMartBestProfit.kt7
15 files changed, 1016 insertions, 186 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/CropMoneyDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/CropMoneyDisplay.kt
new file mode 100644
index 000000000..8af8bf742
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/CropMoneyDisplay.kt
@@ -0,0 +1,171 @@
+package at.hannibal2.skyhanni.features.garden
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.GardenToolChangeEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.features.bazaar.BazaarApi
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc
+import at.hannibal2.skyhanni.utils.NEUItems
+import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates
+import kotlinx.coroutines.launch
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+import java.util.*
+
+class CropMoneyDisplay {
+ private val display = mutableListOf<List<Any>>()
+ private val config get() = SkyHanniMod.feature.garden
+ private var tick = 0
+ private var loaded = false
+ private var ready = false
+ private val multipliers = mutableMapOf<String, Int>()
+ private val cropNames = mutableMapOf<String, String>() // internalName -> cropName
+ private var hasCropInHand = false
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) {
+ if (!isEnabled()) return
+
+ config.moneyPerHourPos.renderStringsAndItems(display)
+ }
+
+ @SubscribeEvent
+ fun onGardenToolChange(event: GardenToolChangeEvent) {
+ val crop = if (event.isRealCrop) event.crop else null
+ hasCropInHand = crop != null
+ update()
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (!isEnabled()) return
+ if (tick++ % (20 * 5) != 0) return
+ if (!hasCropInHand && !config.moneyPerHourAlwaysOn) return
+
+ update()
+ }
+
+ private fun update() {
+ init()
+
+ val newDisplay = drawNewDisplay()
+ display.clear()
+ display.addAll(newDisplay)
+ }
+
+ private fun drawNewDisplay(): MutableList<List<Any>> {
+ val newDisplay = mutableListOf<List<Any>>()
+
+ if (!ready) {
+ newDisplay.add(Collections.singletonList("§7Money per hour when selling:"))
+ newDisplay.add(Collections.singletonList("§eLoading..."))
+ return newDisplay
+ }
+
+ if (!hasCropInHand && !config.moneyPerHourAlwaysOn) return newDisplay
+
+ newDisplay.add(Collections.singletonList("§7Money per hour when selling:"))
+
+ var number = 0
+ val map = calculateMoneyPerHour()
+ if (map.isEmpty()) {
+ newDisplay.add(Collections.singletonList("§cFarm crops to add them to this list!"))
+ } else {
+ for ((internalName, moneyPerHour) in map.sortedDesc()) {
+ number++
+ val cropName = cropNames[internalName]
+ val isCurrent = cropName == GardenAPI.cropInHand
+ if (number > config.moneyPerHourShowOnlyBest && !isCurrent) continue
+
+ val list = mutableListOf<Any>()
+ list.add("§7$number# ")
+
+ try {
+ val itemStack = NEUItems.getItemStack(internalName)
+ list.add(itemStack)
+ } catch (e: NullPointerException) {
+ e.printStackTrace()
+ }
+ val format = LorenzUtils.formatInteger(moneyPerHour.toLong())
+ val itemName = NEUItems.getItemStack(internalName).name?.removeColor() ?: continue
+ val color = if (isCurrent) "§e" else "§7"
+ list.add("$color$itemName§7: §6$format")
+
+ newDisplay.add(list)
+ }
+ }
+
+ return newDisplay
+ }
+
+ private fun calculateMoneyPerHour(): MutableMap<String, Double> {
+ val moneyPerHours = mutableMapOf<String, Double>()
+ for ((internalName, amount) in multipliers) {
+ val price = NEUItems.getPrice(internalName)
+ val cropName = cropNames[internalName]!!
+ val speed = GardenAPI.getCropsPerSecond(cropName)!!
+
+ // No speed data for item in hand
+ if (speed == -1) continue
+
+ // Price not found
+ if (price == -1.0) continue
+
+ val speedPerHr = speed.toDouble() * 60 * 60
+ val blocksPerHour = speedPerHr / amount.toDouble()
+ val moneyPerHour = price * blocksPerHour
+ moneyPerHours[internalName] = moneyPerHour
+ }
+ return moneyPerHours
+ }
+
+ private fun init() {
+ if (loaded) return
+
+ if (BazaarApi.bazaarMap.isEmpty()) {
+ LorenzUtils.debug("bz not ready for money/time!")
+ return
+ }
+
+ loaded = true
+
+ SkyHanniMod.coroutineScope.launch {
+ val crops = listOf(
+ "Wheat",
+ "Carrot",
+ "Potato",
+ "Pumpkin",
+ "Sugar Cane",
+ "Melon",
+ "Cactus",
+ "Cocoa Beans",
+ "Mushroom",
+ "Nether Wart",
+ )
+
+ for ((internalName, _) in NotEnoughUpdates.INSTANCE.manager.itemInformation) {
+ if (!BazaarApi.isBazaarItem(internalName)) continue
+ if (internalName == "ENCHANTED_PAPER") continue
+
+ val (newId, amount) = NEUItems.getMultiplier(internalName)
+ if (amount < 10) continue
+ val itemName = NEUItems.getItemStack(newId).name?.removeColor() ?: continue
+ val cropName = GardenAPI.itemNameToCropName(itemName)
+ if (crops.contains(cropName)) {
+ multipliers[internalName] = amount
+ cropNames[internalName] = cropName
+ }
+ }
+
+
+ ready = true
+ update()
+ }
+ }
+
+ private fun isEnabled() = GardenAPI.inGarden() && config.moneyPerHourDisplay
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/DicerRngDropCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/DicerRngDropCounter.kt
new file mode 100644
index 000000000..8547f4d51
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/DicerRngDropCounter.kt
@@ -0,0 +1,130 @@
+package at.hannibal2.skyhanni.features.garden
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.ConfigLoadEvent
+import at.hannibal2.skyhanni.events.GardenToolChangeEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc
+import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DicerRngDropCounter {
+ private val display = mutableListOf<String>()
+ private val drops = mutableMapOf<String, MutableMap<DropRarity, Int>>()
+ private val itemDrops = mutableListOf<ItemDrop>()
+
+ init {
+ drops["Melon"] = mutableMapOf()
+ drops["Pumpkin"] = mutableMapOf()
+
+ itemDrops.add(ItemDrop("Melon", DropRarity.UNCOMMON, "§a§lUNCOMMON DROP! §r§eDicer dropped §r§a1x §r§aEnchanted Melon§r§e!"))
+ itemDrops.add(ItemDrop("Melon", DropRarity.RARE, "§9§lRARE DROP! §r§eDicer dropped §r§a5x §r§aEnchanted Melon§r§e!"))
+ itemDrops.add(ItemDrop("Melon", DropRarity.CRAZY_RARE, "§d§lCRAZY RARE DROP! §r§eDicer dropped §r§a50x §r§aEnchanted Melon§r§e!"))
+ itemDrops.add(ItemDrop("Melon", DropRarity.PRAY_TO_RNGESUS, "§5§lPRAY TO RNGESUS DROP! §r§eDicer dropped §r§92x §r§9Enchanted Melon Block§r§e!"))
+
+ itemDrops.add(ItemDrop("Pumpkin", DropRarity.UNCOMMON, "§a§lUNCOMMON DROP! §r§eDicer dropped §r§f64x §r§fPumpkin§r§e!"))
+ itemDrops.add(ItemDrop("Pumpkin", DropRarity.RARE, "§9§lRARE DROP! §r§eDicer dropped §r§a1x §r§aEnchanted Pumpkin§r§e!"))
+ itemDrops.add(ItemDrop("Pumpkin", DropRarity.CRAZY_RARE, "§d§lCRAZY RARE DROP! §r§eDicer dropped §r§a10x §r§aEnchanted Pumpkin§r§e!"))
+ itemDrops.add(ItemDrop("Pumpkin", DropRarity.PRAY_TO_RNGESUS, "§5§lPRAY TO RNGESUS DROP! §r§eDicer dropped §r§a64x §r§aEnchanted Pumpkin§r§e!"))
+ }
+
+ enum class DropRarity(val displayName: String) {
+ UNCOMMON("§a§lUNCOMMON DROP"),
+ RARE("§9§lRARE DROP"),
+ CRAZY_RARE("§d§lCRAZY RARE DROP"),
+ PRAY_TO_RNGESUS("§5§lPRAY TO RNGESUS DROP"),
+ }
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+
+ val message = event.message
+ for (drop in itemDrops) {
+ if (message == drop.message) {
+ addDrop(drop.itemName, drop.rarity)
+ saveConfig()
+ update()
+ return
+ }
+ }
+
+ if (message.contains("§r§eDicer dropped")) {
+ LorenzUtils.debug("Unknown dicer drop message!")
+ println("Unknown dicer drop message: '$message'")
+ }
+ }
+
+ private fun update() {
+ val newDisplay = drawDisplay()
+ display.clear()
+ display.addAll(newDisplay)
+ }
+
+ private fun drawDisplay(): List<String> {
+ val help = mutableListOf<String>()
+ val items = drops[itemInHand] ?: return help
+ help.add("§7RNG Drops for $toolname§7:")
+ for ((rarity, amount) in items.sortedDesc()) {
+ val displayName = rarity.displayName
+ help.add(" §7- §e${amount}x $displayName")
+ }
+
+ return help
+ }
+
+ private var itemInHand = ""
+ private var toolname = ""
+
+ @SubscribeEvent
+ fun onGardenToolChange(event: GardenToolChangeEvent) {
+ val crop = event.crop
+ itemInHand = if (crop == "Melon" || crop == "Pumpkin") crop else ""
+ if (itemInHand != "") {
+ toolname = event.heldItem!!.name!!
+ }
+ update()
+ }
+
+ private fun addDrop(item: String, rarity: DropRarity) {
+ val map = drops[item]!!
+ val old = map[rarity] ?: 0
+ map[rarity] = old + 1
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) {
+ if (isEnabled()) {
+ SkyHanniMod.feature.garden.dicerCounterPos.renderStrings(display)
+ }
+ }
+
+ class ItemDrop(val itemName: String, val rarity: DropRarity, val message: String)
+
+ private fun saveConfig() {
+ val list = SkyHanniMod.feature.hidden.gardenDicerRngDrops
+ list.clear()
+ for (drop in drops) {
+ val itemName = drop.key
+ for ((rarity, amount) in drop.value) {
+ list[itemName + "." + rarity.name] = amount
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onConfigLoad(event: ConfigLoadEvent) {
+ for ((internalName, amount) in SkyHanniMod.feature.hidden.gardenDicerRngDrops) {
+ val split = internalName.split(".")
+ val itemName = split[0]
+ val rarityName = split[1]
+ val rarity = DropRarity.valueOf(rarityName)
+ drops[itemName]!![rarity] = amount
+ }
+ }
+
+ fun isEnabled() = GardenAPI.inGarden() && SkyHanniMod.feature.garden.dicerCounterDisplay
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/EliteFarmingWeight.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/EliteFarmingWeight.kt
new file mode 100644
index 000000000..6676f4f77
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/EliteFarmingWeight.kt
@@ -0,0 +1,235 @@
+package at.hannibal2.skyhanni.features.garden
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.api.CollectionAPI
+import at.hannibal2.skyhanni.data.HyPixelData
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.ProfileApiDataLoadedEvent
+import at.hannibal2.skyhanni.events.ProfileJoinEvent
+import at.hannibal2.skyhanni.utils.APIUtil
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.round
+import at.hannibal2.skyhanni.utils.RenderUtils.renderString
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import net.minecraft.client.Minecraft
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+
+class EliteFarmingWeight {
+
+ @SubscribeEvent
+ fun onProfileDataLoad(event: ProfileApiDataLoadedEvent) {
+ // This is still not perfect, but it's definitely better than other alternatives for the moment.
+ extraCollection.clear()
+ dirtyCropWeight = true
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) {
+ if (isEnabled()) {
+ config.eliteFarmingWeightPos.renderString(display)
+ }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ // We want to try to connect to the api again after a world switch.
+ apiError = false
+ }
+
+ var tick = 0
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (tick++ % 5 != 0) return
+ update()
+ }
+
+ @SubscribeEvent
+ fun onProfileJoin(event: ProfileJoinEvent) {
+ // Supporting profile switches
+ leaderboardPosition = -1
+ bonusWeight = -1
+ dirtyCropWeight = true
+ lastLeaderboardUpdate = 0
+ }
+
+ companion object {
+ private val config get() = SkyHanniMod.feature.garden
+ private val extraCollection = mutableMapOf<String, Long>()
+
+ private var display = ""
+ private var profileId = ""
+ private var lastLeaderboardUpdate = 0L
+ private var apiError = false
+ private var leaderboardPosition = -1
+ private var bonusWeight = -2
+ private var cropWeight = 0.0
+ private var dirtyCropWeight = false
+ private var isLoadingWeight = false
+ private var isLoadingLeaderboard = false
+
+ private fun update() {
+ if (!GardenAPI.inGarden()) return
+ if (apiError) {
+ display = "§6Farming Weight§7: §cAPI Error!"
+ return
+ }
+ if (bonusWeight == -2) {
+ display = "§6Farming Weight§7: §eLoading.."
+ return
+ }
+
+ if (bonusWeight == -1) {
+ if (!isLoadingWeight) {
+ isLoadingWeight = true
+ SkyHanniMod.coroutineScope.launch {
+ loadBonusWeight()
+ isLoadingWeight = false
+ }
+ }
+ return
+ }
+
+ val weight = getWeight()
+ val leaderboard = getLeaderboard()
+ display = "§6Farming Weight§7: $weight$leaderboard"
+ }
+
+ private fun getLeaderboard(): String {
+ if (!config.eliteFarmingWeightLeaderboard) return ""
+
+ // Fetching new leaderboard position every 10 minutes
+ if (System.currentTimeMillis() > lastLeaderboardUpdate + 600_000) {
+ if (!isLoadingLeaderboard) {
+ isLoadingLeaderboard = true
+ SkyHanniMod.coroutineScope.launch {
+ leaderboardPosition = loadLeaderboardPosition()
+ lastLeaderboardUpdate = System.currentTimeMillis()
+ isLoadingLeaderboard = false
+ }
+ }
+ }
+
+ return if (leaderboardPosition != -1) {
+ val format = LorenzUtils.formatInteger(leaderboardPosition)
+ " §7[§b#$format§7]"
+ } else {
+ if (isLoadingLeaderboard) {
+ " §7[§b#?§7]"
+ } else {
+ ""
+ }
+ }
+ }
+
+ private fun getWeight(): String {
+ if (dirtyCropWeight) {
+ val values = calculateCollectionWeight().values
+ if (values.isNotEmpty()) {
+ cropWeight = values.sum()
+ dirtyCropWeight = false
+ }
+ }
+
+ val totalWeight = (cropWeight + bonusWeight)
+ return "§e" + LorenzUtils.formatDouble(totalWeight, 2)
+ }
+
+ private fun isEnabled() = GardenAPI.inGarden() && config.eliteFarmingWeightDisplay
+
+ fun addCrop(crop: String, diff: Int) {
+ val old = extraCollection[crop] ?: 0L
+ extraCollection[crop] = old + diff
+ dirtyCropWeight = true
+ }
+
+ private suspend fun loadLeaderboardPosition() = try {
+ val uuid = Minecraft.getMinecraft().thePlayer.uniqueID.toString().replace("-", "")
+ val url = "https://elitebot.dev/api/leaderboard/rank/weight/farming/$uuid/$profileId"
+ val result = withContext(Dispatchers.IO) { APIUtil.getJSONResponse(url) }.asJsonObject
+
+ result["rank"].asInt
+ } catch (e: Exception) {
+ apiError = true
+ LorenzUtils.error("[SkyHanni] Failed to load farming weight data from elitebot.dev! please report this on discord!")
+ e.printStackTrace()
+ -1
+ }
+
+ private suspend fun loadBonusWeight() {
+ val uuid = Minecraft.getMinecraft().thePlayer.uniqueID.toString().replace("-", "")
+ val url = "https://elitebot.dev/api/weight/$uuid"
+ try {
+ val result = withContext(Dispatchers.IO) { APIUtil.getJSONResponse(url) }.asJsonObject
+ val localProfile = HyPixelData.profileName
+ for (profileEntry in result["profiles"].asJsonObject.entrySet()) {
+ val profile = profileEntry.value.asJsonObject
+ val profileName = profile["cute_name"].asString.lowercase()
+ if (profileName == localProfile) {
+ profileId = profileEntry.key
+ bonusWeight = profile["farming"].asJsonObject["bonus"].asInt
+ return
+ }
+ }
+ println("url: '$url'")
+ println("result: '$result'")
+ } catch (e: Exception) {
+ println("url: '$url'")
+ e.printStackTrace()
+ }
+ apiError = true
+ LorenzUtils.error("[SkyHanni] Failed to load farming weight data from elitebot.dev! please report this on discord!")
+ }
+
+ private fun calculateCollectionWeight(): MutableMap<String, Double> {
+ val weightPerCrop = mutableMapOf<String, Double>()
+ var totalWeight = 0.0
+ for ((cropName, factor) in factorPerCrop) {
+ val collection = getCollection(cropName)
+ val weight = (collection / factor).round(2)
+ weightPerCrop[cropName] = weight
+ totalWeight += weight
+ }
+ if (totalWeight > 0) {
+ weightPerCrop["Mushroom"] = specialMushroomWeight(weightPerCrop, totalWeight)
+ }
+ return weightPerCrop
+ }
+
+ private fun specialMushroomWeight(weightPerCrop: MutableMap<String, Double>, totalWeight: Double): Double {
+ val cactusWeight = weightPerCrop["Cactus"]!!
+ val sugarCaneWeight = weightPerCrop["Sugar Cane"]!!
+ val doubleBreakRatio = (cactusWeight + sugarCaneWeight) / totalWeight;
+ val normalRatio = (totalWeight - cactusWeight - sugarCaneWeight) / totalWeight;
+
+ val mushroomFactor = factorPerCrop["Mushroom"]!!
+ val mushroomCollection = getCollection("Mushroom")
+ return doubleBreakRatio * (mushroomCollection / (2 * mushroomFactor)) + normalRatio * (mushroomCollection / mushroomFactor)
+ }
+
+ private fun getCollection(cropName: String): Double {
+ val real = CollectionAPI.getCollectionCounter(cropName)?.second ?: 0L
+ val extra = (extraCollection[cropName] ?: 0L)
+ return (real + extra).toDouble()
+ }
+
+ private val factorPerCrop by lazy {
+ mapOf(
+ "wheat" to 100_000.0,
+ "Carrot" to 300_000.0,
+ "Potato" to 300_000.0,
+ "Sugar Cane" to 200_000.0,
+ "Nether Wart" to 250_000.0,
+ "Pumpkin" to 87_095.11,
+ "Melon" to 435_466.47,
+ "Mushroom" to 168_925.53,
+ "Cocoa Beans" to 257_214.64,
+ "Cactus" to 169_389.33,
+ )
+ }
+ }
+} \ No newline at end of file
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 a8f8e84a8..37d2e1f18 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt
@@ -1,62 +1,97 @@
package at.hannibal2.skyhanni.features.garden
+import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.data.GardenCropMilestones
import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.events.GardenToolChangeEvent
-import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.events.GuiContainerEvent
+import at.hannibal2.skyhanni.events.PacketEvent
+import at.hannibal2.skyhanni.events.ProfileJoinEvent
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
import at.hannibal2.skyhanni.utils.LorenzUtils
import net.minecraft.client.Minecraft
import net.minecraft.item.ItemStack
+import net.minecraft.network.play.client.C09PacketHeldItemChange
+import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import net.minecraftforge.fml.common.gameevent.TickEvent
class GardenAPI {
-
var tick = 0
@SubscribeEvent
+ fun onSendPacket(event: PacketEvent.SendEvent) {
+ if (!inGarden()) return
+ if (event.packet !is C09PacketHeldItemChange) return
+ checkItemInHand()
+ }
+
+ @SubscribeEvent
+ fun onCloseWindow(event: GuiContainerEvent.CloseWindowEvent) {
+ if (!inGarden()) return
+ checkItemInHand()
+ }
+
+ @SubscribeEvent
fun onTick(event: TickEvent.ClientTickEvent) {
if (event.phase != TickEvent.Phase.START) return
if (!inGarden()) return
- if (tick++ % 5 != 0) return
+ if (tick++ % 10 != 0) return
+
+ // We ignore random hypixel moments
+ Minecraft.getMinecraft().currentScreen ?: return
+ checkItemInHand()
+ }
- val crop = loadCropInHand()
+ private fun checkItemInHand() {
+ val heldItem = Minecraft.getMinecraft().thePlayer.heldItem
+ val crop = loadCropInHand(heldItem)
if (cropInHand != crop) {
cropInHand = crop
- GardenToolChangeEvent().postAndCatch()
+ GardenToolChangeEvent(crop, heldItem).postAndCatch()
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW)
+ fun onProfileJoin(event: ProfileJoinEvent) {
+ if (cropsPerSecond.isEmpty()) {
+ // TODO use enum
+ for (key in GardenCropMilestones.cropCounter.keys) {
+ cropsPerSecond[key] = -1
+ }
}
}
- private fun loadCropInHand(): String? {
- val heldItem = Minecraft.getMinecraft().thePlayer.heldItem ?: return null
- if (readCounter(heldItem) == -1) return null
- return getCropTypeFromItem(heldItem)
+ private fun loadCropInHand(heldItem: ItemStack?): String? {
+ if (heldItem == null) return null
+ return getCropTypeFromItem(heldItem, true)
}
companion object {
- // TODO use everywhere instead of IslandType.GARDEN
fun inGarden() = LorenzUtils.inSkyBlock && LorenzUtils.skyBlockIsland == IslandType.GARDEN
var cropInHand: String? = null
+ val cropsPerSecond: MutableMap<String, Int> get() = SkyHanniMod.feature.hidden.gardenCropsPerSecond
- fun getCropTypeFromItem(heldItem: ItemStack): String? {
- val name = heldItem.name ?: return null
- return getCropTypeFromItem(name)
- }
+ fun getCropTypeFromItem(item: ItemStack, withDaedalus: Boolean = false): String? {
+ val internalName = item.getInternalName()
- fun getCropTypeFromItem(itemName: String): String? {
- for ((crop, _) in GardenCropMilestones.cropCounter) {
- if (itemName.contains(crop)) {
- return crop
- }
- }
- if (itemName.contains("Coco Chopper")) {
- return "Cocoa Beans"
- }
- if (itemName.contains("Fungi Cutter")) {
- return "Mushroom"
+ return when {
+ internalName.startsWith("THEORETICAL_HOE_WHEAT") -> "Wheat"
+ internalName.startsWith("THEORETICAL_HOE_CARROT") -> "Carrot"
+ internalName.startsWith("THEORETICAL_HOE_POTATO") -> "Potato"
+ internalName.startsWith("PUMPKIN_DICER") -> "Pumpkin"
+ internalName.startsWith("THEORETICAL_HOE_CANE") -> "Sugar Cane"
+ internalName.startsWith("MELON_DICER") -> "Melon"
+ internalName == "CACTUS_KNIFE" -> "Cactus"
+ internalName == "COCO_CHOPPER" -> "Cocoa Beans"
+ internalName == "FUNGI_CUTTER" -> "Mushroom"
+ internalName.startsWith("THEORETICAL_HOE_WARTS") -> "Nether Wart"
+
+ internalName.startsWith("DAEDALUS_AXE") && withDaedalus -> "Daedalus Axe"
+
+ else -> null
}
- return null
}
fun readCounter(itemStack: ItemStack): Int {
@@ -76,5 +111,16 @@ class GardenAPI {
}
return -1
}
+
+ fun getCropsPerSecond(itemName: String): Int? {
+ return cropsPerSecond[itemNameToCropName(itemName)]
+ }
+
+ fun itemNameToCropName(itemName: String): String {
+ if (itemName == "Red Mushroom" || itemName == "Brown Mushroom") {
+ return "Mushroom"
+ }
+ return itemName
+ }
}
} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenBestCropTime.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenBestCropTime.kt
new file mode 100644
index 000000000..bea64af34
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenBestCropTime.kt
@@ -0,0 +1,84 @@
+package at.hannibal2.skyhanni.features.garden
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.GardenCropMilestones
+import at.hannibal2.skyhanni.utils.LorenzUtils.sorted
+import at.hannibal2.skyhanni.utils.TimeUtils
+import java.util.*
+
+class GardenBestCropTime {
+ val display = mutableListOf<List<Any>>()
+ val timeTillNextCrop = mutableMapOf<String, Long>()
+ private val config get() = SkyHanniMod.feature.garden
+
+ fun drawBestDisplay(currentCrop: String?) {
+ if (timeTillNextCrop.size < GardenAPI.cropsPerSecond.size) {
+ updateTimeTillNextCrop()
+ }
+
+ val gardenExp = config.cropMilestoneBestType == 0
+ val sorted = if (gardenExp) {
+ val helpMap = mutableMapOf<String, Long>()
+ for ((cropName, time) in timeTillNextCrop) {
+ val crops = GardenCropMilestones.cropCounter[cropName]!!
+ val currentTier = GardenCropMilestones.getTierForCrops(crops)
+ val gardenExpForTier = getGardenExpForTier(currentTier + 1)
+ val fakeTime = time / gardenExpForTier
+ helpMap[cropName] = fakeTime
+ }
+ helpMap.sorted()
+ } else {
+ timeTillNextCrop.sorted()
+ }
+
+ val title = if (gardenExp) "§2Garden Experience" else "§bSkyBlock Level"
+ display.add(Collections.singletonList("§eBest Crop Time §7($title§7)"))
+
+ if (sorted.isEmpty()) {
+ display.add(Collections.singletonList("§cFarm crops to add them to this list!"))
+ }
+
+ var number = 0
+ for (cropName in sorted.keys) {
+ val millis = timeTillNextCrop[cropName]!!
+ val duration = TimeUtils.formatDuration(millis)
+
+ val isCurrent = cropName == currentCrop
+ val color = if (isCurrent) "§e" else ""
+ number++
+ if (number > config.cropMilestoneShowOnlyBest && !isCurrent) continue
+ val cropNameDisplay = "§7$number# $color$cropName"
+ if (gardenExp) {
+ val crops = GardenCropMilestones.cropCounter[cropName]!!
+ val currentTier = GardenCropMilestones.getTierForCrops(crops)
+ val gardenExpForTier = getGardenExpForTier(currentTier + 1)
+ display.add(Collections.singletonList("$cropNameDisplay §b$duration §7(§2$gardenExpForTier §7Exp)"))
+ } else {
+ display.add(Collections.singletonList("$cropNameDisplay §b$duration"))
+ }
+ }
+ }
+
+ private fun getGardenExpForTier(gardenLevel: Int) = if (gardenLevel > 30) 300 else gardenLevel * 10
+
+ fun updateTimeTillNextCrop() {
+ for ((cropName, speed) in GardenAPI.cropsPerSecond) {
+ if (speed == -1) continue
+
+ val crops = GardenCropMilestones.cropCounter[cropName]!!
+ val currentTier = GardenCropMilestones.getTierForCrops(crops)
+
+ val cropsForCurrentTier = GardenCropMilestones.getCropsForTier(currentTier)
+ val nextTier = currentTier + 1
+ val cropsForNextTier = GardenCropMilestones.getCropsForTier(nextTier)
+
+ val have = crops - cropsForCurrentTier
+ val need = cropsForNextTier - cropsForCurrentTier
+
+ val missing = need - have
+ val missingTimeSeconds = missing / speed
+ val millis = missingTimeSeconds * 1000
+ timeTillNextCrop[cropName] = millis
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenCropMilestoneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenCropMilestoneDisplay.kt
index d43d2f428..1913406e8 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenCropMilestoneDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenCropMilestoneDisplay.kt
@@ -1,36 +1,54 @@
package at.hannibal2.skyhanni.features.garden
import at.hannibal2.skyhanni.SkyHanniMod
-import at.hannibal2.skyhanni.config.features.Garden
import at.hannibal2.skyhanni.data.GardenCropMilestones
+import at.hannibal2.skyhanni.data.SendTitleHelper
import at.hannibal2.skyhanni.events.*
import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.LorenzUtils.sorted
import at.hannibal2.skyhanni.utils.NEUItems
import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.SoundUti