diff options
13 files changed, 323 insertions, 39 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index ac9617297..816907c31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ + Added **Dicer Counter** - Count RNG drops for Melon Dicer and Pumpkin Dicer. + Added **Optimal Speed** - Show the optimal speed for your current tool in the hand. (Ty MelonKingDE for the values) + Added **Warn When Close** - Warn with title and sound when the next crop milestone upgrade happens in 5 seconds. Useful for switching to a different pet for leveling. ++ Added **Money per Hour** - Displays the money per hour YOU get with YOUR crop/minute value when selling the items to bazaar. ### Features from other Mods > *The following features are only there because I want them when testing SkyHanni features without other mods present.* diff --git a/FEATURES.md b/FEATURES.md index 8b64a48c3..29f7e8881 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -180,6 +180,7 @@ + **Farming Weight and Leaderboard**, provided by the elite skyblock farmers. + **Dicer Counter** - Count RNG drops for Melon Dicer and Pumpkin Dicer. + **Warn When Close** - Warn with title and sound when the next crop milestone upgrade happens in 5 seconds. Useful for switching to a different pet for leveling. ++ **Money per Hour** - Displays the money per hour YOU get with YOUR crop/minute value when selling the items to bazaar. ## Commands - /wiki (using hypixel-skyblock.fandom.com instead of Hypixel wiki) diff --git a/build.gradle.kts b/build.gradle.kts index db11bf67f..2fe65f739 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } group = "at.hannibal2.skyhanni" -version = "0.17.Beta.16" +version = "0.17.Beta.17" // Toolchains: java { diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java index ab1641d5e..f594e9c98 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java @@ -73,7 +73,7 @@ import java.util.List; public class SkyHanniMod { public static final String MODID = "skyhanni"; - public static final String VERSION = "0.17.Beta.16"; + public static final String VERSION = "0.17.Beta.17"; public static Features feature; @@ -219,6 +219,7 @@ public class SkyHanniMod { loadModule(new GardenLevelDisplay()); loadModule(new EliteFarmingWeight()); loadModule(new DicerRngDropCounter()); + loadModule(new CropMoneyDisplay()); Commands.INSTANCE.init(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java index 782f11c19..e087b790c 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Features.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java @@ -218,6 +218,11 @@ public class Features extends Config { editOverlay(activeConfigCategory, 200, 16, garden.dicerCounterPos); return; } + + if (runnableId.equals("moneyPerHour")) { + editOverlay(activeConfigCategory, 200, 16, garden.moneyPerHourPos); + return; + } } @Expose 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 cedbc416c..837dc92e0 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java @@ -364,6 +364,43 @@ public class Garden { public Position dicerCounterPos = new Position(16, -232, false, true); @Expose + @ConfigOption(name = "Money per Hour", desc = "") + @ConfigEditorAccordion(id = 13) + public boolean moneyPerHour = false; + + @Expose + @ConfigOption(name = "Show money per Hour", desc = "Displays the money per hour YOU get with YOUR crop/minute value when selling the item to bazaar.") + @ConfigEditorBoolean + @ConfigAccordionId(id = 13) + public boolean moneyPerHourDisplay = true; + + // TODO moulconfig runnable support + @Expose + @ConfigOption(name = "Only show top", desc = "Only show the best # items.") + @ConfigEditorSlider( + minValue = 1, + maxValue = 25, + minStep = 1 + ) + @ConfigAccordionId(id = 13) + public int moneyPerHourShowOnlyBest = 5; + + // TODO moulconfig runnable support + @Expose + @ConfigOption( + name = "Always On", + desc = "Show the money/hour Display always while on the garden.") + @ConfigEditorBoolean + @ConfigAccordionId(id = 13) + public boolean moneyPerHourAlwaysOn = false; + + @Expose + @ConfigOption(name = "Dicer Counter Position", desc = "") + @ConfigEditorButton(runnableId = "moneyPerHour", buttonText = "Edit") + @ConfigAccordionId(id = 13) + public Position moneyPerHourPos = new Position(16, -232, false, true); + + @Expose @ConfigOption(name = "Plot Price", desc = "Show the price of the plot in coins when inside the Configure Plots inventory.") @ConfigEditorBoolean public boolean plotPrice = true; diff --git a/src/main/java/at/hannibal2/skyhanni/data/ApiDataLoader.kt b/src/main/java/at/hannibal2/skyhanni/data/ApiDataLoader.kt index 17cb04d35..71a5a66b8 100644 --- a/src/main/java/at/hannibal2/skyhanni/data/ApiDataLoader.kt +++ b/src/main/java/at/hannibal2/skyhanni/data/ApiDataLoader.kt @@ -107,7 +107,7 @@ class ApiDataLoader { } private fun findApiCandidatesFromOtherMods(): Map<String, String> { - LorenzUtils.consoleLog("Trying to find the API Key from the config of other mods..") + LorenzUtils.consoleLog("Trying to find the api key from the config of other mods..") val candidates = mutableMapOf<String, String>() for (mod in OtherMod.values()) { val modName = mod.modName @@ -116,6 +116,10 @@ class ApiDataLoader { val reader = APIUtil.readFile(file) try { val key = mod.readKey(reader).replace("\n", "").replace(" ", "") + if (key == "") { + LorenzUtils.consoleLog("- $modName: no api key set!") + continue + } UUID.fromString(key) candidates[modName] = key } catch (e: Throwable) { @@ -123,7 +127,7 @@ class ApiDataLoader { continue } } else { - LorenzUtils.consoleLog("- $modName: no config found!") + LorenzUtils.consoleLog("- $modName: no mod/config found!") } } return candidates 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..0c8e45ff3 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/CropMoneyDisplay.kt @@ -0,0 +1,210 @@ +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.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() + + if (ready) { + val newDisplay = drawNewDisplay() + display.clear() + display.addAll(newDisplay) + } + } + + private fun drawNewDisplay(): MutableList<List<Any>> { + val newDisplay = mutableListOf<List<Any>>() + + if (!hasCropInHand && !config.moneyPerHourAlwaysOn) return newDisplay + + newDisplay.add(Collections.singletonList("§7Money per hour when selling:")) + + var number = 0 + for ((internalName, moneyPerHour) in calculateMoneyPerHour().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 + loaded = true + + SkyHanniMod.coroutineScope.launch { + val crops = listOf( + "Wheat", + "Carrot", + "Potato", + "Pumpkin", + "Sugar Cane", + "Melon", + "Cactus", + "Cocoa Beans", + "Mushroom", + "Nether Wart", + ) + + val ignoreCheapCraftedItems = listOf( + "BREAD", + "BUILDER_BROWN_MUSHROOM", + "BUILDER_CACTUS", + "BUILDER_MELON", + "CACTUS_BOOTS", + "CACTUS_CHESTPLATE", + "CACTUS_HELMET", + "CACTUS_LEGGINGS", + "FARM_SUIT_BOOTS", + "FARM_SUIT_CHESTPLATE", + "FARM_SUIT_HELMET", + "FARM_SUIT_LEGGINGS", + "MUSHROOM_BOOTS", + "MUSHROOM_CHESTPLATE", + "MUSHROOM_HELMET", + "MUSHROOM_LEGGINGS", + "PAPER", + "POTION_AFFINITY_TALISMAN", + "PUMPKIN_BOOTS", + "PUMPKIN_CHESTPLATE", + "PUMPKIN_HELMET", + "PUMPKIN_LEGGINGS", + "SIMPLE_CARROT_CANDY", + "SPEED_TALISMAN", + ) + + val ignoreCheapItems = listOf( + "BROWN_MUSHROOM", + "CACTUS", + "CARROT_ITEM", + "ENCHANTED_BREAD", + "HAY_BLOCK", + "HUGE_MUSHROOM_1", + "HUGE_MUSHROOM_2", + "INK_SACK-3", + "MELON", + "MELON_BLOCK", + "NETHER_STALK", + "POTATO_ITEM", + "PUMPKIN", + "RED_MUSHROOM", + "SUGAR_CANE", + "WHEAT", + ) + + for ((internalName, _) in NotEnoughUpdates.INSTANCE.manager.itemInformation) { + if (ignoreCheapCraftedItems.contains(internalName)) continue + if (ignoreCheapItems.contains(internalName)) continue + // filter craftable items + if (internalName.endsWith("_BOOTS") || + internalName.endsWith("_HELMET") || + internalName.endsWith("_LEGGINGS") || + internalName.endsWith("_CHESTPLATE") || + internalName == "ENCHANTED_PAPER" + ) { + continue + } + + val (newId, amount) = NEUItems.getMultiplier(internalName) + 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/EliteFarmingWeight.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/EliteFarmingWeight.kt index a8dda617d..6676f4f77 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/EliteFarmingWeight.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/EliteFarmingWeight.kt @@ -66,7 +66,7 @@ class EliteFarmingWeight { private var lastLeaderboardUpdate = 0L private var apiError = false private var leaderboardPosition = -1 - private var bonusWeight = -1 + private var bonusWeight = -2 private var cropWeight = 0.0 private var dirtyCropWeight = false private var isLoadingWeight = false @@ -74,18 +74,23 @@ class EliteFarmingWeight { private fun update() { if (!GardenAPI.inGarden()) return - if (apiError) 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 { - bonusWeight = loadBonusWeight() + loadBonusWeight() isLoadingWeight = false } } - - display = "§6Farming Weight§7: §eLoading.." return } @@ -155,26 +160,29 @@ class EliteFarmingWeight { -1 } - private suspend fun loadBonusWeight(): Int { + private suspend fun loadBonusWeight() { + val uuid = Minecraft.getMinecraft().thePlayer.uniqueID.toString().replace("-", "") + val url = "https://elitebot.dev/api/weight/$uuid" try { - val uuid = Minecraft.getMinecraft().thePlayer.uniqueID.toString().replace("-", "") - val url = "https://elitebot.dev/api/weight/$uuid" 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 == HyPixelData.profileName) { + if (profileName == localProfile) { profileId = profileEntry.key - return profile["farming"].asJsonObject["bonus"].asInt + bonusWeight = profile["farming"].asJsonObject["bonus"].asInt + return } } + println("url: '$url'") + println("result: '$result'") } catch (e: Exception) { - apiError = true - LorenzUtils.error("[SkyHanni] Failed to load farming weight data from elitebot.dev! please report this on discord!") + println("url: '$url'") e.printStackTrace() } - return -1 + 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> { 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 44c80f381..71ebe5ea9 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenAPI.kt @@ -112,11 +112,21 @@ class GardenAPI { return -1 } - fun getCropsPerSecond(itemName: String): Int? { - if (itemName.endsWith(" Mushroom")) { - return cropsPerSecond["Mushroom"] + fun getCropsPerSecond(itemName: String): Int { + val name = itemNameToCropName(itemName) + val result = cropsPerSecond[name] + if (result == null) { + println("getCropsPerSecond is null for '$name'") + return -1 } - return cropsPerSecond[itemName] + return result + } + + 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 index d5920658f..bea64af34 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenBestCropTime.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenBestCropTime.kt @@ -47,14 +47,14 @@ class GardenBestCropTime { val color = if (isCurrent) "§e" else "" number++ if (number > config.cropMilestoneShowOnlyBest && !isCurrent) continue - val cropNameDisplay = "$number# $color$cropName" + 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)")) + display.add(Collections.singletonList("$cropNameDisplay §b$duration §7(§2$gardenExpForTier §7Exp)")) } else { - display.add(Collections.singletonList(" $cropNameDisplay §b$duration")) + display.add(Collections.singletonList("$cropNameDisplay §b$duration")) } } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorFeatures.kt index a7bee0a59..d80cdc382 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorFeatures.kt @@ -155,9 +155,14 @@ class GardenVisitorFeatures { if (i > 1 && !endReached) { val (itemName, amount) = ItemUtils.readItemAmount(line) if (itemName != null) { - val internalName: String + var internalName: String try { internalName = NEUItems.getInternalName(itemName) + // This fixes a NEU bug with §9Hay Bale (cosmetic item) + // TODO remove workaround when this is fixed in neu + if (internalName == "HAY_BALE") { + internalName = "HAY_BLOCK" + } } catch (e: NullPointerException) { val message = "internal name is null: '$itemName'" println(message) @@ -169,7 +174,7 @@ class GardenVisitorFeatures { totalPrice += price if (config.visitorShowPrice) { val format = NumberUtil.format(price) - list[i+ itemsWithSpeedCounter] = "$line §7(§6$format§7)" + list[i + itemsWithSpeedCounter] = "$line §7(§6$format§7)" } itemsCounter++ @@ -178,19 +183,18 @@ class GardenVisitorFeatures { val rawName = NEUItems.getItemStack(multiplier.first).name ?: continue val crop = rawName.removeColor() val cropAmount = multiplier.second.toLong() * amount - GardenAPI.getCropsPerSecond(crop)?.let { - val formatAmount = LorenzUtils.formatInteger(cropAmount) - val formatName = "§e${formatAmount}§7x $crop " - val formatSpeed = if (it != -1) { - val missingTimeSeconds = cropAmount / it - val duration = TimeUtils.formatDuration(missingTimeSeconds * 1000) - "in §b$duration" - } else { - "§cno speed data!" - } - itemsWithSpeedCounter++ - list.add(i + itemsWithSpeedCounter, " §7- $formatName($formatSpeed§7)") + val speed = GardenAPI.getCropsPerSecond(crop) + val formatAmount = LorenzUtils.formatInteger(cropAmount) + val formatName = "§e${formatAmount}§7x $crop " + val formatSpeed = if (speed != -1) { + val missingTimeSeconds = cropAmount / speed + val duration = TimeUtils.formatDuration(missingTimeSeconds * 1000) + "in §b$duration" + } else { + "§cno speed data!" } + itemsWithSpeedCounter++ + list.add(i + itemsWithSpeedCounter, " §7- $formatName($formatSpeed§7)") } } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt index b3f67f956..d5ebdce68 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenVisitorTimer.kt @@ -29,6 +29,9 @@ class GardenVisitorTimer { millis = TimeUtils.getMillis(rawTime) } else if (line == " Next Visitor: §r§c§lQueue Full!") { queueFull = true + } else if (line == " Next Visitor: §r§cNot Unlocked!") { + render = "" + return } matcher = patternVisitors.matcher(line) |