diff options
12 files changed, 468 insertions, 4 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index c7839939a..9b93c125b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ + Added **Visitor Timer** - Timer when the next visitor will appear, and a number how many visitors are already waiting. + Added **Visitor Notification** - Show as title and in chat when a new visitor is visiting your island. + Added **Plot Price** - Show the price of the plot in coins when inside the Configure Plots inventory. ++ Added **Garden Crop Milestone Display**. (Requires an tool with either an counter or cultivating enchantment) ### 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 b844e4d3b..a21f6e1cc 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -169,6 +169,7 @@ + **Visitor Timer** - Timer when the next visitor will appear, and a number how many visitors are already waiting. + **Visitor Notification** - Show as title and in chat when a new visitor is visiting your island. + **Plot Price** - Show the price of the plot in coins when inside the Configure Plots inventory. ++ **Garden Crop Milestone Display**. (Requires an tool with either an counter or cultivating enchantment) ## Commands - /wiki (using hypixel-skyblock.fandom.com instead of Hypixel wiki) diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java index dc29df890..9392bb7a1 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java @@ -113,6 +113,8 @@ public class SkyHanniMod { loadModule(new InventoryData()); loadModule(new TabListData()); loadModule(new RenderGuiData()); + loadModule(new GardenCropMilestones()); + loadModule(new OwnInventoryData()); //features loadModule(new BazaarOrderHelper()); @@ -204,6 +206,7 @@ public class SkyHanniMod { loadModule(new GardenInventoryNumbers()); loadModule(new GardenVisitorTimer()); loadModule(new GardenNextPlotPrice()); + loadModule(new GardenCropMilestoneDisplay()); 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 59e14e60d..61dcf0de7 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Features.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java @@ -151,6 +151,11 @@ public class Features extends Config { editOverlay(activeConfigCategory, 200, 16, garden.visitorTimerPos); return; } + + if (runnableId.equals("cropMilestone")) { + editOverlay(activeConfigCategory, 200, 16, garden.cropMilestoneDisplayPos); + 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 94a3b38cb..6ada96ef0 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Garden.java @@ -113,6 +113,27 @@ public class Garden { public boolean numberCropUpgrades = true; @Expose + @ConfigOption(name = "Crop Milestone", desc = "") + @ConfigEditorAccordion(id = 5) + public boolean cropMilestone = false; + + @Expose + @ConfigOption( + name = "Display", + desc = "Show how much more crops are needed to reach the next crop milestone. " + + "§cRequires an tool with either an counter or cultivating enchantment in the hand." + ) + @ConfigEditorBoolean + @ConfigAccordionId(id = 5) + public boolean cropMilestoneDisplay = true; + + @Expose + @ConfigOption(name = "Display Position", desc = "") + @ConfigEditorButton(runnableId = "cropMilestone", buttonText = "Edit") + @ConfigAccordionId(id = 5) + public Position cropMilestoneDisplayPos = new Position(0, 0, 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/GardenCropMilestones.kt b/src/main/java/at/hannibal2/skyhanni/data/GardenCropMilestones.kt new file mode 100644 index 000000000..291b11b81 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/GardenCropMilestones.kt @@ -0,0 +1,176 @@ +package at.hannibal2.skyhanni.data + +import at.hannibal2.skyhanni.events.CropMilestoneUpdateEvent +import at.hannibal2.skyhanni.events.InventoryOpenEvent +import at.hannibal2.skyhanni.events.ProfileJoinEvent +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.regex.Pattern + +class GardenCropMilestones { + private val overflowPattern = Pattern.compile("(?:.*) §e(.*)§6\\/(?:.*)") + private val nextTierPattern = Pattern.compile("§7Progress to Tier (.*): §e(?:.*)") + + // Add when api support is there +// @SubscribeEvent +// fun onProfileDataLoad(event: ProfileApiDataLoadedEvent) { +// val profileData = event.profileData +// for ((key, value) in profileData.entrySet()) { +// if (key.startsWith("experience_skill_")) { +// val label = key.substring(17) +// val exp = value.asLong +// gardenExp[label] = exp +// } +// } +// } + + @SubscribeEvent + fun onProfileJoin(event: ProfileJoinEvent) { + cropCounter.clear() + + cropCounter["Wheat"] = 0 + cropCounter["Carrot"] = 0 + cropCounter["Potato"] = 0 + cropCounter["Pumpkin"] = 0 + cropCounter["Sugar Cane"] = 0 + cropCounter["Melon"] = 0 + cropCounter["Cactus"] = 0 + cropCounter["Cocoa Beans"] = 0 + cropCounter["Mushroom"] = 0 + cropCounter["Nether Wart"] = 0 + } + + @SubscribeEvent + fun onInventoryOpen(event: InventoryOpenEvent) { + if (event.inventoryName != "Crop Milestones") return + + for ((_, stack) in event.inventoryItems) { + val cropName = stack.name?.removeColor() ?: continue + + val lore = stack.getLore() + var cropForTier = 0L + var next = false + for (line in lore) { + if (line.contains("Progress to Tier")) { + val matcher = nextTierPattern.matcher(line) + if (matcher.matches()) { + val nextTier = matcher.group(1).romanToDecimal() + val currentTier = nextTier - 1 + cropForTier = getCropsForTier(currentTier) + } + next = true + continue + } + if (next) { + val matcher = overflowPattern.matcher(line) + if (matcher.matches()) { + val rawNumber = matcher.group(1) + val overflow = rawNumber.formatNumber() + cropCounter[cropName] = cropForTier + overflow + } + next = false + } + } + } + + CropMilestoneUpdateEvent().postAndCatch() + } + + companion object { + val cropCounter = mutableMapOf<String, Long>() + + fun getTierForCrops(crops: Long): Int { + var tier = 0 + var totalCrops = 0L + for (tierCrops in cropMilestone) { + totalCrops += tierCrops + if (totalCrops > crops) { + return tier + } + tier++ + } + + return tier + } + + fun getCropsForTier(requestedTier: Int): Long { + var totalCrops = 0L + var tier = 0 + for (tierCrops in cropMilestone) { + totalCrops += tierCrops + tier++ + if (tier == requestedTier) { + return totalCrops + } + } + + return 0 + } + + // TODO use repo + private val cropMilestone = listOf( + 100, + 150, + 250, + 500, + 1500, + 2500, + 5000, + 5000, + 10000, + 25000, + 25000, + 25000, + 30000, + 70000, + 100000, + 200000, + 250000, + 250000, + 500000, + 1000000, + 1500000, + 2000000, + 3000000, + 4000000, + 7000000, + 10000000, + 20000000, + 25000000, + 25000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 50000000, + 100000000, + ) + } +} + +private fun String.formatNumber(): Long { + var text = replace(",", "") + val multiplier = if (text.endsWith("k")) { + text = text.substring(0, text.length - 1) + 1_000 + } else if (text.endsWith("m")) { + text = text.substring(0, text.length - 1) + 1_000_000 + } else 1 + val d = text.toDouble() + return (d * multiplier).toLong() +} diff --git a/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt b/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt new file mode 100644 index 000000000..1572468d8 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt @@ -0,0 +1,78 @@ +package at.hannibal2.skyhanni.data + +import at.hannibal2.skyhanni.events.OwnInventorItemUpdateEvent +import at.hannibal2.skyhanni.events.PacketEvent +import net.minecraft.network.play.server.S2FPacketSetSlot +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class OwnInventoryData { + +// private var itemNames = mutableMapOf<Int, String>() +// private var itemAmount = mutableMapOf<Int, Int>() +// private var counter = mutableMapOf<String, Int>() + + @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) + fun onChatPacket(event: PacketEvent.ReceiveEvent) { + val packet = event.packet + if (packet is S2FPacketSetSlot) { + val windowId = packet.func_149175_c() + if (windowId == 0) { + val item = packet.func_149174_e() ?: return + OwnInventorItemUpdateEvent(item).postAndCatch() + } + } +// if (packet is S2FPacketSetSlot) { +//// println("S2FPacketSetSlot") +// val windowId = packet.func_149175_c() +// val item = packet.func_149174_e() +// val slot = packet.func_149173_d() +// if (windowId != 0) return +// +// val name = item?.name ?: "null" +// +// val oldItem = itemNames.getOrDefault(slot, "null") +// val oldAmount = itemAmount.getOrDefault(slot, 0) +// +//// println(" ") +//// println("windowId: $windowId") +// val amount = item?.stackSize ?: 0 +// if (name == oldItem) { +// if (amount > oldAmount) { +// val diff = amount - oldAmount +//// println("added $diff $name") +// add(name, diff) +// } +// } else { +// if (name != "null") { +//// println("added new $amount $name") +// add(name, amount) +// } +// } +//// println("$slot $oldItem x$oldAmount -> $name x$amount") +// itemNames[slot] = name +// itemAmount[slot] = amount +// } + } + +// private fun add(name: String, add: Int) { +// if (name == "§fHay Bale") return +// if (name == "§fSeeds") return +// if (name.contains("Hoe")) return +// +// // TODO remove later +// if (name.contains("Mushroom")) return +// +//// println("added $add $name") +// val old = counter.getOrDefault(name, 0) +//// if (name == "§fWheat") { +//// if (old == 1502) { +//// old = 2504173 +//// } +//// } +// val new = old + add +// val format = LorenzUtils.formatInteger(new) +// println("have $name $format") +// counter[name] = new +// } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/events/CropMilestoneUpdateEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/CropMilestoneUpdateEvent.kt new file mode 100644 index 000000000..e243dadcd --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/CropMilestoneUpdateEvent.kt @@ -0,0 +1,3 @@ +package at.hannibal2.skyhanni.events + +class CropMilestoneUpdateEvent: LorenzEvent()
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/events/InventoryOpenEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/InventoryOpenEvent.kt index 50387fa18..ec740a223 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/InventoryOpenEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/InventoryOpenEvent.kt @@ -1,5 +1,12 @@ package at.hannibal2.skyhanni.events import at.hannibal2.skyhanni.data.InventoryData +import net.minecraft.item.ItemStack -class InventoryOpenEvent(val inventory: InventoryData.Inventory): LorenzEvent()
\ No newline at end of file +class InventoryOpenEvent(val inventory: InventoryData.Inventory): LorenzEvent() { + + val inventoryId: Int by lazy { inventory.windowId } + val inventoryName: String by lazy {inventory.title } + val inventorySize: Int by lazy {inventory.slotCount } + val inventoryItems: MutableMap<Int, ItemStack> by lazy {inventory.items } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/events/OwnInventorItemUpdateEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/OwnInventorItemUpdateEvent.kt new file mode 100644 index 000000000..d9b641cee --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/OwnInventorItemUpdateEvent.kt @@ -0,0 +1,5 @@ +package at.hannibal2.skyhanni.events + +import net.minecraft.item.ItemStack + +class OwnInventorItemUpdateEvent(val itemStack: ItemStack): LorenzEvent()
\ 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 new file mode 100644 index 000000000..8d57559ee --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenCropMilestoneDisplay.kt @@ -0,0 +1,162 @@ +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.CropMilestoneUpdateEvent +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.OwnInventorItemUpdateEvent +import at.hannibal2.skyhanni.events.ProfileJoinEvent +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import net.minecraft.client.Minecraft +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import java.util.* + +class GardenCropMilestoneDisplay { + private val display = mutableListOf<List<Any>>() + private var currentCrop: String? = null + private val cultivatingData = mutableMapOf<String, Int>() + private var needsInventory = true + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) { + if (!isEnabled()) return + + SkyHanniMod.feature.garden.cropMilestoneDisplayPos.renderStringsAndItems(display) + } + + @SubscribeEvent + fun onProfileJoin(event: ProfileJoinEvent) { + needsInventory = true + } + + @SubscribeEvent + fun onCropMilestoneUpdate(event: CropMilestoneUpdateEvent) { + needsInventory = false + update() + } + + @SubscribeEvent + fun onOwnInventoryItemUpdate(event: OwnInventorItemUpdateEvent) { + val itemStack = event.itemStack + val counter = readCounter(itemStack) + if (counter == -1) return + val crop = getCropTypeFromItem(itemStack) ?: return + if (cultivatingData.containsKey(crop)) { + val old = cultivatingData[crop]!! + val diff = counter - old + GardenCropMilestones.cropCounter[crop] = GardenCropMilestones.cropCounter[crop]!! + diff + if (currentCrop == crop) { + update() + } + } + cultivatingData[crop] = counter + } + + private fun readCounter(itemStack: ItemStack): Int { + if (itemStack.hasTagCompound()) { + val tag = itemStack.tagCompound + if (tag.hasKey("ExtraAttributes", 10)) { + val ea = tag.getCompoundTag("ExtraAttributes") + if (ea.hasKey("mined_crops", 99)) { + return ea.getInteger("mined_crops") + } + + // only using cultivating when no crops counter is there + if (ea.hasKey("farmed_cultivating", 99)) { + return ea.getInteger("farmed_cultivating") + } + } + } + return -1 + } + + var tick = 0 + + @SubscribeEvent + fun onTick(event: TickEvent.ClientTickEvent) { + if (event.phase != TickEvent.Phase.START) return + if (!isEnabled()) return + + if (tick++ % 5 != 0) return + + val cropInHand = getCropInHand() + if (currentCrop != cropInHand) { + currentCrop = cropInHand + update() + } + } + + private fun update() { + display.clear() + currentCrop?.let { + val crops = GardenCropMilestones.cropCounter[it] + if (crops == null) { + println("cropCounter is null for '$it'") + return + } + display.add(Collections.singletonList("§6Crop Milestones")) + + val list = mutableListOf<Any>() + + try { + val internalName = NEUItems.getInternalName(if (it == "Mushroom") "Red Mushroom" else it) + val itemStack = NEUItems.getItemStack(internalName) + list.add(itemStack) + } catch (e: NullPointerException) { + e.printStackTrace() + } + list.add(it) + display.add(list) + + 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 haveFormat = LorenzUtils.formatInteger(have) + val needFormat = LorenzUtils.formatInteger(need) + display.add(Collections.singletonList("§7Progress to Tier $nextTier§8: §e$haveFormat§8/§e$needFormat")) + + if (needsInventory) { + display.add(Collections.singletonList("§cOpen §e/cropmilestones §cto update!")) + } + } + } + + private fun getCropInHand(): String? { + val heldItem = Minecraft.getMinecraft().thePlayer.heldItem ?: return null + if (readCounter(heldItem) == -1) return null + return getCropTypeFromItem(heldItem) + } + + private fun getCropTypeFromItem(heldItem: ItemStack): String? { + val name = heldItem.name ?: return null + for ((crop, _) in GardenCropMilestones.cropCounter) { + if (name.contains(crop)) { + return crop + } + } + if (name.contains("Coco Chopper")) { + return "Cocoa Beans" + } + if (name.contains("Fungi Cutter")) { + return "Mushroom" + } + return null + } + + private fun isEnabled() = + LorenzUtils.inSkyBlock && + SkyHanniMod.feature.garden.cropMilestoneDisplay && + LorenzUtils.skyBlockIsland == IslandType.GARDEN +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt index 4faa73cff..17b987c2b 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/TabListData.kt @@ -84,12 +84,14 @@ class TabListData { if (action == S38PacketPlayerListItem.Action.UPDATE_DISPLAY_NAME) { // println("UPDATE_DISPLAY_NAME") - val listPlayer = uuidMap[id]!! + val listPlayer = uuidMap[id] + listPlayer?.let { + it.displayName = formattedName + update() + } // println("old: '" + listPlayer.displayName + "'") // println("new: '$formattedName'") - listPlayer.displayName = formattedName - update() return } } |