diff options
Diffstat (limited to 'src/main/java/at/hannibal2')
18 files changed, 1415 insertions, 94 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 082ff7f7c..aaa3231f6 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -42,6 +42,7 @@ import at.hannibal2.skyhanni.features.minion.MinionCollectLogic import at.hannibal2.skyhanni.features.minion.MinionFeatures import at.hannibal2.skyhanni.features.misc.* import at.hannibal2.skyhanni.features.misc.discordrpc.DiscordRPCManager +import at.hannibal2.skyhanni.features.misc.GhostCounter import at.hannibal2.skyhanni.features.misc.items.EstimatedItemValue import at.hannibal2.skyhanni.features.misc.items.EstimatedWardrobePrice import at.hannibal2.skyhanni.features.misc.tabcomplete.PlayerTabComplete @@ -219,7 +220,7 @@ class SkyHanniMod { loadModule(BarnFishingTimer()) loadModule(CrimsonIsleReputationHelper(this)) loadModule(SharkFishCounter()) - loadModule(SkyBlockLevelGuideHelper()) + loadModule(SkyblockLevelGuideHelper()) loadModule(OdgerWaypoint()) loadModule(TiaRelayHelper()) loadModule(TiaRelayWaypoints()) @@ -297,6 +298,7 @@ class SkyHanniMod { loadModule(QuickModMenuSwitch) loadModule(ShowItemUuid()) loadModule(FrozenTreasureTracker()) + loadModule(GhostCounter) init() diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java index 9a65ead70..d80195e71 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Features.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java @@ -116,6 +116,10 @@ public class Features extends Config { public Garden garden = new Garden(); @Expose + @Category(name = "Ghost Counter", desc = "Ghost Counter settings.") + public GhostCounter ghostCounter = new GhostCounter(); + + @Expose @Category(name = "Misc", desc = "Settings without a category.") public Misc misc = new Misc(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/Storage.java b/src/main/java/at/hannibal2/skyhanni/config/Storage.java index f853d4a8d..0a63e5928 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Storage.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Storage.java @@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.features.garden.CropAccessory; import at.hannibal2.skyhanni.features.garden.CropType; import at.hannibal2.skyhanni.features.garden.fortuneguide.FarmingItems; import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward; +import at.hannibal2.skyhanni.features.misc.GhostCounter.Option; import at.hannibal2.skyhanni.utils.LorenzVec; import com.google.gson.annotations.Expose; import net.minecraft.item.ItemStack; @@ -204,6 +205,33 @@ public class Storage { } @Expose + public GhostCounter ghostCounter = new GhostCounter(); + + public static class GhostCounter { + + @Expose + public Map<Option, Double> data = new HashMap<>(); + + @Expose + public boolean ctDataImported = false; + + @Expose + public double bestiaryNextLevel = 0; + + @Expose + public double bestiaryCurrentKill = 0; + + @Expose + public double bestiaryKillNeeded = 0; + + @Expose + public double totalMF = 0; + + } + + public long nextCityProjectParticipationTime = 0L; + + @Expose public Map<String, SlayerProfitList> slayerProfitData = new HashMap<>(); public static class SlayerProfitList { @@ -231,6 +259,5 @@ public class Storage { public boolean hidden; } } - } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt index d49ddf345..ebcb72d28 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -20,6 +20,7 @@ import at.hannibal2.skyhanni.features.garden.fortuneguide.FFGuideGUI import at.hannibal2.skyhanni.features.minion.MinionFeatures import at.hannibal2.skyhanni.features.misc.CityProjectFeatures import at.hannibal2.skyhanni.features.misc.CollectionCounter +import at.hannibal2.skyhanni.features.misc.GhostCounter import at.hannibal2.skyhanni.features.misc.MarkedPlayerManager import at.hannibal2.skyhanni.features.misc.discordrpc.DiscordRPCManager import at.hannibal2.skyhanni.features.slayer.SlayerItemProfitTracker @@ -51,7 +52,7 @@ object Commands { // main commands registerCommand("sh", openMainMenu) registerCommand("skyhanni", openMainMenu) - + registerCommand("ff") { openFortuneGuide() } // for users - regular commands @@ -65,6 +66,7 @@ object Commands { registerCommand("shcropstartlocation") { GardenStartLocation.setLocationCommand() } registerCommand("shstopcityprojectreminder") { CityProjectFeatures.disable() } registerCommand("shclearslayerprofits") { SlayerItemProfitTracker.clearProfitCommand(it) } + registerCommand("shimportghostcounterdata") { GhostCounter.importCTGhostCounterData() } registerCommand("shclearfarmingitems") { clearFarmingItems() } // for users - fix bugs @@ -100,8 +102,9 @@ object Commands { registerCommand("shcopyerror") { CopyErrorCommand.command(it) } } + @JvmStatic - fun openFortuneGuide() { + fun openFortuneGuide() { if (!LorenzUtils.inSkyBlock) { LorenzUtils.chat("§cJoin Skyblock to open the fortune guide!") } else { @@ -122,9 +125,9 @@ object Commands { } private fun createCommand(function: (Array<String>) -> Unit) = - object : ProcessCommandRunnable() { - override fun processCommand(sender: ICommandSender?, args: Array<out String>) { - function(args.asList().toTypedArray()) + object : ProcessCommandRunnable() { + override fun processCommand(sender: ICommandSender?, args: Array<out String>) { + function(args.asList().toTypedArray()) + } } - } }
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/Bazaar.java b/src/main/java/at/hannibal2/skyhanni/config/features/Bazaar.java index bfc1fdd90..f357e76fd 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Bazaar.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Bazaar.java @@ -8,6 +8,11 @@ import io.github.moulberry.moulconfig.annotations.ConfigOption; public class Bazaar { @Expose + @ConfigOption(name = "Purchase Helper", desc = "Highlights the item you are trying to buy in the Bazaar.") + @ConfigEditorBoolean + public boolean purchaseHelper = true; + + @Expose @ConfigOption(name = "Order Helper", desc = "Show visual hints inside the Bazaar Manage Order view when items are ready to pickup or outbid.") @ConfigEditorBoolean public boolean orderHelper = false; diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/GhostCounter.java b/src/main/java/at/hannibal2/skyhanni/config/features/GhostCounter.java new file mode 100644 index 000000000..955253f96 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/GhostCounter.java @@ -0,0 +1,324 @@ +package at.hannibal2.skyhanni.config.features; + +import at.hannibal2.skyhanni.config.core.config.Position; +import com.google.gson.annotations.Expose; +import io.github.moulberry.moulconfig.annotations.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class GhostCounter { + + @Expose + @ConfigOption(name = "Enabled", desc = "Enable ghost counter.") + @ConfigEditorBoolean + public boolean enabled = true; + + @Expose + @ConfigOption( + name = "Display Text", + desc = "Drag text to change the appearance of the overlay." + ) + @ConfigEditorDraggableList( + exampleText = { + "§6Ghosts Counter", + " §bGhost Killed: 42", + " §bSorrow: 6", + " §bGhost since Sorrow: 1", + " §bGhosts/Sorrow: 5", + " §bVolta: 6", + " §bPlasma: 8", + " §bGhostly Boots: 1", + " §bBag Of Cash: 4", + " §bAvg Magic Find: 271", + " §bScavenger Coins: 15,000", + " §bKill Combo: 14", + " §bHighest Kill Combo: 96", + " §bSkill XP Gained: 145,648", + " §bBestiary 1: 0/10", + " §bXP/h: 810,410", + " §bKills/h: 420", + " §bETA: 14d", + " §bMoney/h: 13,420,069" + } + ) + public List<Integer> ghostDisplayText = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 9, 10, 11, 12)); + + @ConfigOption(name = "Text Formatting", desc = "") + @Accordion + @Expose + public TextFormatting textFormatting = new TextFormatting(); + + public static class TextFormatting { + + @ConfigOption(name = "§eText Formatting Info", desc = "§e%session% §ris §e§lalways §rreplaced with\n" + + "§7the count for your current session.\n" + + "§7Reset when restarting the game.\n" + + "§7You can use §e&Z §7color code to use SBA chroma") + @ConfigEditorInfoText + public boolean formatInfo = false; + + @ConfigOption(name = "Reset Formatting", desc = "Reset formatting to default text.") + @ConfigEditorButton(buttonText = "Reset") + public Runnable resetFormatting = at.hannibal2.skyhanni.features.misc.GhostCounter.INSTANCE::resetFormatting; + + @ConfigOption(name = "Export Formatting", desc = "Export current formatting to clipboard.") + @ConfigEditorButton(buttonText = "Export") + public Runnable exportFormatting = at.hannibal2.skyhanni.features.misc.GhostCounter.INSTANCE::exportFormatting; + + @ConfigOption(name = "Import Formatting", desc = "Import formatting from clipboard.") + @ConfigEditorButton(buttonText = "Import") + public Runnable importFormatting = at.hannibal2.skyhanni.features.misc.GhostCounter.INSTANCE::importFormatting; + + @Expose + @ConfigOption(name = "Title", desc = "Title Line.") + @ConfigEditorText + public String titleFormat = "&6Ghost Counter"; + + @Expose + @ConfigOption(name = "Ghost Killed", desc = "Ghost Killed line.\n§e%value% §ris replaced with\n" + + "Ghost Killed.\n" + + "§e%session% §7is replaced with Ghost killed") + @ConfigEditorText + public String ghostKilledFormat = " &6Ghost Killed: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Sorrows", desc = "Sorrows drop line.\n" + + "§e%value% §7is replaced with\nsorrows dropped.") + @ConfigEditorText + public String sorrowsFormat = " &6Sorrow: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Ghost Since Sorrow", desc = "Ghost Since Sorrow line.\n" + + "§e%value% §7is replaced with\nGhost since last sorrow drop.") + @ConfigEditorText + public String ghostSinceSorrowFormat = " &6Ghost since Sorrow: &b%value%"; + + @Expose + @ConfigOption(name = "Ghost Kill Per Sorrow", desc = "Ghost Kill Per Sorrow line.\n" + + "§e%value% §7is replaced with\naverage ghost kill per sorrow drop.") + @ConfigEditorText + public String ghostKillPerSorrowFormat = " &6Ghosts/Sorrow: &b%value%"; + + @Expose + @ConfigOption(name = "Voltas", desc = "Voltas drop line.\n" + + "§e%value% §7is replaced with\nvoltas dropped.") + @ConfigEditorText + public String voltasFormat = " &6Voltas: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Plasmas", desc = "Plasmas drop line.\n" + + "§e%value% §7is replaced with\nplasmas dropped.") + @ConfigEditorText + public String plasmasFormat = " &6Plasmas: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Ghostly Boots", desc = "Ghostly Boots drop line.\n" + + "§e%value% §7is replaced with\nGhostly Boots dropped.") + @ConfigEditorText + public String ghostlyBootsFormat = " &6Ghostly Boots: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Bag Of Cash", desc = "Bag Of Cash drop line.\n" + + "§e%value% §7is replaced with\nBag Of Cash dropped.") + @ConfigEditorText + public String bagOfCashFormat = " &6Bag Of Cash: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Average Magic Find", desc = "Average Magic Find line.\n" + + "§e%value% §7is replaced with\nAverage Magic Find.") + @ConfigEditorText + public String avgMagicFindFormat = " &6Avg Magic Find: &b%value%"; + + @Expose + @ConfigOption(name = "Scavenger Coins", desc = "Scavenger Coins line.\n" + + "§e%value% §7is replaced with\nCoins earned from kill ghosts.\nInclude: Scavenger Enchant, Scavenger Talismans, Kill Combo.") + @ConfigEditorText + public String scavengerCoinsFormat = " &6Scavenger Coins: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Kill Combo", desc = "Kill Combo line.\n" + + "§e%value% §7is replaced with\nYour current kill combo.") + @ConfigEditorText + public String killComboFormat = " &6Kill Combo: &b%value%"; + + @Expose + @ConfigOption(name = "Highest Kill Combo", desc = "Highest Kill Combo line.\n" + + "§e%value% §7is replaced with\nYour current highest kill combo.") + @ConfigEditorText + public String highestKillComboFormat = " &6Highest Kill Combo: &b%value% &7(%session%)"; + + @Expose + @ConfigOption(name = "Skill XP Gained", desc = "Skill XP Gained line.\n" + + "§e%value% §7is replaced with\nSkill XP Gained from killing Ghosts.") + @ConfigEditorText + public String skillXPGainFormat = " &6Skill XP Gained: &b%value% &7(%session%)"; + + @ConfigOption(name = "Bestiary Formatting", desc = "") + @Accordion + @Expose + public BestiaryFormatting bestiaryFormatting = new BestiaryFormatting(); + + public static class BestiaryFormatting { + + @Expose + @ConfigOption(name = "Bestiary", desc = "Bestiary Progress line.\n§e%value% §7is replaced with\n" + + "Your current progress to next level.\n" + + "§e%currentLevel% &7is replaced with your current bestiary level\n" + + "§e%nextLevel% §7is replaced with your current bestiary level +1.\n" + + "§e%value% §7is replaced with one of the text below.") + @ConfigEditorText + public String base = " &6Bestiary %currentLevel%->%nextLevel%: &b%value%"; + + @Expose + @ConfigOption(name = "No Data", desc = "Text to show when you need to open the\nBestiary Menu to gather data.") + @ConfigEditorText + public String openMenu = "§cOpen Bestiary Menu !"; + + @Expose + @ConfigOption(name = "Maxed", desc = "Text to show when your bestiary for ghost is at max level.\n" + + "§e%currentKill% §7is replaced with your current total kill.") + @ConfigEditorText + public String maxed = "%currentKill% (&c&lMaxed!)"; + + @Expose + @ConfigOption(name = "Progress to Max", desc = "Text to show progress when the §eMaxed Bestiary §7option is §aON\n" + + "§e%currentKill% §7is replaced with your current total kill.") + @ConfigEditorText + public String showMax_progress = "%currentKill%/3M (%percentNumber%%)"; + + @Expose + @ConfigOption(name = "Progress", desc = "Text to show progress when the §eMaxed Bestiary§7 option is §cOFF\n" + + "§e%currentKill% §7is replaced with how many kill you have to the next level.\n" + + "§e%killNeeded% §7is replaced with how many kill you need to reach the next level.") + @ConfigEditorText + public String progress = "%currentKill%/%killNeeded%"; + } + + + @ConfigOption(name = "XP Per Hour Formatting", desc = "") + @Accordion + @Expose + public XPHourFormatting xpHourFormatting = new XPHourFormatting(); + + public static class XPHourFormatting { + + @Expose + @ConfigOption(name = "XP/h", desc = "XP Per Hour line.\n" + + "§e%value% §7is replaced with one of the text below.") + @ConfigEditorText + public String base = " &6XP/h: &b%value%"; + + @Expose + @ConfigOption(name = "No Data", desc = "XP Per Hour line.\n§e%value% §7is replaced with\nEstimated amount of combat xp you gain per hour.") + @ConfigEditorText + public String noData = "&bN/A"; + + @Expose + @ConfigOption(name = "Paused", desc = "Text displayed next to the time \n" + + "when you are doing nothing for a given amount of seconds") + @ConfigEditorText + public String paused = "&c(PAUSED)"; + } + + + @ConfigOption(name = "ETA Formatting", desc = "") + @Accordion + @Expose + public ETAFormatting etaFormatting = new ETAFormatting(); + + public static class ETAFormatting { + @Expose + @ConfigOption(name = "ETA to next level", desc = "ETA To Next Level Line.\n" + + "§e%value% §7is replaced with one of the text below.") + @ConfigEditorText + public String base = " &6ETA: &b%value%"; + + @Expose + @ConfigOption(name = "Maxed!", desc = "So you really maxed ghost bestiary ?") + @ConfigEditorText + public String maxed = "&c&lMAXED!"; + + @Expose + @ConfigOption(name = "No Data", desc = "Start killing some ghosts !") + @ConfigEditorText + public String noData = "&bN/A"; + + @Expose + @ConfigOption(name = "Progress", desc = "Text to show progress to next level.") + @ConfigEditorText + public String progress = "&b%value%"; + + @Expose + @ConfigOption(name = "Paused", desc = "Text displayed next to the time \n" + + "when you are doing nothing for a given amount of seconds") + @ConfigEditorText + public String paused = "&c(PAUSED)"; + } + + @ConfigOption(name = "Kill Per Hour Formatting", desc = "") + @Expose + @Accordion + public KillHourFormatting killHourFormatting = new KillHourFormatting(); + + public static class KillHourFormatting { + @Expose + @ConfigOption(name = "Kill/h", desc = "Kill Per Hour line.\n§e%value% §7is replaced with\nEstimated kills per hour you get.") + @ConfigEditorText + public String base = " &6Kill/h: &b%value%"; + + @Expose + @ConfigOption(name = "No Data", desc = "Start killing some ghosts !") + @ConfigEditorText + public String noData = "&bN/A"; + + @Expose + @ConfigOption(name = "Paused", desc = "Text displayed next to the time \n" + + "when you are doing nothing for a given amount of seconds") + @ConfigEditorText + public String paused = "&c(PAUSED)"; + } + + @Expose + @ConfigOption(name = "Money Per Hour", desc = "Money Per Hour.\n§e%value% §7is replaced with\nEstimated money you get per hour\n" + + "Calculated with your kill per hour and your average magic find.") + @ConfigEditorText + public String moneyHourFormat = " &6$/h: &b%value%"; + } + + @Expose + @ConfigOption(name = "Extra space", desc = "Space between each line of text.") + @ConfigEditorSlider( + minValue = -5, + maxValue = 10, + minStep = 1) + public int extraSpace = 1; + + @Expose + @ConfigOption(name = "Pause Timer", desc = "How many seconds does it wait before pausing.") + @ConfigEditorSlider( + minValue = 1, + maxValue = 20, + minStep = 1 + ) + public int pauseTimer = 3; + + @Expose + @ConfigOption(name = "Show only in The Mist", desc = "Show the overlay only when you are in The Mist.") + @ConfigEditorBoolean + public boolean onlyOnMist = true; + + @Expose + @ConfigOption(name = "Maxed Bestiary", desc = "Show progress to max bestiary instead of next level.") + @ConfigEditorBoolean + public boolean showMax = false; + + @ConfigOption(name = "Reset", desc = "Reset the counter.") + @ConfigEditorButton(buttonText = "Reset") + public Runnable resetCounter = at.hannibal2.skyhanni.features.misc.GhostCounter.INSTANCE::reset; + + @Expose + public Position position = new Position(50, 50, false, true); + +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/Misc.java b/src/main/java/at/hannibal2/skyhanni/config/features/Misc.java index f5a08521d..f0d5b7d7b 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/Misc.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/Misc.java @@ -1,10 +1,13 @@ package at.hannibal2.skyhanni.config.features; -import at.hannibal2.skyhanni.config.core.config.Position; -import com.google.gson.annotations.Expose; +import at.hannibal2.skyhanni.config.core.config.*; +import com.google.gson.annotations.*; import io.github.moulberry.moulconfig.annotations.*; -import io.github.moulberry.moulconfig.observer.Property; -import org.lwjgl.input.Keyboard; +import io.github.moulberry.moulconfig.observer.*; +import org.lwjgl.input.*; + +import java.util.ArrayList; +import java.util.List; import java.util.ArrayList; import java.util.Arrays; @@ -263,38 +266,38 @@ public class Misc { @Expose @ConfigOption(name = "First Line", desc = "Decide what to show in the first line.") @ConfigEditorDropdown(values = { - "Nothing", - "Location", - "Purse", - "Bits", - "Stats", - "Held Item", - "Skyblock Date", - "Profile (Fruit)", - "Slayer", - "Custom", - "Dynamic", - "Crop Milestone", - "Current Pet" + "Nothing", + "Location", + "Purse", + "Bits", + "Stats", + "Held Item", + "Skyblock Date", + "Profile", + "Slayer", + "Custom", + "Dynamic", + "Crop Milestone", + "Current Pet" }) public Property<Integer> firstLine = Property.of(0); @Expose @ConfigOption(name = "Second Line", desc = "Decide what to show in the second line.") @ConfigEditorDropdown(values = { - "Nothing", - "Location", - "Purse", - "Bits", - "Stats", - "Held Item", - "Skyblock Date", - "Profile (Fruit)", - "Slayer", - "Custom", - "Dynamic", - "Crop Milestone", - "Current Pet" + "Nothing", + "Location", + "Purse", + "Bits", + "Stats", + "Held Item", + "Skyblock Date", + "Profile", + "Slayer", + "Custom", + "Dynamic", + "Crop Milestone", + "Current Pet" }) public Property<Integer> secondLine = Property.of(0); @@ -304,20 +307,20 @@ public class Misc { public Property<String> customText = Property.of(""); @Expose - @ConfigOption(name = "Dynamic", desc = "\"Dynamic\" above shows your Crop Milestone or Slayer progress while doing those, but this if you're doing neither.") + @ConfigOption(name = "Dynamic", desc = "\"Dynamic\" above shows your Crop Milestone, Slayer progress, or Stacking enchantment when possible, but this if you're doing none of them.") @ConfigEditorDropdown(values = { - "Nothing", - "Location", - "Purse", - "Bits", - "Stats", - "Held Item", - "Skyblock Date", - "Profile (Fruit)", - "Slayer", - "Custom", - "Crop Milestone", - "Current Pet" + "Nothing", + "Location", + "Purse", + "Bits", + "Stats", + "Held Item", + "Skyblock Date", + "Profile", + "Slayer", + "Custom", + "Crop Milestone", + "Current Pet" }) public Property<Integer> auto = Property.of(0); } diff --git a/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarApi.kt b/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarApi.kt index 85267240e..899001e75 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarApi.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/bazaar/BazaarApi.kt @@ -1,12 +1,21 @@ package at.hannibal2.skyhanni.features.bazaar +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.GuiContainerEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.InventoryOpenEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.OSUtils +import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.inventory.ContainerChest import net.minecraft.item.ItemStack import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent @@ -17,6 +26,7 @@ class BazaarApi { companion object { val holder = BazaarDataHolder() var inBazaarInventory = false + private var currentSearchedItem = "" fun getBazaarDataByName(name: String): BazaarData? = NEUItems.getInternalNameOrNull(name)?.let { getBazaarDataByInternalName(it) } @@ -32,6 +42,16 @@ class BazaarApi { fun isBazaarItem(internalName: String): Boolean { return NEUItems.manager.auctionManager.getBazaarInfo(internalName) != null } + + fun searchForBazaarItem(displayName: String, amount: Int = -1) { + if (!LorenzUtils.inSkyBlock) return + if (NEUItems.neuHasFocus()) return + if (LorenzUtils.noTradeMode) return + if (LorenzUtils.inDungeons || LorenzUtils.inKuudraFight) return + LorenzUtils.sendCommandToServer("bz ${displayName.removeColor()}") + if (amount != -1) OSUtils.copyToClipboard(amount.toString()) + currentSearchedItem = displayName.removeColor() + } } @SubscribeEvent @@ -39,7 +59,6 @@ class BazaarApi { inBazaarInventory = checkIfInBazaar(event) } - @SubscribeEvent fun onTick(event: TickEvent.ClientTickEvent) { if (event.phase != TickEvent.Phase.START) return @@ -50,6 +69,38 @@ class BazaarApi { } } + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (!LorenzUtils.inSkyBlock) return + if (!inBazaarInventory) return + if (!SkyHanniMod.feature.bazaar.purchaseHelper) return + if (currentSearchedItem == "") return + + if (event.gui !is GuiChest) return + val guiChest = event.gui + val chest = guiChest.inventorySlots as ContainerChest + + for (slot in chest.inventorySlots) { + if (slot == null) continue + val stack = slot.stack ?: continue + + if (chest.inventorySlots.indexOf(slot) !in 9..44) { + continue + } + + if (stack.displayName.removeColor() == currentSearchedItem) { + slot highlight LorenzColor.GREEN + } + } + } + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if ("\\[Bazaar] (Buy Order Setup!|Bought).*$currentSearchedItem.*".toRegex().matches(event.message.removeColor())) { + currentSearchedItem = "" + } + } + private fun checkIfInBazaar(event: InventoryOpenEvent): Boolean { val returnItem = event.inventorySize - 5 for ((slot, item) in event.inventoryItems) { 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 241f6cbcd..5c67ea67e 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 @@ -3,19 +3,23 @@ package at.hannibal2.skyhanni.features.garden.composter import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.data.model.ComposterUpgrade import at.hannibal2.skyhanni.events.* +import at.hannibal2.skyhanni.features.bazaar.BazaarApi import at.hannibal2.skyhanni.features.garden.GardenAPI 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 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.NEUItems +import at.hannibal2.skyhanni.utils.NumberUtil import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNeeded import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.TimeUtils import at.hannibal2.skyhanni.utils.jsonobjects.GardenJson import at.hannibal2.skyhanni.utils.renderables.Renderable import io.github.moulberry.notenoughupdates.NotEnoughUpdates @@ -389,10 +393,9 @@ class ComposterOverlay { val name = itemName.substring(0, 2) + selected + rawItemName list.add(Renderable.link("$name§r §8x${itemsNeeded.addSeparators()} §7(§6$format§7)") { onClick(internalName) - if (LorenzUtils.isControlKeyDown() && !LorenzUtils.noTradeMode) { + if (LorenzUtils.isControlKeyDown()) { inInventory = false - LorenzUtils.sendCommandToServer("bz $rawItemName") - OSUtils.copyToClipboard("${itemsNeeded.toInt()}") + BazaarApi.searchForBazaarItem(itemName, itemsNeeded.toInt()) } }) bigList.add(list) diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/OverviewPage.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/OverviewPage.kt index fcf0096a1..32578e647 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/OverviewPage.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/OverviewPage.kt @@ -67,6 +67,8 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { else -> FFStats.armorTotalFF } + var word = if (currentArmor == 0) "Armor" else "Piece" + line = if (currentArmor == 0) "§7§2Total fortune from your armor\n§2Select a piece for more info" else "§7§2Total fortune from your\n${armorItem.getItem().displayName}" var value = if (currentArmor == 0) { @@ -84,7 +86,7 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { else -> 78.75 } } - GuiRenderUtils.drawFarmingBar("§2Total Armor Fortune", line, armorFF[FFTypes.TOTAL] ?: 0, value, + GuiRenderUtils.drawFarmingBar("§2Total $word Fortune", line, armorFF[FFTypes.TOTAL] ?: 0, value, FFGuideGUI.guiLeft + 135, FFGuideGUI.guiTop + 30, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) line = if (currentArmor == 0) "§7§2The base fortune from your armor\n§2Select a piece for more info" @@ -97,7 +99,7 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { else { if (FFStats.usingSpeedBoots) 60 else 30 } - GuiRenderUtils.drawFarmingBar("§2Base Armor Fortune", line, armorFF[FFTypes.BASE] ?: 0, + GuiRenderUtils.drawFarmingBar("§2Base $word Fortune", line, armorFF[FFTypes.BASE] ?: 0, value, FFGuideGUI.guiLeft + 135, FFGuideGUI.guiTop + 55, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) @@ -106,6 +108,7 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { value = if (FFStats.usingSpeedBoots) { when (currentArmor) { 0 -> 50 + 4 -> 0 else -> 16.67 } } else { @@ -115,7 +118,7 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { } } - GuiRenderUtils.drawFarmingBar("§2Armor Ability", line, armorFF[FFTypes.ABILITY] ?: 0, + GuiRenderUtils.drawFarmingBar("§2$word Ability", line, armorFF[FFTypes.ABILITY] ?: 0, value, FFGuideGUI.guiLeft + 135, FFGuideGUI.guiTop + 80, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) @@ -126,7 +129,7 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { } else if (currentArmor == 4) { if (FFStats.usingSpeedBoots) 25 else 30 } else 30 - GuiRenderUtils.drawFarmingBar("§2Armor Reforge", line, armorFF[FFTypes.REFORGE] ?: 0, + GuiRenderUtils.drawFarmingBar("§2$word Reforge", line, armorFF[FFTypes.REFORGE] ?: 0, value, FFGuideGUI.guiLeft + 135, FFGuideGUI.guiTop + 105, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) @@ -149,6 +152,8 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { GuiRenderUtils.drawFarmingBar("§2Pet Item", line, currentPet[FFTypes.PET_ITEM] ?: 0, 60, FFGuideGUI.guiLeft + 185, FFGuideGUI.guiTop + 155, 70, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) + word = if (currentEquipment == 0) "Equipment" else "Piece" + val equipmentItem = when (currentEquipment) { 1 -> FarmingItems.NECKLACE 2 -> FarmingItems.CLOAK @@ -166,31 +171,31 @@ class OverviewPage: FFGuideGUI.FFGuidePage() { line = if (currentEquipment == 0) "§7§2Total fortune from all your equipment\n§2Select a piece for more info" else "§7§2Total fortune from your\n${equipmentItem.getItem().displayName}" - GuiRenderUtils.drawFarmingBar("§2Total Equipment Fortune", line, equipmentFF[FFTypes.TOTAL] ?: 0, + GuiRenderUtils.drawFarmingBar("§2Total $word Fortune", line, equipmentFF[FFTypes.TOTAL] ?: 0, if (currentEquipment == 0) 198 else 49.5, FFGuideGUI.guiLeft + 255, FFGuideGUI.guiTop + 30, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) line = if (currentEquipment == 0) "§7§2The base fortune from all your equipment\n§2Select a piece for more info" else "§7§2Total base fortune from your\n${equipmentItem.getItem().displayName}" - GuiRenderUtils.drawFarmingBar("§2Equipment Base Fortune", line, equipmentFF[FFTypes.BASE] ?: 0, + GuiRenderUtils.drawFarmingBar("§2$word Base Fortune", line, equipmentFF[FFTypes.BASE] ?: 0, if (currentEquipment == 0) 20 else 5, FFGuideGUI.guiLeft + 255, FFGuideGUI.guiTop + 55, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) line = if (currentEquipment == 0) "§7§2The fortune from all of your equipment's abilities\n§2Select a piece for more info" else "§7§2Total ability fortune from your\n${equipmentItem.getItem().displayName}" - GuiRenderUtils.drawFarmingBar("§2Equipment Ability", line, equipmentFF[FFTypes.ABILITY] ?: 0, + GuiRenderUtils.drawFarmingBar("§2$word Ability", line, equipmentFF[FFTypes.ABILITY] ?: 0, if (currentEquipment == 0) 60 else 15, FFGuideGUI.guiLeft + 255, FFGuideGUI.guiTop + 80, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) line = if (currentEquipment == 0) "§7§2The fortune from all of your equipment's reforges\n§2Select a piece for more info" else "§7§2Total reforge fortune from your\n${equipmentItem.getItem().displayName}" - GuiRenderUtils.drawFarmingBar("§2Equipment Reforge", line, equipmentFF[FFTypes.REFORGE] ?: 0, + GuiRenderUtils.drawFarmingBar("§2$word Reforge", line, equipmentFF[FFTypes.REFORGE] ?: 0, if (currentEquipment == 0) 40 else 10, FFGuideGUI.guiLeft + 255, FFGuideGUI.guiTop + 105, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) line = if (currentEquipment == 0) "§7§2The fortune from all of your equipment's enchantments\n§2Select a piece for more info" else "§7§2Total enchantment fortune from your\n${equipmentItem.getItem().displayName}" - GuiRenderUtils.drawFarmingBar("§2Equipment Enchantment", line, equipmentFF[FFTypes.GREEN_THUMB] ?: 0, + GuiRenderUtils.drawFarmingBar("§2$word Enchantment", line, equipmentFF[FFTypes.GREEN_THUMB] ?: 0, if (currentEquipment == 0) 78 else 19.5, FFGuideGUI.guiLeft + 255, FFGuideGUI.guiTop + 130, 90, mouseX, mouseY, FFGuideGUI.tooltipToDisplay) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/UpgradePage.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/UpgradePage.kt index 36f67eab6..e04c0dc54 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/UpgradePage.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/fortuneguide/pages/UpgradePage.kt @@ -1,13 +1,12 @@ package at.hannibal2.skyhanni.features.garden.fortuneguide.pages +import at.hannibal2.skyhanni.features.bazaar.BazaarApi import at.hannibal2.skyhanni.features.garden.fortuneguide.FFGuideGUI import at.hannibal2.skyhanni.features.garden.fortuneguide.FortuneUpgrades import at.hannibal2.skyhanni.utils.GuiRenderUtils import at.hannibal2.skyhanni.utils.ItemUtils.nameWithEnchantment -import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.NumberUtil -import at.hannibal2.skyhanni.utils.StringUtils.removeColor import net.minecraft.client.renderer.GlStateManager import net.minecraft.util.MathHelper import java.text.DecimalFormat @@ -38,9 +37,7 @@ class UpgradePage: FFGuideGUI.FFGuidePage() { var formattedUpgrade = upgradeItem.nameWithEnchantment ?: return if (adjustedY + 25 * index - 5 < FFGuideGUI.lastClickedHeight && FFGuideGUI.lastClickedHeight < adjustedY + 25 * index + 10) { FFGuideGUI.lastClickedHeight = 0 - if (!NEUItems.neuHasFocus() && !LorenzUtils.noTradeMode) { - LorenzUtils.sendCommandToServer("bz ${formattedUpgrade.removeColor()}") - } + BazaarApi.searchForBazaarItem(formattedUpgrade, upgrade.itemQuantity) } if (upgrade.itemQuantity != 1) { formattedUpgrade = "$formattedUpgrade §fx${upgrade.itemQuantity}" diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt index 973f122d5..f7fedd18c 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt @@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.data.IslandType import at.hannibal2.skyhanni.data.TitleUtils import at.hannibal2.skyhanni.events.* +import at.hannibal2.skyhanni.features.bazaar.BazaarApi import at.hannibal2.skyhanni.features.garden.CropType.Companion.getByNameOrNull import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getSpeed @@ -175,9 +176,8 @@ class GardenVisitorFeatures { list.add(Renderable.optionalLink("$name §ex${amount.addSeparators()}", { if (Minecraft.getMinecraft().currentScreen is GuiEditSign) { LorenzUtils.setTextIntoSign("$amount") - } else if (!NEUItems.neuHasFocus() && !LorenzUtils.noTradeMode) { - LorenzUtils.sendCommandToServer("bz ${name.removeColor()}") - OSUtils.copyToClipboard("$amount") + } else { + BazaarApi.searchForBazaarItem(name, amount) } }) { GardenAPI.inGarden() && !NEUItems.neuHasFocus() }) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt index 6f5678bc8..acb7d6b67 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/SackDisplay.kt @@ -186,9 +186,7 @@ class SackDisplay { add(itemStack) if (!isTrophySack) add(Renderable.optionalLink("${itemName.replace("§k", "")}: ", { - if (!NEUItems.neuHasFocus() && !LorenzUtils.noTradeMode) { - LorenzUtils.sendCommandToServer("bz ${itemName.removeColor()}") - } + BazaarApi.searchForBazaarItem(itemName) }) { !NEUItems.neuHasFocus() }) else add("${itemName.replace("§k", "")}: ") @@ -254,9 +252,7 @@ class SackDisplay { add(" §7- ") add(NEUItems.getItemStack(internalName)) add(Renderable.optionalLink("$name: ", { - if (!NEUItems.neuHasFocus() && !LorenzUtils.noTradeMode) { - LorenzUtils.sendCommandToServer("bz ${name.removeColor().dropLast(1)}") - } + BazaarApi.searchForBazaarItem(name.dropLast(1)) }) { !NEUItems.neuHasFocus() }) add(" ($rough-§a$flawed-§9$fine-§5$flawless)") val price = (roughprice + flawedprice + fineprice + flawlessprice) diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockLevelGuideHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockLevelGuideHelper.kt index 60c1a28de..16df062f8 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockLevelGuideHelper.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/SkyBlockLevelGuideHelper.kt @@ -11,7 +11,7 @@ import net.minecraft.client.gui.inventory.GuiChest import net.minecraft.inventory.ContainerChest import net.minecraftforge.fml.common.eventhandler.SubscribeEvent -class SkyBlockLevelGuideHelper { +class SkyblockLevelGuideHelper { @SubscribeEvent fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CityProjectFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CityProjectFeatures.kt index dee222aa2..d8d18ec6d 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/CityProjectFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CityProjectFeatures.kt @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.GuiContainerEvent import at.hannibal2.skyhanni.events.GuiRenderEvent import at.hannibal2.skyhanni.events.InventoryCloseEvent import at.hannibal2.skyhanni.events.InventoryOpenEvent +import at.hannibal2.skyhanni.features.bazaar.BazaarApi import at.hannibal2.skyhanni.features.garden.contest.FarmingContestAPI import at.hannibal2.skyhanni.utils.* import at.hannibal2.skyhanni.utils.ItemUtils.getLore @@ -15,7 +16,6 @@ import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators import at.hannibal2.skyhanni.utils.RenderUtils.highlight import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher -import at.hannibal2.skyhanni.utils.StringUtils.removeColor import at.hannibal2.skyhanni.utils.renderables.Renderable import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiChest @@ -136,9 +136,8 @@ class CityProjectFeatures { list.add(Renderable.optionalLink("$name §ex${amount.addSeparators()}", { if (Minecraft.getMinecraft().currentScreen is GuiEditSign) { LorenzUtils.setTextIntoSign("$amount") - } else if (!NEUItems.neuHasFocus() && !LorenzUtils.noTradeMode) { - LorenzUtils.sendCommandToServer("bz ${name.removeColor()}") - OSUtils.copyToClipboard("$amount") + } else { + BazaarApi.searchForBazaarItem(name, amount) } }) { inInventory && !NEUItems.neuHasFocus() }) diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/GhostCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/GhostCounter.kt new file mode 100644 index 000000000..0be9f0655 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/GhostCounter.kt @@ -0,0 +1,647 @@ +package at.hannibal2.skyhanni.features.misc + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigManager +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.events.* +import at.hannibal2.skyhanni.features.bazaar.BazaarApi +import at.hannibal2.skyhanni.features.misc.GhostCounter.Option.* +import at.hannibal2.skyhanni.utils.CombatUtils._isKilling +import at.hannibal2.skyhanni.utils.CombatUtils.calculateETA +import at.hannibal2.skyhanni.utils.CombatUtils.calculateXP +import at.hannibal2.skyhanni.utils.CombatUtils.interp +import at.hannibal2.skyhanni.utils.CombatUtils.isKilling +import at.hannibal2.skyhanni.utils.CombatUtils.killGainHour +import at.hannibal2.skyhanni.utils.CombatUtils.killGainHourLast +import at.hannibal2.skyhanni.utils.CombatUtils.lastKillUpdate +import at.hannibal2.skyhanni.utils.CombatUtils.lastUpdate +import at.hannibal2.skyhanni.utils.CombatUtils.xpGainHour +import at.hannibal2.skyhanni.utils.CombatUtils.xpGainHourLast +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.LorenzUtils.chat +import at.hannibal2.skyhanni.utils.LorenzUtils.clickableChat +import at.hannibal2.skyhanni.utils.NumberUtil +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber +import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import com.google.gson.JsonArray +import com.google.gson.JsonParser +import com.google.gson.JsonPrimitive +import io.github.moulberry.notenoughupdates.util.Utils +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import net.minecraftforge.fml.common.gameevent.TickEvent +import java.awt.Toolkit +import java.awt.datatransfer.DataFlavor +import java.awt.datatransfer.StringSelection +import java.io.File +import java.io.FileReader +import java.nio.charset.StandardCharsets +import java.text.NumberFormat +import java.util.* +import java.util.regex.Pattern +import kotlin.math.roundToInt +import kotlin.math.roundToLong + +object GhostCounter { + + val config get() = SkyHanniMod.feature.ghostCounter + val hidden get() = ProfileStorageData.profileSpecific?.ghostCounter + private var display = listOf<List<Any>>() + private var ghostCounterV3File = File("." + File.separator + "config" + File.separator + "ChatTriggers" + File.separator + "modules" + File.separator + "GhostCounterV3" + File.separator + ".persistantData.json") + private val skillXPPattern = ".*§3\\+(?<gained>.*) .* \\((?<total>.*)\\/(?<current>.*)\\).*".toPattern() + private val killComboExpiredPattern = "§cYour Kill Combo has expired! You reached a (?<combo>.*) Kill Combo!".toPattern() + private val ghostXPPattern = "(?<current>\\d+(?:\\.\\d+)?(?:,\\d+)?[kK]?)\\/(?<total>\\d+(?:\\.\\d+)?(?:,\\d+)?[kKmM]?)".toPattern() + private val bestiaryPattern = "BESTIARY Ghost .*➜(?<newLevel>.*)".toPattern() + private val format = NumberFormat.getIntegerInstance() + private const val exportPrefix = "gc/" + private var tick = 0 + private var lastXp: String = "0" + private var gain: Int = 0 + private var num: Double = 0.0 + private var inMist = false + private var notifyCTModule = true + var bestiaryCurrentKill = 0 + private var killETA = "" + private var session = mutableMapOf( + KILLS to 0.0, + SORROWCOUNT to 0.0, + VOLTACOUNT to 0.0, + PLASMACOUNT to 0.0, + GHOSTLYBOOTS to 0.0, + BAGOFCASH to 0.0, + TOTALDROPS to 0.0, + SCAVENGERCOINS to 0.0, + MAXKILLCOMBO to 0.0, + SKILLXPGAINED to 0.0 + ) + val bestiaryData = mutableMapOf<Int, Int>().apply { + val commonValue = 100_000 + for (i in 1..46) { + this[i] = when (i) { + 1 -> 10 + 2 -> 15 + 3 -> 75 + 4 -> 150 + 5 -> 250 + 6 -> 500 + 7 -> 1_500 + 8 -> 2_500 + 9 -> 5_000 + 10 -> 15_000 + 11 -> 25_000 + 12 -> 50_000 + else -> commonValue + } + } + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) { + if (!isEnabled()) return + if (config.onlyOnMist && !inMist) return + config.position.renderStringsAndItems(display, + extraSpace = config.extraSpace, + posLabel = "Ghost Counter") + } + + private fun formatDisplay(map: List<List<Any>>): List<List<Any>> { + val newList = mutableListOf<List<Any>>() + for (index in config.ghostDisplayText) { + newList.add(map[index]) + } + return newList + } + + fun update() { + display = formatDisplay(drawDisplay()) + } + + private fun drawDisplay() = buildList<List<Any>> { + val value: Int = when (SORROWCOUNT.get()) { + 0.0 -> 0 + else -> "${((((KILLS.get() / SORROWCOUNT.get()) + Math.ulp(1.0)) * 100) / 100).roundToInt()}".toInt() + } + val mgc = when (TOTALDROPS.get()) { + 0.0 -> "0" + else -> "${((((hidden?.totalMF!! / TOTALDROPS.get()) + Math.ulp(1.0)) * 100) / 100).roundToPrecision(2)}" + } + + val xpHourFormatting = config.textFormatting.xpHourFormatting + val xp: String + val xpInterp: Float + if (xpGainHourLast == xpGainHour && xpGainHour <= 0) { + xp = xpHourFormatting.noData + } else { + xpInterp = interp(xpGainHour, xpGainHourLast, lastUpdate) + xp = "${format.format(xpInterp)} ${if (isKilling) "" else xpHourFormatting.paused}" + } + + val killHourFormatting = config.textFormatting.killHourFormatting + val killh: String + var killInterp: Long = 0 + if (killGainHourLast == killGainHour && killGainHour <= 0) { + killh = killHourFormatting.noData + } else { + killInterp = interp(killGainHour.toFloat(), killGainHourLast.toFloat(), lastKillUpdate).toLong() + killh = "${format.format(killInterp)} ${if (_isKilling) "" else killHourFormatting.paused}" + } + + + val bestiaryFormatting = config.textFormatting.bestiaryFormatting + val currentKill = hidden?.bestiaryCurrentKill?.toInt() ?: 0 + val killNeeded = hidden?.bestiaryKillNeeded?.toInt() ?: 0 + val nextLevel = hidden?.bestiaryNextLevel?.toInt() ?: 0 + val bestiary = if (config.showMax) { + when (nextLevel) { + -1 -> bestiaryFormatting.maxed + in 1..46 -> { + val sum = bestiaryData.filterKeys { it <= nextLevel - 1 }.values.sum() + val cKill = sum + currentKill + bestiaryCurrentKill = cKill + bestiaryFormatting.showMax_progress + } + + else -> bestiaryFormatting.openMenu + } + } else { + when (nextLevel) { + -1 -> bestiaryFormatting.maxed + in 1..46 -> bestiaryFormatting.progress + else -> bestiaryFormatting.openMenu + } + } + + val etaFormatting = config.textFormatting.etaFormatting + val remaining: Int = when (config.showMax) { + true -> 3_000_000 - bestiaryCurrentKill + false -> killNeeded - currentKill + } + + val eta = if (remaining < 0) { + etaFormatting.maxed + } else { + if (killGainHour < 1) { + etaFormatting.noData + } else { + killETA = Utils.prettyTime(remaining.toLong() * 1000 * 60 * 60 / killInterp) + etaFormatting.progress + if (_isKilling) "" else etaFormatting.paused + } + } + + addAsSingletonList(Utils.chromaStringByColourCode(config.textFormatting.titleFormat.replace("&", "§"))) + addAsSingletonList(config.textFormatting.ghostKilledFormat.formatText(KILLS.getInt(), KILLS.getInt(true))) + addAsSingletonList(config.textFormatting.sorrowsFormat.formatText(SORROWCOUNT.getInt(), SORROWCOUNT.getInt(true))) + addAsSingletonList(config.textFormatting.ghostSinceSorrowFormat.formatText(GHOSTSINCESORROW.getInt())) + addAsSingletonList(config.textFormatting.ghostKillPerSorrowFormat.formatText(value)) + addAsSingletonList(config.textFormatting.voltasFormat.formatText(VOLTACOUNT.getInt(), VOLTACOUNT.getInt(true))) + addAsSingletonList(config.textFormatting.plasmasFormat.formatText(PLASMACOUNT.getInt(), PLASMACOUNT.getInt(true))) + addAsSingletonList(config.textFormatting.ghostlyBootsFormat.formatText(GHOSTLYBOOTS.getInt(), GHOSTLYBOOTS.getInt(true))) + addAsSingletonList(config.textFormatting.bagOfCashFormat.formatText(BAGOFCASH.getInt(), BAGOFCASH.getInt(true))) + addAsSingletonList(config.textFormatting.avgMagicFindFormat.formatText(mgc)) + addAsSingletonList(config.textFormatting.scavengerCoinsFormat.formatText(SCAVENGERCOINS.getInt(), SCAVENGERCOINS.getInt(true))) + addAsSingletonList(config.textFormatting.killComboFormat.formatText(KILLCOMBO.getInt(), MAXKILLCOMBO.getInt(true))) + addAsSingletonList(config.textFormatting.highestKillComboFormat.formatText(MAXKILLCOMBO.getInt(), MAXKILLCOMBO.getInt(true))) + addAsSingletonList(config.textFormatting.skillXPGainFormat.formatText(SKILLXPGAINED.get(), SKILLXPGAINED.get(true))) + addAsSingletonList(bestiaryFormatting.base.formatText(bestiary).formatBestiary(currentKill, killNeeded)) + addAsSingletonList(xpHourFormatting.base.formatText(xp)) + addAsSingletonList(killHourFormatting.base.formatText(killh)) + addAsSingletonList(etaFormatting.base.formatText(eta).formatText(killETA)) + + /* + I'm very not sure about all that + */ + val rate = 0.12 * (1 + (mgc.toDouble()/100)) + val price = (BazaarApi.getBazaarDataByInternalName("SORROW")?.sellPrice ?: 0).toLong() + val final: String = (killInterp * price * (rate/100)).toLong().addSeparators() + addAsSingletonList(config.textFormatting.moneyHourFormat.formatText(final)) + } + + + // Part of this was taken from GhostCounterV3 CT module + // maybe replace this with a SkillXpGainEvent ? + @SubscribeEvent + fun onActionBar(event: LorenzActionBarEvent) { + if (!isEnabled()) return + skillXPPattern.matchMatcher(event.message) { + val gained = group("gained").formatNumber().toDouble() + val total = group("total") + if (total != lastXp) { + val res = total.replace("\\D".toRegex(), "") + gain = (res.toLong() - lastXp.toLong()).toDouble().roundToInt() + num = (gain.toDouble() / gained) + if (gained in 150.0..450.0) { + if (lastXp != "0") { + if (num >= 0) { + KILLS.add(num) + KILLS.add(num, true) + GHOSTSINCESORROW.add(num) + KILLCOMBO.add(num) + SKILLXPGAINED.add(gained * num.roundToLong()) + SKILLXPGAINED.add(gained * num.roundToLong(), true) + hidden?.bestiaryCurrentKill = hidden?.bestiaryCurrentKill?.plus(num) ?: num + } + } + } + lastXp = res + } + } + } + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!isEnabled()) return + if (LorenzUtils.skyBlockIsland != IslandType.DWARVEN_MINES) return + for (opt in Option.values()) { + val pattern = opt.pattern ?: continue + pattern.matchMatcher(event.message) { + when (opt) { + SORROWCOUNT, VOLTACOUNT, PLASMACOUNT, GHOSTLYBOOTS -> { + opt.add(1.0) + opt.add(1.0, true) + hidden?.totalMF = hidden?.totalMF?.plus(group("mf").substring(4).toDouble()) + ?: group("mf").substring(4).toDouble() + TOTALDROPS.add(1.0) + update() + } + + BAGOFCASH -> { + BAGOFCASH.add(1.0) + BAGOFCASH.add(1.0, true) + update() + } + + KILLCOMBOCOINS -> { + KILLCOMBOCOINS.set(KILLCOMBOCOINS.get() + group("coin").toDouble()) + update() + } + + else -> {} + } + } + + } + killComboExpiredPattern.matchMatcher(event.message) { + if (KILLCOMBO.getInt() > MAXKILLCOMBO.getInt()) { + MAXKILLCOMBO.set(group("combo").toDouble()) + } + if (KILLCOMBO.getInt() > MAXKILLCOMBO.getInt(true)) { + MAXKILLCOMBO.set(group("combo").toDouble(), true) + } + KILLCOMBOCOINS.set(0.0) + KILLCOMBO.set(0.0) + update() + } + //replace with BestiaryLevelUpEvent ? + bestiaryPattern.matchMatcher(event.message.removeColor()) { + val currentLevel = group("newLevel").toInt() + + when (val nextLevel = if (currentLevel >= 46) 47 else currentLevel + 1) { + 47 -> { + hidden?.bestiaryNextLevel = -1.0 + hidden?.bestiaryCurrentKill = 3_000_000.0 + hidden?.bestiaryKillNeeded = 0.0 + } + + else -> { + val killNeeded: Int = bestiaryData[nextLevel] ?: 0 + hidden?.bestiaryNextLevel = nextLevel.toDouble() + hidden?.bestiaryCurrentKill = 0.0 + hidden?.bestiaryKillNeeded = killNeeded.toDouble() + } + } + update() + } + } + + @SubscribeEvent + fun onPurseChange(event: PurseChangeEvent) { + if (!isEnabled()) return + if (LorenzUtils.skyBlockArea != "The Mist") return + if (event.reason != PurseChangeCause.GAIN_MOB_KILL) return + SCAVENGERCOINS.add(event.coins, true) + SCAVENGERCOINS.add(event.coins) + } + + @SubscribeEvent + fun onTick(event: TickEvent.ClientTickEvent) { + if (!isEnabled()) return + tick++ + if (tick % 20 == 0) { + inMist = LorenzUtils.skyBlockArea == "The Mist" + update() + } + + if (tick % 40 == 20) { + calculateXP() + calculateETA() + } + + if (notifyCTModule && ProfileStorageData.profileSpecific?.ghostCounter?.ctDataImported == true) { + notifyCTModule = false + if (isUsingCTGhostCounter()) { + clickableChat("§6[SkyHanni] GhostCounterV3 ChatTriggers module has been detected, do you want to import saved data ? Click here to import data", "shimportghostcounterdata") + } + } + } + + @SubscribeEvent + fun onInventoryOpen(event: InventoryOpenEvent) { + if (!LorenzUtils.inSkyBlock) return + val inventoryName = event.inventoryName + if (inventoryName != "Bestiary ➜ Deep Caverns") return + val stacks = event.inventoryItems + val ghostStack = stacks[13] ?: return + val bestiaryNextLevel = Utils.parseIntOrRomanNumeral(ghostStack.displayName.substring(8)) + 1 + hidden?.bestiaryNextLevel = bestiaryNextLevel.toDouble() + + for (line in ghostStack.getLore()) { + ghostXPPattern.matchMatcher(line.removeColor().trim()) { + hidden?.bestiaryCurrentKill = group("current").formatNumber().toDouble() + hidden?.bestiaryKillNeeded = group("total").formatNumber().toDouble() + } + } + update() + } + + fun isEnabled(): Boolean { + return LorenzUtils.inSkyBlock && config.enabled && LorenzUtils.skyBlockIsland == IslandType.DWARVEN_MINES + } + + private fun percent(number: Double, total: Double): String { + return "${((number / total) * 100).roundToPrecision(4)}" + } + + fun reset() { + for (opt in Option.values()) { + opt.set(0.0) + opt.set(0.0, true) + } + update() + } + + private fun isUsingCTGhostCounter(): Boolean { + return ghostCounterV3File.exists() && ghostCounterV3File.isFile + } + + fun importCTGhostCounterData() { + val c = ProfileStorageData.profileSpecific?.ghostCounter ?: return + if (isUsingCTGhostCounter()) { + if (c.ctDataImported) { + chat("§e[SkyHanni] §cYou already imported GhostCounterV3 data!") + return + } + val json = ConfigManager.gson.fromJson(FileReader(ghostCounterV3File), com.google.gson.JsonObject::class.java) + GHOSTSINCESORROW.add(json["ghostsSinceSorrow"].asDouble) + SORROWCOUNT.add(json["sorrowCount"].asDouble) + BAGOFCASH.add(json["BagOfCashCount"].asDouble) + PLASMACOUNT.add(json["PlasmaCount"].asDouble) + VOLTACOUNT.add(json["VoltaCount"].asDouble) + GHOSTLYBOOTS.add(json["GhostlyBootsCount"].asDouble) + KILLS.add(json["ghostsKilled"].asDouble) + hidden?.totalMF = hidden?.totalMF?.plus(json["TotalMF"].asDouble) ?: json["TotalMF"].asDouble + TOTALDROPS.add(json["TotalDrops"].asDouble) + c.ctDataImported = true + chat("§e[SkyHanni] §aImported data successfully!") + } else + chat("§e[SkyHanni] §cGhostCounterV3 ChatTriggers module not found!") + } + + private fun String.formatText(value: Int, session: Int = -1): String { + return Utils.chromaStringByColourCode(this.replace("%value%", value.addSeparators()) + .replace("%session%", session.addSeparators()) + .replace("&", "§")) + } + + private fun String.formatText(t: String): String { + return Utils.chromaStringByColourCode(this.replace("%value%", t) + .replace("&", "§")) + } + + private fun String.formatText(value: Double, session: Double): String { + return Utils.chromaStringByColourCode(this.replace("%value%", value.roundToPrecision(2).addSeparators()) + .replace("%session%", session.roundToPrecision(2).addSeparators()) + .replace("&", "§")) + } + + private fun String.formatBestiary(currentKill: Int, killNeeded: Int): String { + return Utils.chromaStringByColourCode( + this.replace("%currentKill%", if (config.showMax) bestiaryCurrentKill.addSeparators() else currentKill.addSeparators()) + .replace("%percentNumber%", percent(bestiaryCurrentKill.toDouble(), 3_000_000.0)) + .replace("%killNeeded%", NumberUtil.format(killNeeded)) + .replace("%currentLevel%", if (hidden?.bestiaryNextLevel?.toInt()!! < 0) "46" else "${hidden?.bestiaryNextLevel?.toInt()!! - 1}") + .replace("%nextLevel%", if (config.showMax) "46" else "${hidden?.bestiaryNextLevel?.toInt()!!}") + .replace("&", "§")) + } + + enum class Option(val pattern: Pattern? = null) { + KILLS, + SORROWCOUNT("§6§lRARE DROP! §r§9Sorrow §r§b\\([+](?<mf>.*)% §r§b✯ Magic Find§r§b\\)".toPattern()), + VOLTACOUNT("§6§lRARE DROP! §r§9Volta §r§b\\([+](?<mf>.*)% §r§b✯ Magic Find§r§b\\)".toPattern()), + PLASMACOUNT("§6§lRARE DROP! §r§9Plasma §r§b\\([+](?<mf>.*)% §r§b✯ Magic Find§r§b\\)".toPattern()), + GHOSTLYBOOTS("§6§lRARE DROP! §r§9Ghostly Boots §r§b\\([+](?<mf>.*)% §r§b✯ Magic Find§r§b\\)".toPattern()), + BAGOFCASH("§eThe ghost's death materialized §r§61,000,000 coins §r§efrom the mists!".toPattern()), + KILLCOMBOCOINS("[+]\\d+ Kill Combo [+](?<coin>.*) coins per kill".toPattern()), + TOTALDROPS, + GHOSTSINCESORROW, + SCAVENGERCOINS, + MAXKILLCOMBO, + KILLCOMBO("[+]\\d+ Kill Combo [+](?<coin>.*) coins per kill".toPattern()), + SKILLXPGAINED; + + fun add(i: Double, s: Boolean = false) { + if (s) + session[this] = session[this]?.plus(i) ?: i + else + hidden?.data?.set(this, hidden?.data?.get(this)?.plus(i) ?: i) + } + + fun set(i: Double, s: Boolean = false) { + if (s) + session[this] = i + else + hidden?.data?.set(this, i) + } + + fun getInt(s: Boolean = false): Int { + return if (s) + session[this]?.roundToInt() ?: 0 + else + hidden?.data?.get(this)?.roundToInt() ?: 0 + } + + fun get(s: Boolean = false): Double { + return if (s) + session[this] ?: 0.0 + else + hidden?.data?.get(this) ?: 0.0 + } + } + + fun importFormatting() { + val base64: String = try { + Toolkit.getDefaultToolkit().systemClipboard.getData(DataFlavor.stringFlavor) as String + } catch (e: Exception) { + return + } + + if (base64.length <= exportPrefix.length) return + + val jsonString = try { + val t = String(Base64.getDecoder().decode(base64.trim())) + if (!t.startsWith(exportPrefix)) return + t.substring(exportPrefix.length) + } catch (e: IllegalArgumentException) { + return + } + + val list = try { + JsonParser().parse(jsonString).asJsonArray + .filter { it.isJsonPrimitive } + .map { it.asString } + } catch (e: Exception) { + return + } + + if (list.size == 30) { + with(config.textFormatting) { + titleFormat = list[0] + ghostKilledFormat = list[1] + sorrowsFormat = list[2] + ghostSinceSorrowFormat = list[3] + ghostKillPerSorrowFormat = list[4] + voltasFormat = list[5] + plasmasFormat = list[6] + ghostlyBootsFormat = list[7] + bagOfCashFormat = list[8] + avgMagicFindFormat = list[9] + scavengerCoinsFormat = list[10] + killComboFormat = list[11] + highestKillComboFormat = list[12] + skillXPGainFormat = list[13] + with(xpHourFormatting) { + base = list[14] + noData = list[15] + paused = list[16] + } + with(bestiaryFormatting) { + base = list[17] + openMenu = list[18] + maxed = list[19] + showMax_progress = list[20] + progress = list[21] + } + with(killHourFormatting) { + base = list[22] + noData = list[23] + paused = list[24] + } + with(etaFormatting) { + base = list[25] + maxed = list[26] + noData = list[27] + progress = list[28] + } + moneyHourFormat = list[29] + } + } + } + + fun exportFormatting() { + val list = mutableListOf<String>() + with(config.textFormatting) { + list.add(titleFormat) + list.add(ghostKilledFormat) + list.add(sorrowsFormat) + list.add(ghostSinceSorrowFormat) + list.add(ghostKillPerSorrowFormat) + list.add(voltasFormat) + list.add(plasmasFormat) + list.add(ghostlyBootsFormat) + list.add(bagOfCashFormat) + list.add(avgMagicFindFormat) + list.add(scavengerCoinsFormat) + list.add(killComboFormat) + list.add(highestKillComboFormat) + list.add(skillXPGainFormat) + with(xpHourFormatting) { + list.add(base) + list.add(noData) + list.add(paused) + } + with(bestiaryFormatting) { + list.add(base) + list.add(openMenu) + list.add(maxed) + list.add(showMax_progress) + list.add(progress) + } + with(killHourFormatting) { + list.add(base) + list.add(noData) + list.add(paused) + } + with(etaFormatting) { + list.add(base) + list.add(maxed) + list.add(noData) + list.add(progress) + } + list.add(moneyHourFormat) + } + val jsonArray = JsonArray() + for (l in list) { + jsonArray.add(JsonPrimitive(l)) + } + val base64 = Base64.getEncoder().encodeToString((exportPrefix + jsonArray).toByteArray(StandardCharsets.UTF_8)) + Toolkit.getDefaultToolkit().systemClipboard.setContents(StringSelection(base64), null) + } + + fun resetFormatting() { + with(config.textFormatting) { + titleFormat = "&6Ghost Counter" + ghostKilledFormat = " &6Ghost Killed: &b%value% &7(%session%)" + sorrowsFormat = " &6Sorrow: &b%value% &7(%session%)" + ghostSinceSorrowFormat = " &6Ghost since Sorrow: &b%value%" + ghostKillPerSorrowFormat = " &6Ghosts/Sorrow: &b%value%" + voltasFormat = " &6Volta: &b%value% &7(%session%)" + plasmasFormat = " &6Plasmas: &b%value% &7(%session%)" + ghostlyBootsFormat = " &6Ghostly Boots: &b%value% &7(%session%)" + bagOfCashFormat = " &6Bag Of Cash: &b%value% &7(%session%)" + avgMagicFindFormat = " &6Avg Magic Find: &b%value%" + scavengerCoinsFormat = " &6Scavenger Coins: &b%value% &7(%session%)" + killComboFormat = " &6Kill Combo: &b%value%" + highestKillComboFormat = " &6Highest Kill Combo: &b%value% &7(%session%)" + skillXPGainFormat = " &6Skill XP Gained: &b%value% &7(%session%)" + with(xpHourFormatting) { + base = " &6XP/h: &b%value%" + noData = "&bN/A" + paused = "&c(PAUSED)" + } + with(bestiaryFormatting) { + base = " &6Bestiary %currentLevel%->%nextLevel%: &b%value%" + openMenu = "§cOpen Bestiary Menu !" + maxed = "%currentKill% (&c&lMaxed!)" + showMax_progress = "%currentKill%/3M (%percentNumber%%)" + progress = "%currentKill%/%killNeeded%" + } + with(killHourFormatting) { + base = " &6Kill/h: &b%value%" + noData = "§bN/A" + paused = "&c(PAUSED)" + } + with(etaFormatting) { + base = " &6ETA: &b%value%" + maxed = "§c§lMAXED!" + noData = "§bN/A" + progress = "§b%value%" + } + moneyHourFormat = " &6$/h: &b%value%" + } + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordStatus.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordStatus.kt index 9cf02c07d..20f203bb7 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordStatus.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/discordrpc/DiscordStatus.kt @@ -12,12 +12,19 @@ import at.hannibal2.skyhanni.data.ProfileStorageData import at.hannibal2.skyhanni.data.ScoreboardData import at.hannibal2.skyhanni.features.garden.GardenAPI.getCropType import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName +import at.hannibal2.skyhanni.utils.ItemUtils.name import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.colorCodeToRarity +import at.hannibal2.skyhanni.utils.NEUItems import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.TabListData.Companion.getTabList import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay.getCurrentPet import io.github.moulberry.notenoughupdates.util.SkyBlockTime +import net.minecraft.client.Minecraft +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound import java.util.function.Supplier import java.util.regex.Pattern @@ -27,6 +34,49 @@ var lastKnownDisplayStrings: MutableMap<DiscordStatus, String> = val purseRegex = Regex("""(?:Purse|Piggy): ([\d,]+)[\d.]*""") val bitsRegex = Regex("""Bits: ([\d|,]+)[\d|.]*""") +val stackingEnchants = mapOf( + "compact" to mapOf( + "levels" to listOf(0, 100, 500, 1500, 5000, 15000, 50000, 150000, 500000, 1000000), + "nbtNum" to "compact_blocks" + ), + "cultivating" to mapOf( + "levels" to listOf( + 0, + 1000, + 5000, + 25000, + 100000, + 300000, + 1500000, + 5000000, + 20000000, + 100000000 + ), "nbtNum" to "farmed_cultivating" + ), + "expertise" to mapOf( + "levels" to listOf(0, 50, 100, 250, 500, 1000, 2500, 5500, 10000, 15000), + "nbtNum" to "expertise_kills" + ), + "hecatomb" to mapOf( + "levels" to listOf(0, 2, 5, 10, 20, 30, 40, 60, 80, 100), + "nbtNum" to "hecatomb_s_runs" + ), + "champion" to mapOf( + "levels" to listOf( + 0, + 50000, + 100000, + 250000, + 500000, + 1000000, + 1500000, + 2000000, + 2500000, + 3000000 + ), "nbtNum" to "champion_combat_xp" + ) +) // nbtNum is the id of the enchantment in the nbt data + enum class DiscordStatus(private val displayMessageSupplier: Supplier<String>?) { NONE(null), @@ -54,7 +104,7 @@ enum class DiscordStatus(private val displayMessageSupplier: Supplier<String>?) val scoreboard = ScoreboardData.sidebarLinesFormatted // Matches coins amount in purse or piggy, with optional decimal points val coins = scoreboard.firstOrNull { purseRegex.matches(it.removeColor()) }?.let { - purseRegex.find(it.removeColor())?.groupValues?.get(1) + purseRegex.find(it.removeColor())?.groupValues?.get(1) ?: "" } if (coins == "1") { lastKnownDisplayStrings[PURSE] = "1 Coin" @@ -69,6 +119,7 @@ enum class DiscordStatus(private val displayMessageSupplier: Supplier<String>?) val bits = scoreboard.firstOrNull { bitsRegex.matches(it.removeColor()) }?.let { bitsRegex.find(it.removeColor())?.groupValues?.get(1) } + when (bits) { "1" -> "1 Bit" null -> "0 Bits" @@ -119,7 +170,36 @@ enum class DiscordStatus(private val displayMessageSupplier: Supplier<String>?) }), PROFILE({ - HypixelData.profileName.firstLetterUppercase() + val player = LorenzUtils.getPlayerName() + + val tabData = getTabList() + val levelRegex = Regex("""\[(\d{1,3})] $player""") + var sbLevel = "" +// SkyBlock Level: [999] on Lemon + for (line in tabData) { + if (line.contains(player)) { + val colorlessLine = line.removeColor() + sbLevel = levelRegex.find(colorlessLine)!!.groupValues[1] + break + } + } + + var profile = "SkyBlock Level: [$sbLevel] on " + + profile += ( + if (HypixelData.ironman) "♲" + else if (HypixelData.bingo) "Ⓑ" + else if (HypixelData.stranded) "☀" + else "" + ) + + val fruit = HypixelData.profileName.firstLetterUppercase() + if (fruit == "") profile = + lastKnownDisplayStrings[PROFILE] ?: "SkyBlock Level: [$sbLevel]" // profile fruit has not loaded in yet + else profile += fruit + + lastKnownDisplayStrings[PROFILE] = profile + profile }), SLAYER({ @@ -152,13 +232,11 @@ enum class DiscordStatus(private val displayMessageSupplier: Supplier<String>?) AUTO({ val slayerResult = SLAYER.displayMessageSupplier!!.get() - val milestoneResult = try { - CROP_MILESTONES.displayMessageSupplier!!.get() - } catch (e: Exception) { - "Unable to get milestone" - } + val stackingResult = STACKING.displayMessageSupplier!!.get() + val milestoneResult = CROP_MILESTONES.displayMessageSupplier!!.get() if (slayerResult != "Planning to do a slayer quest") slayerResult - else if (milestoneResult != "Unable to get milestone" && milestoneResult != "Unknown Item" && milestoneResult != "") milestoneResult + else if (milestoneResult != "Not farming!") milestoneResult + else if (stackingResult != "") stackingResult else { val statusNoAuto = DiscordStatus.values().toMutableList() statusNoAuto.remove(AUTO) @@ -176,9 +254,10 @@ enum class DiscordStatus(private val displayMessageSupplier: Supplier<String>?) } ?: 100 // percentage to next milestone if (tier != null) { - lastKnownDisplayStrings[CROP_MILESTONES] = tier.let { "${crop.cropName}: Milestone $it ($progress)" } + "${crop.cropName}: Milestone $tier ($progress)" + } else { + "Not farming!" } - lastKnownDisplayStrings[CROP_MILESTONES] ?: "" }), PETS({ @@ -189,6 +268,62 @@ enum class DiscordStatus(private val displayMessageSupplier: Supplier<String>?) "[Lvl $petLevel] ${colorCodeToRarity(colorCode)} $petName" } ?: "No pet equipped" + }), + + // Dynamic-only + STACKING({ + // Logic for getting the currently held stacking enchant is from Skytils, except for getExtraAttributes() which they got from BiscuitDevelopment + + fun getExtraAttributes(item: ItemStack?): NBTTagCompound? { + return if (item == null || !item.hasTagCompound()) { + null + } else item.getSubCompound("ExtraAttributes", false) + } + + val itemInHand = Minecraft.getMinecraft().thePlayer.inventory.getCurrentItem() + val itemName = itemInHand?.let { NEUItems.getItemStack(it.getInternalName()).name?.removeColor() ?: "" } ?: "" + + val extraAttributes = getExtraAttributes(itemInHand) + + fun getProgressPercent(amount: Int, levels: List<Int>): String { + var currentLevel = 0 + var percent = "MAXED" + for (level in levels.indices) { + if (amount > levels[level]) { + currentLevel++ + continue + } + percent = if (amount.toDouble() == 0.0) { + "" + } else { + LorenzUtils.formatPercentage((amount.toDouble() - levels[level - 1]) / (levels[level] - levels[level - 1])) + } + break + } + return percent + } + + var stackingReturn = "" + if (extraAttributes != null) { + val enchantments = extraAttributes.getCompoundTag("enchantments") + var stackingEnchant = "" + for (enchant in stackingEnchants.keys) { + if (extraAttributes.hasKey(stackingEnchants[enchant]?.get("nbtNum").toString())) { + stackingEnchant = enchant + break + } + } + val levels = stackingEnchants[stackingEnchant]?.get("levels") as? List<Int> ?: listOf(0) + val level = enchantments.getInteger(stackingEnchant) + val amount = extraAttributes.getInteger(stackingEnchants[stackingEnchant]?.get("nbtNum").toString()) + val stackingPercent = getProgressPercent(amount, levels) + + stackingReturn = + if (stackingPercent == "" || amount == 0) "" // outdated info is useless for AUTO; empty strings are manually ignored + else "$itemName: ${stackingEnchant.firstLetterUppercase()} $level ($stackingPercent)" // Hecatomb 100: (55.55%) + } + stackingReturn + }) ; diff --git a/src/main/java/at/hannibal2/skyhanni/utils/CombatUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/CombatUtils.kt new file mode 100644 index 000000000..bb199625d --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/CombatUtils.kt @@ -0,0 +1,120 @@ +package at.hannibal2.skyhanni.utils + +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.features.misc.GhostCounter +import io.github.moulberry.notenoughupdates.core.util.lerp.LerpUtils +import io.github.moulberry.notenoughupdates.util.XPInformation + +object CombatUtils { + + private var lastTotalXp = -1f + private val xpGainQueue = mutableListOf<Float>() + private var xpGainTimer = 0 + var lastUpdate: Long = -1 + private var skillInfo: XPInformation.SkillInfo? = null + private var skillInfoLast: XPInformation.SkillInfo? = null + private const val skillType = "Combat" + var xpGainHourLast = -1f + var xpGainHour = -1f + var isKilling = false + private var lastTotalKill = -1 + private val killGainQueue = mutableListOf<Int>() + var lastKillUpdate: Long = -1 + var killGainHourLast = -1 + var killGainHour = -1 + private var gainTimer = 0 + var _isKilling = false + + + /** + * Taken from NotEnoughUpdates + */ + fun calculateXP() { + lastUpdate = System.currentTimeMillis() + xpGainHourLast = xpGainHour + skillInfoLast = skillInfo + skillInfo = XPInformation.getInstance().getSkillInfo(skillType) ?: return + val totalXp: Float = skillInfo!!.totalXp + if (lastTotalXp > 0) { + val delta: Float = totalXp - lastTotalXp + + if (delta > 0 && delta < 1000) { + xpGainTimer = GhostCounter.config.pauseTimer + xpGainQueue.add(0, delta) + while (xpGainQueue.size > 30) { + xpGainQueue.removeLast() + } + var totalGain = 0f + for (f in xpGainQueue) totalGain += f + xpGainHour = totalGain * (60 * 60) / xpGainQueue.size + isKilling = true + } else if (xpGainTimer > 0) { + xpGainTimer-- + xpGainQueue.add(0, 0f) + while (xpGainQueue.size > 30) { + xpGainQueue.removeLast() + } + var totalGain = 0f + for (f in xpGainQueue) totalGain += f + xpGainHour = totalGain * (60 * 60) / xpGainQueue.size + isKilling = true + } else if (delta <= 0) { + isKilling = false + } + } + lastTotalXp = totalXp + } + + /* + Must be a better way to do this than repeating the calculateXP function + */ + fun calculateETA(){ + lastKillUpdate = System.currentTimeMillis() + killGainHourLast = killGainHour + val nextLevel = GhostCounter.hidden?.bestiaryNextLevel?.toInt() ?: return + val kill = GhostCounter.hidden?.bestiaryCurrentKill?.toInt() ?: return + val sum = GhostCounter.bestiaryData.filterKeys { it <= nextLevel - 1 }.values.sum() + val cKill = sum + kill + val totalKill = if (GhostCounter.config.showMax) GhostCounter.bestiaryCurrentKill else cKill + if (lastTotalKill > 0){ + val delta: Int = totalKill - lastTotalKill + if (delta in 1..19){ + gainTimer = GhostCounter.config.pauseTimer + killGainQueue.add(0, delta) + while (killGainQueue.size > 30){ + killGainQueue.removeLast() + } + var totalGain = 0 + for (f in killGainQueue) totalGain += f + killGainHour = totalGain * (60 * 60) / killGainQueue.size + _isKilling = true + }else if(gainTimer > 0){ + gainTimer-- + killGainQueue.add(0, 0) + while (killGainQueue.size > 30){ + killGainQueue.removeLast() + } + var totalGain = 0 + for (f in killGainQueue) totalGain += f + killGainHour = totalGain * (60 * 60) / killGainQueue.size + _isKilling = true + }else if (delta <= 0){ + _isKilling = false + } + } + lastTotalKill = totalKill + } + /** + * Taken from NotEnoughUpdates + */ + fun interp(now: Float, last: Float, lastupdate: Long): Float { + var interp = now + if (last >= 0 && last != now) { + var factor = (System.currentTimeMillis() - lastupdate) / 1000f + factor = LerpUtils.clampZeroOne(factor) + interp = last + (now - last) * factor + } + return interp + } + +}
\ No newline at end of file |